Swift底層進階--007:Runtime & 元類型

Runtime探索
案例1

測試以下代碼满俗,能否將?法列表及成員屬性打印出來涵叮?

class LGTeacher {
    var age: Int = 18
    func teach(){
        print("teach")
    }
}

let t = LGTeacher()

func test(){
    
    var methodCount: UInt32 = 0
    let methodList = class_copyMethodList(LGTeacher.self, &methodCount)
    
    for i in 0..<numericCast(methodCount) {
        if let method = methodList?[i]{
            let methodName = method_getName(method)
            print("方法列表:\(methodName)")
        }else{
            print("not found method")
        }
    }
    
    var count: UInt32 = 0
    let proList = class_copyPropertyList(LGTeacher.self, &count)
    
    for i in 0..<numericCast(count) {
        if let property = proList?[i]{
            let propertyName = property_getName(property)
            print("成員屬性:\(property)")
        }else{
            print("not found property")
        }
    }
    
    print("test run")
}

test()

//輸出以下內(nèi)容:
//test run

從運行結(jié)果來看并沒有達到預(yù)期谋国,?法列表和成員屬性都沒有打印出來

案例2

修改案例1哨啃,給方法和屬性添加@objc修飾萨醒,能否打印出結(jié)果斟珊?

class LGTeacher {
    @objc var age: Int = 18
    @objc func teach(){
        print("teach")
    }
}

//輸出以下內(nèi)容:
//方法列表:teach
//方法列表:age
//方法列表:setAge:
//成員屬性:0x0000000100008510
//test run

從運行結(jié)果來看,?法列表及成員屬性全部被打印出來富纸,但Class沒有繼承NSObject囤踩,所以并不能暴漏給OC使用

案例3

修改案例2,將Class繼承于NSObject晓褪,去掉@objc修飾堵漱,能否打印出結(jié)果?

class LGTeacher : NSObject {
    var age: Int = 18
    func teach(){
        print("teach")
    }
}

//輸出以下內(nèi)容:
//方法列表:init
//test run

從運行結(jié)果來看涣仿,只有init方法被打印出來勤庐。因為繼承NSObject后,swift.h中默認只有init方法暴露

案例4

修改案例3好港,Class繼承于NSObject愉镰,同時給方法和屬性添加@objc修飾,能否打印出結(jié)果钧汹?

class LGTeacher : NSObject {
    @objc var age: Int = 18
    @objc func teach(){
        print("teach")
    }
}

//輸出以下內(nèi)容:
//方法列表:teach
//方法列表:init
//方法列表:age
//方法列表:setAge:
//成員屬性:0x0000000100008518

從運行結(jié)果來看丈探,?法列表及成員屬性全部被打印出來,同時可供OC使用崭孤,但對于teach()方法类嗤,依然是V_table函數(shù)表調(diào)度糊肠,無法使用Runtime的方法交換,因為方法此時還不具備動態(tài)特性

案例5

修改案例4遗锣,Class繼承于NSObject货裹,將@objc修飾改為dynamic修飾,能否打印出結(jié)果精偿?

class LGTeacher : NSObject {
    dynamic var age: Int = 18
    dynamic func teach(){
        print("teach")
    }
}

//輸出以下內(nèi)容:
//方法列表:init
//test run

從運行結(jié)果來看弧圆,還是只有init方法被打印出來。因為dynamic修飾只給方法和屬性增加了動態(tài)特性笔咽,它們依然不能被OC使用

通過上述案例搔预,得出以下結(jié)論:

  • Swift是靜態(tài)語言,所以沒有動態(tài)特性叶组。?法和屬性不加任何修飾符的情況下拯田,不具備所謂的Runtime特性,它的方法調(diào)度方式使用V_table函數(shù)表調(diào)度
  • 對于純Swift類甩十,給?法和屬性添加@objc修飾后船庇,可以通過Runtime API獲取到?法和屬性列表,但是在OC中無法進?調(diào)度侣监,例如Runtime的方法交換
  • 繼承?NSObject的類鸭轮,如果想要動態(tài)獲取當前?法和屬性,必須在其聲明前添加@objc關(guān)鍵字橄霉。如果想通過Runtime API使用它們窃爷,例如Runtime的方法交換,需要添加dynamic關(guān)鍵字姓蜂,讓它們具備動態(tài)特性
objc源碼分析

進入class_copyMethodList定義按厘,先獲取當前的data,而data的作用就是存儲類的信息

class_copyMethodList

進入data定義覆糟,在objc_class里打印superclass刻剥,輸出的是Swift中有默認基類_SwiftObject

