swift学习-02 从oc到swift01

519 阅读4分钟

标记

我们可以通过一些注释标记来特殊标明注释的含义

// MARK: 类似OC中的#pragma mark
// MARK: - 类似OC中的#pragma mark -
// TODO: 用于标记未完成的任务
// FIXME: 用于标记待修复的问题

条件编译

image.png 我们还可以在Build Settings-> Swift Compiler -Custom Flags自定义标记

在Other Swift Flags里自定义的标记要以-D开头

image.png

image.png

打印

我们可以自定义打印的内容,便于开发中的详情观察

/*
 * 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() {}
}

更多用法参考:docs.swift.org/swift-book/…

程序入口

在AppDelegate上面默认有个@main标记,这表示编译器自动生成入口代码(main函数代码),自动设置AppDelegate为APP的代理

之前的Xcode版本会生成@UIApplicationMain标记,和@main的作用一样

也可以删掉@main或者@UIApplicationMain,自定义入口代码 1.创建main.swift文件

2.去掉AppDelegate里的标记

3.在main.swift里面自定义UIApplication并增加入口代码

image.png

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

反汇编之后

image.png