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的消息发送机制

反汇编之后

浙公网安备 33010602011771号