我們都知道 Objective-C 是一門(mén)動(dòng)態(tài)的語(yǔ)言,有的時(shí)候我們會(huì)使用 Runtime 處理一些在 Objective-C 上面無(wú)法實(shí)現(xiàn)或者很難實(shí)現(xiàn)的功能。例如:在擴(kuò)展中添加屬性;動(dòng)態(tài)的獲取屬性的名稱,方法名等。那么究竟什么是 Runtime?
什么是 “Runtime” ?
Runtime 簡(jiǎn)稱運(yùn)行時(shí)。Objective-C 就是運(yùn)行時(shí)機(jī)制,也就是在程序運(yùn)行時(shí)候的一些機(jī)制,其中最主要的是消息機(jī)制。對(duì)于我們熟悉的C語(yǔ)言,函數(shù)的調(diào)用在編譯的時(shí)候會(huì)決定調(diào)用哪個(gè)函數(shù)。但對(duì)于 Objective-C 的函數(shù),屬于動(dòng)態(tài)調(diào)用過(guò)程,在編譯的時(shí)候并不能決定真正調(diào)用哪個(gè)函數(shù),只有在真正運(yùn)行的時(shí)候才會(huì)根據(jù)函數(shù)的名稱找到對(duì)應(yīng)的函數(shù)來(lái)調(diào)用。
也就有了下面這兩點(diǎn)結(jié)論:
- 在編譯階段,Objective-C 可以調(diào)用任何函數(shù),即使這個(gè)函數(shù)并未實(shí)現(xiàn),只要聲明過(guò)就不會(huì)報(bào)錯(cuò)。
- 在編譯階段,C語(yǔ)言調(diào)用未實(shí)現(xiàn)的函數(shù)就會(huì)報(bào)錯(cuò)。
Swift 中的 Runtime
好了上面說(shuō)了這么多都是在說(shuō) Objective-C 的 Runtime,那么 Swift 的 Runtime 是什么樣的呢?我們來(lái)寫(xiě)幾行代碼看一下。
class Demo {
var name: String?
var address: String?
func myPrint() {
print("Demo")
}
}
var count: UInt32 = 0
let demo = Demo()
let list = class_copyPropertyList(object_getClass(demo), &count)
for idx in 0..<numericCast(count) {
if let item = list?[idx],
let name = String(utf8String: property_getName(item)) {
print("Property: \(name)")
}
}
猜一下,會(huì)輸出什么?當(dāng)然,這里什么都不輸出!為什么?因?yàn)槲覀儎?chuàng)建的類 Demo 是一個(gè)純 Swift 的類,因?yàn)?Swift 是一門(mén)靜態(tài)語(yǔ)言,所以我們對(duì)其運(yùn)用 Runtime 機(jī)制當(dāng)然是不可能獲取到 Demo 類的屬性的。如果我非要在 Swift 代碼中運(yùn)用 Runtime 技術(shù)呢?當(dāng)然是有辦法的。我們可以在你要獲取的屬性(函數(shù)同理)前用 @objc 修飾即可(當(dāng)然加上 dynamic 也是可以的,不過(guò)編譯器會(huì)提示錯(cuò)誤,必須要在 dynamic 前添加 @objc),就像如下這樣:
class Demo {
@objc var name: String?
@objc dynamic var address: String?
@objc func myPrint() {
print("Demo")
}
}
我們?cè)谔砑尤缦碌拇a來(lái)打印出方法名:
var fCount: UInt32 = 0
let funcList = class_copyMethodList(object_getClass(demo), &fCount)
for idx in 0..<numericCast(fCount) {
if let item = funcList?[idx] {
let name = NSStringFromSelector(method_getName(item))
print("Method: \(name)")
}
}
輸出如下:
Property: name
Property: address
Method: address
Method: name
Method: setName:
Method: setAddress:
Method: myPrint
為什么加上 @objc 就有 Runtime 機(jī)制了呢?Swift 不是靜態(tài)語(yǔ)言嗎?
我們來(lái)看一下官方文檔里對(duì) @objc 是怎么說(shuō)的。
把這個(gè)特性用到任何可以在 Objective-C 中表示的聲明上——例如,非內(nèi)嵌類,協(xié)議,非泛型枚舉(原始值類型只能是整數(shù)),類和協(xié)議的屬性、方法(包括
setter和getter),初始化> > 器,反初始化器,下標(biāo)。objc特性告訴編譯器,這個(gè)聲明在 Objective-C 代碼中是可用的。給擴(kuò)展應(yīng)用這個(gè)特性與為這個(gè)擴(kuò)展中所有不顯式標(biāo)記為 nonobjc 特性的成員應(yīng)用是一樣的效果。
用
objc特性標(biāo)記的類必須繼承自一個(gè) Objective-C 中定義的類。如果你把objc用到類或協(xié)議中,它會(huì)隱式地應(yīng)用于該類或協(xié)議中 Objective-C 兼容的成員上。如果一個(gè)類繼承自另一個(gè)> 帶objc特性標(biāo)記或 Objective-C 中定義的類,編譯器也會(huì)隱式地給這個(gè)類添加objc特性。標(biāo)記為objc特性的協(xié)議不能繼承自非objc特性的協(xié)議。
objc特性同樣會(huì)在下面的情況中隱式地添加:
- 聲明是子類的重寫(xiě),并且父類的聲明有
objc特性;- 聲明滿足的需求來(lái)自一個(gè)擁有
objc特性的協(xié)議;- 聲明有
IBAction,IBOutlet,IBDesignable,IBInspectable,NSManaged, 或者GKInspectable特性。如果你在一個(gè)枚舉中使用
objc特性,枚舉名和每個(gè)成員名串聯(lián)起來(lái),作為枚舉成員暴露給 Objective-C 代碼。成員名首字母大寫(xiě)。例如,一個(gè) SwiftPlanet枚舉成員叫做venus,> 它作為一個(gè)叫PlanetVenus的成員暴露到 Objective-C 代碼中。
objc特性可以接受一個(gè)特性實(shí)參,由一個(gè)標(biāo)識(shí)符組成。當(dāng)你想在 Objective-C 中為objc特性標(biāo)記的實(shí)體暴露一個(gè)不同的名字時(shí),用這個(gè)特性。你可以把這個(gè)實(shí)參用在命名類,枚舉,枚舉成> 員,協(xié)議,方法,getter,setter,初始化器。下面的例子把 ExampleClass 中 enabled 屬性的getter作為 isEnabled 暴露給 Objective-C 代碼,而不僅僅是屬性本身的名字。@objc class ExampleClass: NSObject { var enabled: Bool { @objc(isEnabled) get { // Return the appropriate value } } }
首先有 @objc 這個(gè)關(guān)鍵字,它是用來(lái)將 Swift 的 API 暴漏給 Objective-C 和 Runtime 使用的,文檔里也很清楚的說(shuō)明了,如果你類繼承自 Objective-C 的類,這個(gè)標(biāo)識(shí)符就會(huì)被自動(dòng)加進(jìn)去,加了這標(biāo)識(shí)符的屬性、方法無(wú)法保證都會(huì)被運(yùn)行時(shí)調(diào)用,因?yàn)?Swift 會(huì)做靜態(tài)優(yōu)化,想要完全被聲明成動(dòng)態(tài)調(diào)用,必須使用 dynamic 標(biāo)識(shí)符修飾,當(dāng)然添加了 dynamic 的時(shí)候,它會(huì)自己在加上 @objc 這個(gè)標(biāo)識(shí)符。
舉個(gè)例子:
class Demo: NSObject {
var dBool = true
var dInt = 1
func demoTest() {
print("NSObject Class")
}
}
雖然這時(shí)我們沒(méi)有在 dBool 和 dInt 兩個(gè)屬性添加 @objc 修飾,但是 Runtime 時(shí)依然能夠獲取到,因?yàn)轭惱^承了 Objective-C 中的 NSObject,他會(huì)隱式的在屬相前面添加 @objc,同理,繼承自 UIViewController 等的類,都有這個(gè)特性。