data

Swift源碼中找到_SwiftObject,發(fā)現(xiàn)它實現(xiàn)了NSObject協(xié)議滩字。本質(zhì)上Swift為了和OC進行交互造虏,它保留了OC的數(shù)據(jù)結(jié)構(gòu)

_SwiftObject

回到objc源碼,打印methods麦箍,輸出一個存放method的二維數(shù)組漓藕。使用@objc修飾的方法就能被獲取到,因為Swift在底層數(shù)據(jù)結(jié)構(gòu)和OC保持部分一致

methods

objc源碼中找到swift_class_t挟裂,繼承自objc_class享钞,保留了父類isasuperclass诀蓉、cacheData栗竖、data四個屬性暑脆,其次才是自己的屬性

swift_class_t

必須繼承NSObject的原因:Swift在底層數(shù)據(jù)結(jié)構(gòu)和OC只保持了部分一致,通過NSObject的聲明狐肢,標記了當前類是一個和OC交互的類添吗。可以幫助編譯器判斷這個類在編譯過程中份名,到底應(yīng)該走哪些方法的分支碟联。因為上層API的調(diào)用者暴露不同,所以選擇不同僵腺,在編譯器里優(yōu)化的調(diào)用方式也就不同鲤孵。

元類型
AnyObject

代表任意類的instance,類的類型辰如,僅類遵守的協(xié)議

class LGTeacher {
    var age: Int = 18
}

var t = LGTeacher()

//此時代表的就是當前 LGTeacher 的實例對象
var t1: AnyObject = t

//此時代表的就是 LGTeacher 這個類的類型
var t2: AnyObject = LGTeacher.self

//僅類遵守的協(xié)議
protocol JSONMap: AnyObject {}

//class可遵守此協(xié)議
class LGJSONMap: JSONMap {}

上述代碼中普监,t1代表LGTeacher的實例對象,t2代表LGTeacher類的類型丧没,JSONMap是僅類遵守的協(xié)議鹰椒,因為LGJSONMapClass類型锡移,所以可以遵守JSONMap協(xié)議

如果是結(jié)構(gòu)體呕童,可以遵守JSONMap協(xié)議嗎?

編譯報錯
JSONMap是僅類遵守的協(xié)議淆珊,結(jié)構(gòu)體無法使用夺饲,編譯報錯

Any

代表任意類型,包括funcation類型或者Optional類型

var array: [Any] = [1, "Teacher", true]

Any包含的類型比AnyObject更為廣泛施符,可以理解為AnyObjectAny的子集往声。

如果將array數(shù)組的元素聲明為AnyObject類型,編譯報錯

編譯報錯

AnyClass

代表任意實例的類型:AnyObject.Type

public typealias AnyClass = AnyObject.Type

上述代碼是AnyClass的定義戳吝,類型是AnyObject.Type

T.self
  • T是實例對象浩销,返回的就是它本身
  • T是類,那么返回的是Metadata
class LGTeacher {
    var age: Int = 18
}

var t = LGTeacher()
//返回實例對象本身
var t1 = t.self
//返回LGTeacher這個類的類型听哭,metadata元類型
var t2 = LGTeacher.self

上述代碼中慢洋, t1返回的是實例對象本身, t2返回的是LGTeacher.Type陆盘,也就是LGTeacher這個類的類型

T.Type

?種類型普筹,T.self的類型是T.Type

T.Type

type(of:)

?來獲取?個值的動態(tài)類型

var age: Int = 18

func test(_ value : Any) {
    print(type(of: value))
}

test(age)

//輸出以下內(nèi)容:
//Int

上述代碼中,value的靜態(tài)類型(static type)Any隘马,是編譯期確定好的太防。而type(of:)方法?來獲取?個值的動態(tài)類型(dynamic type),所以輸出的是Int

案例1

valueLGTeacher類型酸员,實際傳入的tLGChild類型蜒车,在test方法內(nèi)執(zhí)行value.teahc()讳嘱,打印結(jié)果是什么?

class LGTeacher {
    func teahc() {
        print("LGTeacher teahc")
    }
}

class LGChild : LGTeacher {
    override func teahc() {
        print("LGChild teahc")
    }
}

func test(_ value : LGTeacher) {
    value.teahc();
}

var t = LGChild()
test(t)

//輸出以下內(nèi)容:
//LGChild teahc

上述代碼中酿愧,value編譯期類型是LGTeacher呢燥,運行時的實際類型是LGChild,所以打印結(jié)果是LGChild teahc

案例2

