Swift 进阶(十五)从OC到Swift(上)

标记

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

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

使用示例如下

-w370
-w440

我们还可以使用#warning来作为警告的提示,效果更为显著

-w714

条件编译

-w720

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

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

-w716

-w278

打印

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

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

-w642

更多用法参考:https://docs.swift.org/swift-book/ReferenceManual/Attributes.html

程序入口

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

-w776

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

-w776

也可以删掉@main或者@UIApplicationMain,自定义入口代码

1.创建main.swift文件

-w728

2.去掉AppDelegate里的标记

-w775

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

-w748

Swift调用OC

如果我们在Swift项目中需要调用到OC的代码,需要建立一个桥接头文件,文件名格式为{targetName}-Bridging-Header.h

在桥接文件里引用需要的OC头文件

-w1119

Build Setting -> Swift Compiler - General中写好桥接文件路径

-w849

如果我们是在Swift项目里第一次创建OC文件,Xcode会提示是否需要帮助创建桥接文件

-w731

然后我们就可以在Swift文件里调用OC的代码了

-w532
-w728

如果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

-w847

Swift暴露给OC的类最终都要继承自NSObject

使用@objc修饰需要暴露给OC的成员

-w400
-w592

使用@objcMembers修饰类,代表默认所有成员都会暴露给OC(包括扩展中定义的成员)

最终是否成功暴露,还需要考虑成员自身的权限问题

-w496

我们进入到test-Swift.h里看看编译器默认帮我们转成的OC代码是怎样的

-w722

我们还可以通过@objc来对Swift文件里的类和成员重命名,来更适应于OC的代码规范

-w573
-w635

选择器

Swift中依然可以使用选择器,使用#selector(name)定义一个选择器

必须是被@objcMembers@objc修饰的方法才可以定义选择器

-w728

如果不加@objcMembers@objc是会报错的

-w777

混编调用的本质

我们先来思考一个问题,为什么Swift暴露给OC的类最终要继承NSObject?

只有OC调用最后还是走的消息发送机制,要想能够实现消息机制,就需要有isa指针,所以要继承NSObject

我们在调用的地方打上断点,然后进行反汇编

-w557

我们发现,反汇编内部最终调用了objc_msgSend,很明显是消息发送机制

-w846

那Swift调用OC的方法,是走的消息发送机制,还是Swift本身的调用方式呢?

我们在调用的地方打上断点,然后进行反汇编

-w781

我们发现,反汇编内部最终调用了objc_msgSend,很明显是消息发送机制

-w846

暴露给OC使用的Swift函数和类,如果被Swift调用,是走的消息发送机制,还是Swift本身的调用方式呢?

我们在调用的地方打上断点,然后进行反汇编

-w784

我们发现,反汇编内部是按照根据元类信息里的函数地址去调用的方式,没有Runtime相关的调用

-w841
-w847

我们可以加上dynamic关键字,这样不管是OC调用还是Swift调用都会走Runtime的消息发送机制

-w505

反汇编之后

-w843

posted on 2021-03-31 02:37  FunkyRay  阅读(250)  评论(0编辑  收藏  举报