Swift-Runtime機(jī)制

相對(duì)于Objective-C的Runtime機(jī)制,Swift的運(yùn)行時(shí)機(jī)制相對(duì)低調(diào)很多,Swift語(yǔ)言是用C++編寫的,Swift的核Library使用Swift編寫的.

方法調(diào)度

Objective-C采用消息發(fā)送策略,選擇器向接收器發(fā)送消息,編譯階段無(wú)法知道對(duì)象是否有對(duì)應(yīng)的方法,運(yùn)行時(shí)根據(jù)isa指針,找到對(duì)象所屬的類結(jié)構(gòu)體,然后結(jié)合類中的緩存方法列表指針和虛函數(shù)指針找到選擇器對(duì)應(yīng)的SEL選擇器類型變量,如果找到則SEL變量對(duì)應(yīng)的IMP指針找到方法實(shí)現(xiàn).如果找不到對(duì)應(yīng)的方法,則會(huì)啟動(dòng)消息轉(zhuǎn)發(fā)機(jī)制,如果仍然失敗,拋出異?;虮罎?

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

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

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

動(dòng)態(tài)調(diào)度:Swift中Class是動(dòng)態(tài)調(diào)度的,添加方法之后Class本身在棧上分配的仍然是一個(gè)word.堆上需要額外的一個(gè)word來(lái)存儲(chǔ)Class的Type信息,在Class的Type信息中,在Class的Type信息中,存儲(chǔ)著virtual table(V-Table)。根據(jù)V-Table就可以找到對(duì)應(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運(yùn)行時(shí)依賴TypeEncoding,也就是method_getTypeEncoding返回的結(jié)果,他指定了方法的參數(shù)類型以及在函數(shù)調(diào)用時(shí)參數(shù)入棧所要的內(nèi)存空間,沒(méi)有這個(gè)標(biāo)識(shí)就無(wú)法動(dòng)態(tài)的壓入?yún)?shù)

如果Swift類沒(méi)有繼承NSObject,那么是無(wú)法通過(guò)運(yùn)行時(shí)獲取屬性和方法的.如果Swift類繼承了NSObject,屬性或方法中包含Objective-C中不存在的類型,如果說(shuō)元組,那么也是對(duì)應(yīng)的屬性或方法是無(wú)法獲取的.

定義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
    }
    
}

測(cè)試代碼:

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")
        }
        
    }
測(cè)試結(jié)果.png

方法交換

相對(duì)于Objective-C的方法交換,對(duì)于單獨(dú)的Swift類,是無(wú)法通過(guò)Objective-C直接交換的,對(duì)于繼承的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);
        }
    }

交換測(cè)試:

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í)行")
    }

注意測(cè)試方法加入了dynamic特性,否則是無(wú)法通過(guò)運(yùn)行時(shí)進(jìn)行交換的,viewDidAppear是繼承Objective-C類獲得的方法,本身就被修飾為dynamic,所以能被動(dòng)態(tài)替換.

測(cè)試的交換的是寫在ViewController中的,Objective-C runtime 理論上會(huì)在加載和初始化類的時(shí)候調(diào)用兩個(gè)類方法: load 和 initialize
。出于安全性和一致性的考慮,方法交叉過(guò)程 永遠(yuǎn) 會(huì)在 load()
方法中進(jìn)行.

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

參考資料
Swift進(jìn)階之內(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 編譯和運(yùn)行時(shí)原理初探
http://allegro.tech/2014/12/swift-method-dispatching.html
Type EnCodings
Swift Runtime分析:還像OC Runtime一樣嗎?

最后編輯于
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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