valueTestProtocol類型寓娩,分別傳入t1t2叛氨,打印結(jié)果是什么?

protocol TestProtocol {}

class LGTeacher : TestProtocol {}

func test(_ value : TestProtocol) {
    print(type(of: value))
}

var t1 = LGTeacher()
var t2: TestProtocol = LGTeacher()

test(t1)
test(t2)

//輸出以下內(nèi)容:
//LGTeacher
//LGTeacher

上述代碼中棘伴,分別傳入的t1t2寞埠,運行時的實際類型都是LGTeacher,所以打印的type(of:)都是LGTeacher

案例3

value是泛型焊夸,分別傳入t1t2仁连,打印結(jié)果是什么?

protocol TestProtocol {}

class LGTeacher: TestProtocol {}

func test<T>(_ value : T) {
    print(type(of: value))
}

var t1 = LGTeacher()
var t2: TestProtocol = LGTeacher()

test(t1)
test(t2)

//輸出以下內(nèi)容:
//LGTeacher
//TestProtocol

上述代碼中阱穗,test方法的參數(shù)改為泛型饭冬,兩次打印type(of:)的結(jié)果不一樣了,t1輸出LGTeacher揪阶,t2輸出TestProtocol昌抠。因為當泛型和協(xié)議同時參與,編譯器無法推導(dǎo)出準確類型鲁僚,需要在調(diào)用type(of:)時將value轉(zhuǎn)換為Any

func test<T>(_ value : T) {
    print(type(of: value as Any))
}

//輸出以下內(nèi)容:
//LGTeacher
//LGTeacher

修改后的代碼炊苫,打印結(jié)果都是LGTeacher

案例4

class_getClassMethod方法cls參數(shù)要求傳入AnyClass類型,如果傳入t.self冰沙,可以獲取方法列表嗎侨艾?

編譯報錯
如圖所示,因為t是實例對象拓挥,t.self返回的就是它本身唠梨,是LGTeacher類型。而參數(shù)cls要求傳入AnyClass類型侥啤,也就是需要LGTeacher.Type類型当叭,所以類型不符,編譯報錯

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末愿棋,一起剝皮案震驚了整個濱河市科展,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌糠雨,老刑警劉巖才睹,帶你破解...
    沈念sama閱讀 216,843評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡琅攘,警方通過查閱死者的電腦和手機垮庐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來坞琴,“玉大人哨查,你說我怎么就攤上這事【绶” “怎么了寒亥?”我有些...
    開封第一講書人閱讀 163,187評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長荧关。 經(jīng)常有香客問我溉奕,道長,這世上最難降的妖魔是什么忍啤? 我笑而不...
    開封第一講書人閱讀 58,264評論 1 292
  • 正文 為了忘掉前任加勤,我火速辦了婚禮,結(jié)果婚禮上同波,老公的妹妹穿的比我還像新娘鳄梅。我一直安慰自己,他們只是感情好未檩,可當我...
    茶點故事閱讀 67,289評論 6 390
  • 文/花漫 我一把揭開白布戴尸。 她就那樣靜靜地躺著,像睡著了一般讹挎。 火紅的嫁衣襯著肌膚如雪校赤。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,231評論 1 299
  • 那天筒溃,我揣著相機與錄音,去河邊找鬼沾乘。 笑死怜奖,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的翅阵。 我是一名探鬼主播歪玲,決...
    沈念sama閱讀 40,116評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼掷匠!你這毒婦竟也來了滥崩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,945評論 0 275
  • 序言:老撾萬榮一對情侶失蹤讹语,失蹤者是張志新(化名)和其女友劉穎钙皮,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,367評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡短条,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,581評論 2 333
  • 正文 我和宋清朗相戀三年导匣,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片茸时。...
    茶點故事閱讀 39,754評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡贡定,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出可都,到底是詐尸還是另有隱情缓待,我是刑警寧澤,帶...
    沈念sama閱讀 35,458評論 5 344
  • 正文 年R本政府宣布渠牲,位于F島的核電站命斧,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏嘱兼。R本人自食惡果不足惜国葬,卻給世界環(huán)境...
    茶點故事閱讀 41,068評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望芹壕。 院中可真熱鬧汇四,春花似錦、人聲如沸踢涌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽睁壁。三九已至背苦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間潘明,已是汗流浹背行剂。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留钳降,地道東北人厚宰。 一個月前我還...
    沈念sama閱讀 47,797評論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像遂填,于是被迫代替她去往敵國和親铲觉。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,654評論 2 354

推薦閱讀更多精彩內(nèi)容