Swift-Runtime機制

相對于Objective-C的Runtime機制缭受,Swift的運行時機制相對低調(diào)很多趣斤,Swift語言是用C++編寫的,Swift的核Library使用Swift編寫的.

方法調(diào)度

Objective-C采用消息發(fā)送策略幅疼,選擇器向接收器發(fā)送消息呜魄,編譯階段無法知道對象是否有對應(yīng)的方法,運行時根據(jù)isa指針构韵,找到對象所屬的類結(jié)構(gòu)體周蹭,然后結(jié)合類中的緩存方法列表指針和虛函數(shù)指針找到選擇器對應(yīng)的SEL選擇器類型變量,如果找到則SEL變量對應(yīng)的IMP指針找到方法實現(xiàn).如果找不到對應(yīng)的方法疲恢,則會啟動消息轉(zhuǎn)發(fā)機制凶朗,如果仍然失敗,拋出異诚匀或崩潰.

Swift的方法調(diào)度分為靜態(tài)調(diào)度和動態(tài)調(diào)度兩種.

靜態(tài)調(diào)度:Swift中的struct方法調(diào)度是靜態(tài)的棚愤,執(zhí)行的時候直接跳到方法的實現(xiàn),靜態(tài)調(diào)度可以進行inline和其他編譯器優(yōu)化.需要額外的方法來存儲方法信息.

struct Point{
    var x:Double // 8 Bytes
    var y:Double // 8 bytes
    func draw(){
        print("Draw point at\(x,y)")
    }
}
let point1 = Point(x: 5.0, y: 5.0)
        point1.draw()
        print("占用內(nèi)存大小:\(MemoryLayout<Point>.size)") //16

動態(tài)調(diào)度:Swift中Class是動態(tài)調(diào)度的杂数,添加方法之后Class本身在棧上分配的仍然是一個word.堆上需要額外的一個word來存儲Class的Type信息宛畦,在Class的Type信息中,在Class的Type信息中揍移,存儲著virtual table(V-Table)次和。根據(jù)V-Table就可以找到對應(yīng)的方法執(zhí)行體.

class Point{
    var x:Double // 8 Bytes
    var y:Double // 8 bytes
    init(x:Double,y:Double) {
        self.x = x
        self.y = y
    }
    func draw(){
        print("Draw point at\(x,y)")
    }
}
let point2 = Point(x: 5.0, y: 5.0)
        point2.draw()
        print(MemoryLayout<Point>.size) //8
2599112-9d0c69e9511b6513.png

方法獲取

Objective-C運行時依賴TypeEncoding,也就是method_getTypeEncoding返回的結(jié)果羊精,他指定了方法的參數(shù)類型以及在函數(shù)調(diào)用時參數(shù)入棧所要的內(nèi)存空間斯够,沒有這個標(biāo)識就無法動態(tài)的壓入?yún)?shù)

如果Swift類沒有繼承NSObject囚玫,那么是無法通過運行時獲取屬性和方法的.如果Swift類繼承了NSObject,屬性或方法中包含Objective-C中不存在的類型读规,如果說元組抓督,那么也是對應(yīng)的屬性或方法是無法獲取的.

定義TestClass和TestController:

class TestClass {
    
    var tBool:Bool = true
    
    var tInt:Int32 = 32
    
    var tFloat:Float = 72.5
    
    var tString:String = "FlyElephant"
    
    var tObject:AnyObject? = nil
    
    func tInterViewInfo() {
        
    }
    
}

class TestController:UIViewController {
    
    var tBool:Bool = true
    
    var tInt:Int32 = 32
    
    var tFloat:Float = 72.5
    
    var tString:String = "FlyElephant"
    
    var tObject:AnyObject? = nil
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
    }
    
    
    func tReturnVoid(view:UIView) {
        
    }
    
    func tReturnVoidWithBool(value:Bool) {
        
    }
    
    func tReturnTuple(boolValue:Bool) -> (String,Int) {
        return ("FlyElephant",100)
    }
    
    func tReturnVoidWithCharacter(aCharacter:Character) {
        
    }
    
    func tableView(tableView:UITableView) -> Int {
        return 10
    }
    
}

測試代碼:

private func setUp1() {
        
        let testClass:TestClass = TestClass()
        showClsRuntime(cls: object_getClass(testClass))
        print("\n")
        
        let testController:TestController = TestController()
        showClsRuntime(cls: object_getClass(testController))
    }
    
    func showClsRuntime(cls:AnyClass) {
        
        print("showClsRuntime--獲取方法(FlyElephant)")
        
        var methodNum:UInt32 = 0
        
        let methodList = class_copyMethodList(cls, &methodNum)
        
        for index in 0..<numericCast(methodNum) {
            let method:Method = methodList![index]!
            
            print(String(utf8String: method_getTypeEncoding(method)) ?? " ",terminator: " ")
            print(String(utf8String: method_copyReturnType(method)) ?? " ",terminator: " ")
            print(String(_sel: method_getName(method)),terminator: " ")
            print("\n")
        }
        
         print("showClsRuntime--獲取變量(FlyElephant)")
         var propertyNum:UInt32 = 0
         let propertyList = class_copyPropertyList(cls, &propertyNum)
        
        for index in 0..<numericCast(propertyNum) {
            let property:objc_property_t = propertyList![index]!
            print(String(utf8String: property_getName(property)) ?? " ",terminator: " ")
            print(String(utf8String: property_getAttributes(property)) ?? " ",terminator: " ")
            print("\n")
        }
        
    }
