swift进阶八:闭包 & Runtime & Any等类型
2020-12-17 本文已影响0人
markhetao
- 上一节,我们分析了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!") } } } }
image.png
OC
调用的文件中,必须导入XXX-Swift.h
头文件。确保桥接文件中,已自动生成OC类
、属性
和函数
。
image.pngimage.png
被调用
时,可以看到我们动态获取
到了@objc声明
的属性
和函数
:
当我们调用
image.pngswift函数
时,打开汇编模式
,可以看到已经是使用objc_msgSend
进行消息发送
了。此时这些@objc修饰
的函数
和变量
,可具备OC
的运行时特性
。
image.png
- 至此,我们已知道
swift
借助OC
实现运行时
机制。
进一步探究( 👉 objc4源码)
image.png
- 我们在
objc4源码
中搜索class_copyMethodList(
,可以把上述案例代码复制在oc源码
中,打断点
,可以发现cls是Swift类
。
如果进入
image.pngdata()
里面,打印superClass
,会发现是Swift._SwiftObject
在
image.pngOC源码
中,有针对swift
一一对应
的结构关系
:
在
image.pngSwift
源码中,针对Runtime
,有专门遵循NSObject
协议的SwiftObject
基类:
所以本质上
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)
image.png
编译期
:value
是Any类型
,运行时
:type(of:)
可获取真实类型
父子类
的调用,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
类型的详细介绍
。