swift dispatch 筆記

d_d發表於2018-11-07

swift中方法派發方式

swift語言中方法派發方式一共有3種:直接派發、函式表派發、動態派發。

派發方式介紹

1.直接派發

直接派發效率最高,在編譯階段就能確定方法呼叫位置,然後會直接呼叫,不需要複雜的定址確定方法的過程。編譯器也會在編譯期間做一些優化。

2.函式表派發

每一個物件都會維護一個方法列表,在方法呼叫時,會找到方法列表相應的方法,然後進行方法呼叫。相對於直接派發會多出兩次讀記憶體和一次定址的過程,因為首先需要讀到方法列表的指標然後跳轉到方法列表,然後讀列表中的方法,找到對應方法才能跳轉到實現,因此效率相對來說會低一些。

3.動態派發

動態派發會在執行時才確定方法的呼叫,因此也是效率最低的。但它為kvoMethod Swizzling提供了基石,可以說非常實用。

使用場景

在我看來,能夠由編譯器直接確定方法呼叫就會使用直接派發。比如說結構體由於其不可繼承,那麼對於一個結構體其方法也是確定的,所以對於結構體就是直接派發。相應的所有值型別,不可繼承的類(加final)、擴充套件內類和協議方法、加private的類方法,都是唯一可以確定的所以都使用直接派發。

對於函式表派發:

class ParentClass {
    func method1() {}
    func method2() {}
}
class ChildClass: ParentClass {
    override func method2() {}
    func method3() {}
}
複製程式碼

對應的資料結構為:

swift dispatch 筆記

而對於動態派發:

class ParentClass {
    dynamic func method1() {}
    dynamic func method2() {}
}
class ChildClass: ParentClass {
    override func method2() {}
    dynamic func method3() {}
}
複製程式碼

對應的資料結構:

swift dispatch 筆記
由此可見動態派發相對於函式表派發其定址到具體的方法更為複雜,呼叫一個父類方法可能要進行多次查詢,oc為了提高效率會用一個雜湊表對常用方法進行快取,這樣就解決了每次呼叫方法都不停查詢的尷尬局面。順便補充一下OC裡面類的結構:

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    class_rw_t *data() { 
        return bits.data();
    }
    ...
}
複製程式碼
struct class_data_bits_t {
    uintptr_t bits;
    
    class_rw_t* data() {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }
	// ...
}
複製程式碼
struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint32_t version;

    const class_ro_t *ro;

    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;
    // ...
}
複製程式碼
struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
    // ....
    const char * name;
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;
    // ....
};
複製程式碼

rw 表示 readwrite ro 表示 readonly。從以上結構中不難看出類可以動態新增方法、屬性、協議,但是不可以動態新增變數。

所以總結得方法派發方式:

swift dispatch 筆記

小例子

protocol Run { }
extension Run {
    func run() {
        print("run")
    }
}
class Dog: Run {
    func run() {
        print("dog run")
    }
}


let dog: Run = Dog()
dog.run() //會列印:run
複製程式碼

因為在協議的擴充套件內的方法會採用直接派發,dog被宣告為遵循Run協議,因此在編譯時就確定了其呼叫的是協議擴充套件內的run方法。

參考

www.raizlabs.com/dev/2016/12…

相關文章