- 上一節(jié),我們分析了swfit的引用計(jì)數(shù)(strong、unwoned、weak)。本節(jié),我們分析:
- 閉包
- Runtime
- Any、AnyObject 、 AnyClass、self、Type 、type(of)
1. 閉包
閉包
,swift中強(qiáng)大的存在,它類似
于OC中的Block
。
- 閉包
默認(rèn)自動(dòng)捕獲
外部變量,可直接對(duì)捕獲變量
進(jìn)行操作
,影響捕獲變量
的值
測(cè)試代碼:
自動(dòng)捕獲
變量,會(huì)影響
外部變量var age = 10 let closure = { age += 1 } closure() print(age)
- 打印結(jié)果:
image.png
自動(dòng)捕獲了外部變量age
,閉包內(nèi)修改age
值,影響了外部age
的值
手動(dòng)捕獲
外界變量,值類型
對(duì)象,會(huì)被copy一份
,不可修改
,也不影響外部值
。
image.png
- 如果
閉包
直接持有
外部變量
,容易造成循環(huán)引用
。
- 例如:
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")
- 打印結(jié)果:
image.png
- person
持有completion
屬性,completion
閉包中捕獲person
這個(gè)外部變量,造成循環(huán)引用
。
-
swift
提供兩種方式
打破循環(huán):
【方式一】weak
弱引用 -
weak修飾
的對(duì)象,不影響強(qiáng)引用計(jì)數(shù),不影響
對(duì)象的釋放
。對(duì)象允許
為nil
,nil
時(shí)不執(zhí)行
后續(xù)語句
。
image.png
【方式二】unowned
無主引用
-
unowned
修飾的對(duì)象,不影響強(qiáng)引用計(jì)數(shù),不影響
對(duì)象的釋放
。假定
對(duì)象一直存在
,不能
為nil
,必須確保捕獲對(duì)象
的生命周期可控
(當(dāng)對(duì)象已被釋放
時(shí),會(huì)存在產(chǎn)生野指針
,會(huì)有crash
風(fēng)險(xiǎn))
image.png
總結(jié)
- swift閉包會(huì)
自動(dòng)捕獲
外界變量
,并影響引用計(jì)數(shù)
,有循環(huán)引用風(fēng)險(xiǎn)
。手動(dòng)聲明
捕獲變量(寫在[ ]
內(nèi)):
值類型
:數(shù)據(jù)會(huì)被copy
一份,為只讀屬性
,不受
該值的后續(xù)
變化影響
,也不
會(huì)影響該值
。
引用類型
:copy
的是對(duì)象地址
,操作對(duì)象地址
,會(huì)影響對(duì)象引用計(jì)數(shù)
,也會(huì)影響
該對(duì)象
的內(nèi)容。有循環(huán)引用風(fēng)險(xiǎn)
。打斷循環(huán)引用
有兩種方式:
[weak
弱引用]: 將對(duì)象
變?yōu)?code>可空類型,不強(qiáng)引用
對(duì)象,對(duì)象為nil時(shí)
,不執(zhí)行
后續(xù)語句,很安全。(生命周期跟對(duì)象一致)
[unowned
無主引用]:假定
對(duì)象一直存在
,不強(qiáng)引用
對(duì)象,對(duì)象不能
為nil
。必須確保捕獲對(duì)象
的生命周期可控
(當(dāng)對(duì)象已被釋放
時(shí),會(huì)存在產(chǎn)生野指針
,會(huì)有crash
風(fēng)險(xiǎn))
2. Runtime
-
swift
是一門靜態(tài)語言
,本身不具備動(dòng)態(tài)性
,不像OC
有Runtime
運(yùn)行時(shí)的機(jī)制(此處指OC
提供運(yùn)行時(shí)API
供程序員操作)。但由于swift
兼容OC
,所以可以轉(zhuǎn)
成OC類
和函數(shù)
,利用OC的運(yùn)行時(shí)機(jī)制
,來實(shí)現(xiàn)動(dòng)態(tài)性。
與
運(yùn)行時(shí)
靠邊的內(nèi)容,swift只有dynamic修飾詞
和Mirror反射機(jī)制
。
dynamic
在編譯時(shí)
就已完成
函數(shù)的替換操作
,在運(yùn)行前
就處理好了。Mirror
反射可以動(dòng)態(tài)
獲取類型
和成員信息
,在程序運(yùn)行時(shí)
調(diào)用方法
、屬性
等行為特性
。但沒有OCRuntime那么強(qiáng)大
- 創(chuàng)建一個(gè)
oc
項(xiàng)目:
- 創(chuàng)建
swift類
,自動(dòng)生成橋接文件
。class類
必須繼承
自NSObject
,所有需要OC調(diào)用的函數(shù)
和方法
,都必須使用@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
調(diào)用的文件中,必須導(dǎo)入XXX-Swift.h
頭文件。確保橋接文件中,已自動(dòng)生成OC類
、屬性
和函數(shù)
。
image.png
image.png
被調(diào)用
時(shí),可以看到我們動(dòng)態(tài)獲取
到了@objc聲明
的屬性
和函數(shù)
:
image.png當(dāng)我們調(diào)用
swift函數(shù)
時(shí),打開匯編模式
,可以看到已經(jīng)是使用objc_msgSend
進(jìn)行消息發(fā)送
了。此時(shí)這些@objc修飾
的函數(shù)
和變量
,可具備OC
的運(yùn)行時(shí)特性
。
image.png
image.png
- 至此,我們已知道
swift
借助OC
實(shí)現(xiàn)運(yùn)行時(shí)
機(jī)制。
進(jìn)一步探究( ?? objc4源碼)
- 我們?cè)?code>objc4源碼中搜索
class_copyMethodList(
,可以把上述案例代碼復(fù)制在oc源碼
中,打斷點(diǎn)
,可以發(fā)現(xiàn)cls是Swift類
。image.png
如果進(jìn)入
data()
里面,打印superClass
,會(huì)發(fā)現(xiàn)是Swift._SwiftObject
image.png在
OC源碼
中,有針對(duì)swift
一一對(duì)應(yīng)
的結(jié)構(gòu)關(guān)系
:
image.png在
Swift
源碼中,針對(duì)Runtime
,有專門遵循NSObject
協(xié)議的SwiftObject
基類:
image.png所以本質(zhì)上
swift
能實(shí)現(xiàn)OC Runtime
的原因,是結(jié)構(gòu)
相互兼容
。
- 關(guān)于
dynamic
的介紹,在?? 方法調(diào)度 & @objc & 指針 有介紹。
3. Any、AnyObject 、 AnyClass、self、Type 、type(of)
-
AnyObjetc
: 代表任意類的Instance、類的類型(class)、僅類遵守的協(xié)議。(struct不行) -
Any
: 代表任意類型,包括function
類型或Optional
類型 -
AnyClass
: 僅代表實(shí)例
或類
的類型(AnyObject.Type
) -
T.self
: 如果T是實(shí)例對(duì)象
,就返回實(shí)例本身
。如果T是類
,就返回metadata
-
T.Type
: 一種類型
,T.self
是T.Type
類型 -
type(of:)
:用于獲取
一個(gè)值的動(dòng)態(tài)類型
我們通過案例,來深刻理解他們的作用與區(qū)別:
3.1 AnyObject
- 可代表
類的Instance實(shí)例
、類的類型
、類遵守的協(xié)議
。 (struct不行)
在開發(fā)過程中,如果不確定具體對(duì)象類,可使用AnyObject
。
class HTPerson { var age = 18 } // 1. 類實(shí)例 var p1: AnyObject = HTPerson() // 2. 類的類型 var p2: AnyObject = HTPerson.self // 3. 類遵守的協(xié)議 (繼承AnyObjec) protocol JSONMap: AnyObject { } // 4. struct不是AnyObject類型 // struct報(bào)錯(cuò): [Non-class type 'HTJSON' cannot conform to class protocol 'JSONMap'] // struct HTJSON: JSONMap { } // 5. 基礎(chǔ)類型強(qiáng)轉(zhuǎn)為Class,就屬于AnyObject var age: AnyObject = 10 as NSNumber // Int不屬于AnyObject,強(qiáng)轉(zhuǎn)NSNumber就屬于AnyObject
3.2 Any
-
Any
比AnyObject
代表的范圍更廣,不僅支持類實(shí)例對(duì)象
、類類型
、類協(xié)議
。還支持struct
、函數(shù)
以及Optioanl
可選類型。
class HTPerson { var age = 18 } // 1. 類實(shí)例 var p1: Any = HTPerson() // 2. 類的類型 var p2: Any = HTPerson.self // 3. 類遵守的協(xié)議 (繼承AnyObjec) protocol JSONMap: Any { } // 4. struct struct HTJSON: JSONMap { } // 5. 函數(shù) func test() {} // 6. struct對(duì)象 let s = HTJSON() // 7. 可選類型 let option: HTPerson? = nil // Any類型的數(shù)組 var array: [Any] = [1, // Int "2", // String HTPerson.self, // class類型 p1, // 類的實(shí)例對(duì)象 JSONMap.self, // 協(xié)議本身 HTJSON.self, // struct類型 s, // struct實(shí)例對(duì)象 option, // 可選值 test() // 函數(shù) ] print(array) // 打印結(jié)果: [1, "2", Demo.HTPerson, Demo.HTPerson, Demo.JSONMap, Demo.HTJSON, >Demo.HTJSON(), nil, ()]
- 通過
[Any]數(shù)組
,我們可以看到Any
可指代范圍
有多廣
。
3.3 AnyClass
-
AnyClass
僅代表類
的類型
。
雖可接受AnyObject
所有對(duì)象,但實(shí)際只存儲(chǔ)
了類
的類型
(通過下面[AnyObject]
數(shù)組可以觀察到這一現(xiàn)象
)
class HTPerson {
var age = 18
}
// 1. 類實(shí)例
var p1: AnyObject = HTPerson()
// 2. 類的類型
var p2: AnyObject = HTPerson.self
// 3. 類遵守的協(xié)議 (繼承AnyObjec)
protocol JSONMap: AnyObject { }
// 4. struct 不支持
//struct HTJSON: JSONMap { }
class HTTest: JSONMap { }
var p3: JSONMap = HTTest()
// 7. 可選類型
let option: HTPerson? = nil
// Any類型的數(shù)組
var array: [AnyObject] = [ HTPerson.self, // class類型
p1, // 類的實(shí)例對(duì)象
p3 // 遵守AnyObject協(xié)議的類對(duì)象也符合(類對(duì)象本身符合)
]
print(array) // 打印結(jié)果: [Demo.HTPerson, Demo.HTPerson, Demo.HTTest]
3.4 T.self
- 如果
T
是實(shí)例對(duì)象
,就返回實(shí)例本身
。如果T
是類
,就返回metadata
(首地址:類的類型
)
class HTPerson { var age = 18 } struct HTTest { var name = "ht" } // 1. class實(shí)例對(duì)象,返回對(duì)象本身 var p = HTPerson().self print(type(of: p)) // 打印: HTPerson(實(shí)例對(duì)象類型) // 2. class類型, 返回class類型 var pClass = HTPerson.self print(type(of: pClass)) // 打印: HTPerson.Type(class類型) // 3. struct實(shí)例對(duì)象,返回對(duì)象本身 var t = HTTest().self print(type(of: t)) // 打?。?HTTest (實(shí)例對(duì)象類型) // 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:)
- 用于
獲取
一個(gè)值的動(dòng)態(tài)類型
var age = 10 // 編譯器任務(wù)value接收Any類型 func test(_ value: Any) { // type(of:)可獲取真實(shí)類型 print(type(of: value)) // 打印Int } test(age)
編譯期
:value
是Any類型
,運(yùn)行時(shí)
:type(of:)
可獲取真實(shí)類型
image.png
父子類
的調(diào)用,type(of:)
是讀取真實(shí)調(diào)用
的對(duì)象
:class HTPerson { } class HTStudent: HTPerson { } func test(_ value: HTPerson) { print(type(of: value)) // 當(dāng)前真實(shí)調(diào)用對(duì)象 } var person = HTPerson() var student = HTStudent() test(person) // 打印:HTPerson test(student) // 打?。篐TStudent
- 遵循
協(xié)議
的類調(diào)用,,type(of:)
也是讀取真實(shí)調(diào)用
的對(duì)象
,而不是協(xié)議:protocol TestProtocol { } class HTPerson: TestProtocol { } func test(_ value: TestProtocol) { print(type(of: value)) // 當(dāng)前真實(shí)調(diào)用對(duì)象(不是協(xié)議) } var p = HTPerson() var p1: TestProtocol = HTPerson() test(p) // 打?。?HTPerson test(p1) // 打?。?HTPerson (注意,不是協(xié)議)
補(bǔ)充: 當(dāng)使用
泛型T
時(shí),調(diào)用
時(shí),可確定T類型
。type(of:)
讀取的就是T類型
:protocol TestProtocol { } class HTPerson: TestProtocol { } func test<T>(_ value: T) { print(type(of: value)) // (是協(xié)議) 如果需要取到類,可將value強(qiáng)轉(zhuǎn)為Any類型 (value as Any) } var p = HTPerson() var p1: TestProtocol = HTPerson() test(p) // 打?。?HTPerson test(p1) // 打印: TestProtocol (是協(xié)議)
- 以上,就是swift的
閉包
、Runtime
、Any & AnyObject & AnyClass & Type
類型的詳細(xì)介紹
。