- 上一节,我们分析了swfit的引用计数(strong、unwoned、weak)。本节,我们分析:
- 闭包
- Runtime
- Any、AnyObject 、 AnyClass、self、Type 、type(of)
1. 闭包
闭包,swift中强大的存在,它类似于OC中的Block。
- 闭包
默认自动捕获外部变量,可直接对捕获变量进行操作,影响捕获变量的值
测试代码:
自动捕获变量,会影响外部变量var age = 10 let closure = { age += 1 } closure() print(age)
- 打印结果:
image.png
自动捕获了外部变量age,闭包内修改age值,影响了外部age的值
手动捕获外界变量,值类型对象,会被copy一份,不可修改,也不影响外部值。
image.png
- 如果
闭包直接持有外部变量,容易造成循环引用。
- 例如:
class HTPerson { var age = 18 var completion: (()->Void)? deinit { print("HTPerson deinit") } } func test() { var person = HTPerson() person.completion = { person.age += 1 } } test() print("end")
- 打印结果:
image.png
- person
持有completion属性,completion闭包中捕获person这个外部变量,造成循环引用。
-
swift提供两种方式打破循环:
【方式一】weak弱引用 -
weak修饰的对象,不影响强引用计数,不影响对象的释放。对象允许为nil,nil时不执行后续语句。
image.png
【方式二】unowned无主引用
-
unowned修饰的对象,不影响强引用计数,不影响对象的释放。假定对象一直存在,不能为nil,必须确保捕获对象的生命周期可控(当对象已被释放时,会存在产生野指针,会有crash风险)
image.png
总结
- swift闭包会
自动捕获外界变量,并影响引用计数,有循环引用风险。手动声明捕获变量(写在[ ]内):
值类型:数据会被copy一份,为只读属性,不受该值的后续变化影响,也不会影响该值。
引用类型:copy的是对象地址,操作对象地址,会影响对象引用计数,也会影响该对象的内容。有循环引用风险。打断循环引用有两种方式:
[weak弱引用]: 将对象变为可空类型,不强引用对象,对象为nil时,不执行后续语句,很安全。(生命周期跟对象一致)
[unowned无主引用]:假定对象一直存在,不强引用对象,对象不能为nil。必须确保捕获对象的生命周期可控(当对象已被释放时,会存在产生野指针,会有crash风险)
2. Runtime
-
swift是一门静态语言,本身不具备动态性,不像OC有Runtime运行时的机制(此处指OC提供运行时API供程序员操作)。但由于swift兼容OC,所以可以转成OC类和函数,利用OC的运行时机制,来实现动态性。
与
运行时靠边的内容,swift只有dynamic修饰词和Mirror反射机制。
dynamic在编译时就已完成函数的替换操作,在运行前就处理好了。Mirror反射可以动态获取类型和成员信息,在程序运行时调用方法、属性等行为特性。但没有OCRuntime那么强大
- 创建一个
oc项目:
- 创建
swift类,自动生成桥接文件。class类必须继承自NSObject,所有需要OC调用的函数和方法,都必须使用@objc声明。class HTPerson: NSObject { @objc var age = 10 @objc func sayHello() { print("sayHello") } } class HTTool: NSObject { @objc static func test() { var methodCount: UInt32 = 0 let methodList = class_copyMethodList(HTPerson.self, &methodCount) for i in 0..<numericCast(methodCount) { if let method = methodList?[i] { let methodName = method_getName(method) print("方法列表:\(String(methodName.description))") } else { print("not found method!") } } var count: UInt32 = 0 let propertyList = class_copyPropertyList(HTPerson.self, &count) for i in 0..<numericCast(count) { if let property = propertyList?[i] { let propertyName = property_getName(property) print("属性列表:\(String(utf8String: propertyName)!)") } else { print("not found property!") } } } }
OC调用的文件中,必须导入XXX-Swift.h头文件。确保桥接文件中,已自动生成OC类、属性和函数。
image.png
image.png
被调用时,可以看到我们动态获取到了@objc声明的属性和函数:
image.png当我们调用
swift函数时,打开汇编模式,可以看到已经是使用objc_msgSend进行消息发送了。此时这些@objc修饰的函数和变量,可具备OC的运行时特性。
image.png
image.png
- 至此,我们已知道
swift借助OC实现运行时机制。
进一步探究( 👉 objc4源码)
- 我们在
objc4源码中搜索class_copyMethodList(,可以把上述案例代码复制在oc源码中,打断点,可以发现cls是Swift类。image.png
如果进入
data()里面,打印superClass,会发现是Swift._SwiftObject
image.png在
OC源码中,有针对swift一一对应的结构关系:
image.png在
Swift源码中,针对Runtime,有专门遵循NSObject协议的SwiftObject基类:
image.png所以本质上
swift能实现OC Runtime的原因,是结构相互兼容。
- 关于
dynamic的介绍,在👉 方法调度 & @objc & 指针 有介绍。
3. Any、AnyObject 、 AnyClass、self、Type 、type(of)
-
AnyObjetc: 代表任意类的Instance、类的类型(class)、仅类遵守的协议。(struct不行) -
Any: 代表任意类型,包括function类型或Optional类型 -
AnyClass: 仅代表实例或类的类型(AnyObject.Type) -
T.self: 如果T是实例对象,就返回实例本身。如果T是类,就返回metadata -
T.Type: 一种类型,T.self是T.Type类型 -
type(of:):用于获取一个值的动态类型
我们通过案例,来深刻理解他们的作用与区别:
3.1 AnyObject
- 可代表
类的Instance实例、类的类型、类遵守的协议。 (struct不行)
在开发过程中,如果不确定具体对象类,可使用AnyObject。
class HTPerson { var age = 18 } // 1. 类实例 var p1: AnyObject = HTPerson() // 2. 类的类型 var p2: AnyObject = HTPerson.self // 3. 类遵守的协议 (继承AnyObjec) protocol JSONMap: AnyObject { } // 4. struct不是AnyObject类型 // struct报错: [Non-class type 'HTJSON' cannot conform to class protocol 'JSONMap'] // struct HTJSON: JSONMap { } // 5. 基础类型强转为Class,就属于AnyObject var age: AnyObject = 10 as NSNumber // Int不属于AnyObject,强转NSNumber就属于AnyObject
3.2 Any
-
Any比AnyObject代表的范围更广,不仅支持类实例对象、类类型、类协议。还支持struct、函数以及Optioanl可选类型。
class HTPerson { var age = 18 } // 1. 类实例 var p1: Any = HTPerson() // 2. 类的类型 var p2: Any = HTPerson.self // 3. 类遵守的协议 (继承AnyObjec) protocol JSONMap: Any { } // 4. struct struct HTJSON: JSONMap { } // 5. 函数 func test() {} // 6. struct对象 let s = HTJSON() // 7. 可选类型 let option: HTPerson? = nil // Any类型的数组 var array: [Any] = [1, // Int "2", // String HTPerson.self, // class类型 p1, // 类的实例对象 JSONMap.self, // 协议本身 HTJSON.self, // struct类型 s, // struct实例对象 option, // 可选值 test() // 函数 ] print(array) // 打印结果: [1, "2", Demo.HTPerson, Demo.HTPerson, Demo.JSONMap, Demo.HTJSON, >Demo.HTJSON(), nil, ()]
- 通过
[Any]数组,我们可以看到Any可指代范围有多广。
3.3 AnyClass
-
AnyClass仅代表类的类型。
虽可接受AnyObject所有对象,但实际只存储了类的类型(通过下面[AnyObject]数组可以观察到这一现象)
class HTPerson {
var age = 18
}
// 1. 类实例
var p1: AnyObject = HTPerson()
// 2. 类的类型
var p2: AnyObject = HTPerson.self
// 3. 类遵守的协议 (继承AnyObjec)
protocol JSONMap: AnyObject { }
// 4. struct 不支持
//struct HTJSON: JSONMap { }
class HTTest: JSONMap { }
var p3: JSONMap = HTTest()
// 7. 可选类型
let option: HTPerson? = nil
// Any类型的数组
var array: [AnyObject] = [ HTPerson.self, // class类型
p1, // 类的实例对象
p3 // 遵守AnyObject协议的类对象也符合(类对象本身符合)
]
print(array) // 打印结果: [Demo.HTPerson, Demo.HTPerson, Demo.HTTest]
3.4 T.self
- 如果
T是实例对象,就返回实例本身。如果T是类,就返回metadata(首地址:类的类型)
class HTPerson { var age = 18 } struct HTTest { var name = "ht" } // 1. class实例对象,返回对象本身 var p = HTPerson().self print(type(of: p)) // 打印: HTPerson(实例对象类型) // 2. class类型, 返回class类型 var pClass = HTPerson.self print(type(of: pClass)) // 打印: HTPerson.Type(class类型) // 3. struct实例对象,返回对象本身 var t = HTTest().self print(type(of: t)) // 打印: HTTest (实例对象类型) // 4. struct类型,返回struct类型 var tStruct = HTTest.self print(type(of: tStruct)) // 打印: HTTest.Type(struct类型)
3.5 T.Type
- 一种类型,
T.self是T.Type类型。(使用type(of:)读取)
class类型的Type
image.pngstruct类型的Type
image.png
3.6 type(of:)
- 用于
获取一个值的动态类型
var age = 10 // 编译器任务value接收Any类型 func test(_ value: Any) { // type(of:)可获取真实类型 print(type(of: value)) // 打印Int } test(age)
编译期:value是Any类型,运行时:type(of:)可获取真实类型
image.png
父子类的调用,type(of:)是读取真实调用的对象:class HTPerson { } class HTStudent: HTPerson { } func test(_ value: HTPerson) { print(type(of: value)) // 当前真实调用对象 } var person = HTPerson() var student = HTStudent() test(person) // 打印:HTPerson test(student) // 打印:HTStudent
- 遵循
协议的类调用,,type(of:)也是读取真实调用的对象,而不是协议:protocol TestProtocol { } class HTPerson: TestProtocol { } func test(_ value: TestProtocol) { print(type(of: value)) // 当前真实调用对象(不是协议) } var p = HTPerson() var p1: TestProtocol = HTPerson() test(p) // 打印: HTPerson test(p1) // 打印: HTPerson (注意,不是协议)
补充: 当使用
泛型T时,调用时,可确定T类型。type(of:)读取的就是T类型:protocol TestProtocol { } class HTPerson: TestProtocol { } func test<T>(_ value: T) { print(type(of: value)) // (是协议) 如果需要取到类,可将value强转为Any类型 (value as Any) } var p = HTPerson() var p1: TestProtocol = HTPerson() test(p) // 打印: HTPerson test(p1) // 打印: TestProtocol (是协议)
- 以上,就是swift的
闭包、Runtime、Any & AnyObject & AnyClass & Type类型的详细介绍。
















