Swift Runtime

我們都知道 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é)論:

  1. 在編譯階段,Objective-C 可以調(diào)用任何函數(shù),即使這個(gè)函數(shù)并未實(shí)現(xiàn),只要聲明過(guò)就不會(huì)報(bào)錯(cuò)。
  2. 在編譯階段,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é)議的屬性、方法(包括 settergetter ),初始化> > 器,反初始化器,下標(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è) Swift Planet 枚舉成員叫做 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)有在 dBooldInt 兩個(gè)屬性添加 @objc 修飾,但是 Runtime 時(shí)依然能夠獲取到,因?yàn)轭惱^承了 Objective-C 中的 NSObject,他會(huì)隱式的在屬相前面添加 @objc,同理,繼承自 UIViewController 等的類,都有這個(gè)特性。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)載原文地址 Swift是蘋(píng)果2014年發(fā)布的編程開(kāi)發(fā)語(yǔ)言,可與Objective-C共同運(yùn)行于Mac OS和iO...
    John_LS閱讀 4,340評(píng)論 2 31
  • 分析用例 我們拿一個(gè)純Swift類和一個(gè)繼承自NSObject的類的類來(lái)做分析,這兩個(gè)類里包含盡量多的Swift的...
    hahaYXXXJ閱讀 795評(píng)論 0 2
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,072評(píng)論 0 9
  • 轉(zhuǎn)載自:移動(dòng)開(kāi)發(fā)前線 Swift是蘋(píng)果2014年發(fā)布的編程開(kāi)發(fā)語(yǔ)言,可與Objective-C共同運(yùn)行于Mac O...
    MichleMin閱讀 1,088評(píng)論 0 0
  • 全身黃又黃,又酸又甜又很忙,果子只在樹(shù)上長(zhǎng),榨成果汁人人嘗。你猜這是什么?不錯(cuò),就是芒果。 芒果是大多數(shù)人的...
    鄭舒元閱讀 470評(píng)論 0 1

友情鏈接更多精彩內(nèi)容