Swift 进阶(十五)从OC到Swift(上)
标记
我们可以通过一些注释标记来特殊标明注释的含义
// MARK:
类似OC
中的#pragma mark
// MARK: -
类似OC
中的#pragma mark -
// TODO:
用于标记未完成的任务// FIXME:
用于标记待修复的问题
使用示例如下
我们还可以使用#warning
来作为警告的提示,效果更为显著
条件编译
我们还可以在Build Settings-> Swift Compiler -Custom Flags
自定义标记
在Other Swift Flags
里自定义的标记要以-D
开头
打印
我们可以自定义打印的内容,便于开发中的详情观察
/*
* msg: 打印的内容
* file: 文件名
* line: 所在行数
* fn: 执行的函数名
*/
func log<T>(_ msg: T, file: NSString = #file, line: Int = #line, fn: String = #function) {
#if DEBUG
let prefix = "\(file.lastPathComponent)_\(line)_\(fn):"
print(prefix, msg)
#endif
}
func test() {
log("哈哈")
}
// 输出:
// main.swift_66_test(): 哈哈
系统的版本检测
if #available(iOS 10, macOS 10.12, *) {
// 对于iOS平台,只在iOS10及以上版本执行
// 对于macOS平台,只在macOS 10.12以上版本执行
// 最后的*表示在其他所有平台都执行
}
API可用性说明
@available(iOS 10, macOS 10.12, *)
class Person {}
struct Student {
// 旧的方法名更改,使用者用到时就会报错
@available(*, unavailable, renamed: "study")
func study_() {}
func study() {}
// 表示该方法在这个平台已经过期
@available(iOS, deprecated: 11)
@available(macOS, deprecated: 10.12)
func run() {}
}
更多用法参考:https://docs.swift.org/swift-book/ReferenceManual/Attributes.html
程序入口
在AppDelegate
上面默认有个@main
标记,这表示编译器自动生成入口代码(main函数代码),自动设置AppDelegate
为APP的代理
之前的Xcode
版本会生成@UIApplicationMain
标记,和@main
的作用一样
也可以删掉@main
或者@UIApplicationMain
,自定义入口代码
1.创建main.swift
文件
2.去掉AppDelegate
里的标记
3.在main.swift
里面自定义UIApplication
并增加入口代码
Swift调用OC
如果我们在Swift项目中需要调用到OC的代码,需要建立一个桥接头文件,文件名格式为{targetName}-Bridging-Header.h
在桥接文件里引用需要的OC头文件
在Build Setting -> Swift Compiler - General
中写好桥接文件路径
如果我们是在Swift项目里第一次创建OC文件,Xcode
会提示是否需要帮助创建桥接文件
然后我们就可以在Swift文件里调用OC的代码了
如果C语言暴露给Swift的函数名和Swift中的其他函数名冲突了,可以在Swift中使用@_silgen_name
修改C语言的函数名
// C文件
int sum(int a, int b) {
return a + b;
}
// Swift文件
func sum(_ a: Int, _ b: Int) -> Int {
a - b
}
@_silgen_name("sum")
func swift_sum(_ a: Int32, _ b: Int32) -> Int32
print(sum(5, 6))
print(swift_sum(5, 6))
@_silgen_name
还可以用来调用C的私有函数
OC调用Swift
我们要是想在OC文件中调用Swift代码,需要引用一个隐藏文件{targetName}-Swift.h
Swift暴露给OC的类最终都要继承自NSObject
使用@objc
修饰需要暴露给OC的成员
使用@objcMembers
修饰类,代表默认所有成员都会暴露给OC(包括扩展中定义的成员)
最终是否成功暴露,还需要考虑成员自身的权限问题
我们进入到test-Swift.h
里看看编译器默认帮我们转成的OC代码是怎样的
我们还可以通过@objc
来对Swift文件里的类和成员重命名,来更适应于OC的代码规范
选择器
Swift中依然可以使用选择器,使用#selector(name)
定义一个选择器
必须是被@objcMembers
或@objc
修饰的方法才可以定义选择器
如果不加@objcMembers
或@objc
是会报错的
混编调用的本质
我们先来思考一个问题,为什么Swift暴露给OC的类最终要继承NSObject?
只有OC调用最后还是走的消息发送机制,要想能够实现消息机制,就需要有isa指针
,所以要继承NSObject
我们在调用的地方打上断点,然后进行反汇编
我们发现,反汇编内部最终调用了objc_msgSend
,很明显是消息发送机制
那Swift调用OC的方法,是走的消息发送机制,还是Swift本身的调用方式呢?
我们在调用的地方打上断点,然后进行反汇编
我们发现,反汇编内部最终调用了objc_msgSend
,很明显是消息发送机制
暴露给OC使用的Swift函数和类,如果被Swift调用,是走的消息发送机制,还是Swift本身的调用方式呢?
我们在调用的地方打上断点,然后进行反汇编
我们发现,反汇编内部是按照根据元类信息里的函数地址去调用的方式,没有Runtime
相关的调用
我们可以加上dynamic
关键字,这样不管是OC调用还是Swift调用都会走Runtime
的消息发送机制
反汇编之后