測試結(jié)果.png

方法交換

相對于Objective-C的方法交換,對于單獨的Swift類束亏,是無法通過Objective-C直接交換的铃在,對于繼承的NSObject的類,也不是所有的方法都可以直接交換.

按照OC的套路定義的交換方法:

 func methodSwizzle(cls:AnyClass,originalSelector:Selector,swizzledSelector:Selector) {

        let originalMethod = class_getInstanceMethod(cls, originalSelector)
        let swizzledMethod = class_getInstanceMethod(cls, swizzledSelector)
        
        let didAddMethod = class_addMethod(cls, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))
        
        if didAddMethod {
            class_replaceMethod(cls, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    }

交換測試:

methodSwizzle(cls: object_getClass(self), originalSelector: #selector(ViewController.viewDidAppear(_:)), swizzledSelector: #selector(ViewController.fe_viewDidAppear(_:)))
        methodSwizzle(cls: object_getClass(self), originalSelector: #selector(ViewController.testMethod), swizzledSelector: #selector(ViewController.fe_testMethod))
        testMethod()
 override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
    }
    
    func fe_viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        print("FlyElephant_viewDidAppear方法交換")
    }
    
    dynamic func testMethod() {
        print("testMethod交換之前的執(zhí)行")
    }
    
    dynamic func fe_testMethod() {
        print("fe_testMethod交換之后的執(zhí)行")
    }

注意測試方法加入了dynamic特性碍遍,否則是無法通過運行時進行交換的定铜,viewDidAppear是繼承Objective-C類獲得的方法,本身就被修飾為dynamic怕敬,所以能被動態(tài)替換.

測試的交換的是寫在ViewController中的揣炕,Objective-C runtime 理論上會在加載和初始化類的時候調(diào)用兩個類方法: load 和 initialize
。出于安全性和一致性的考慮东跪,方法交叉過程 永遠 會在 load()
方法中進行.

每一個類在加載時只會調(diào)用一次 load方法畸陡,一個 initialize 方法可以被一個類和它所有的子類調(diào)用,Swift中l(wèi)oad類方法不會被runtime調(diào)用虽填,所有可以在initialize執(zhí)行交互過程丁恭,由于initialize會執(zhí)行多次,可以通過dispatch_once確保只執(zhí)行一次.

參考資料
Swift進階之內(nèi)存模型和方法調(diào)度
https://stackoverflow.com/questions/39302834/does-swift-guarantee-the-storage-order-of-fields-in-classes-and-structs/39302927#39302927
http://nshipster.cn/swift-objc-runtime/
Swift Runtime 編譯和運行時原理初探
http://allegro.tech/2014/12/swift-method-dispatching.html
Type EnCodings
Swift Runtime分析:還像OC Runtime一樣嗎斋日?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末牲览,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子恶守,更是在濱河造成了極大的恐慌第献,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件兔港,死亡現(xiàn)場離奇詭異痊硕,居然都是意外死亡,警方通過查閱死者的電腦和手機押框,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來理逊,“玉大人橡伞,你說我怎么就攤上這事〗唬” “怎么了兑徘?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長羡洛。 經(jīng)常有香客問我挂脑,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任崭闲,我火速辦了婚禮肋联,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘刁俭。我一直安慰自己橄仍,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布牍戚。 她就那樣靜靜地躺著侮繁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪如孝。 梳的紋絲不亂的頭發(fā)上宪哩,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天,我揣著相機與錄音第晰,去河邊找鬼锁孟。 笑死,一個胖子當(dāng)著我的面吹牛但荤,可吹牛的內(nèi)容都是我干的罗岖。 我是一名探鬼主播,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼腹躁,長吁一口氣:“原來是場噩夢啊……” “哼桑包!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起纺非,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤哑了,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后烧颖,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體弱左,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年炕淮,在試婚紗的時候發(fā)現(xiàn)自己被綠了拆火。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡涂圆,死狀恐怖们镜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情润歉,我是刑警寧澤模狭,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站踩衩,受9級特大地震影響嚼鹉,放射性物質(zhì)發(fā)生泄漏贩汉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一锚赤、第九天 我趴在偏房一處隱蔽的房頂上張望匹舞。 院中可真熱鬧,春花似錦宴树、人聲如沸策菜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽又憨。三九已至,卻和暖如春锭吨,著一層夾襖步出監(jiān)牢的瞬間蠢莺,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工零如, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留躏将,地道東北人。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓考蕾,卻偏偏與公主長得像祸憋,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子肖卧,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,627評論 2 350

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