swift dispatch 笔记

本文深入探讨Swift语言中的三种方法派发方式:直接派发、函数表派发和动态派发,详细解释每种方式的工作原理及使用场景,并通过实例说明不同派发方式的选择依据。

swift中方法派发方式

swift语言中方法派发方式一共有3种:直接派发、函数表派发、动态派发。

派发方式介绍

1.直接派发

直接派发效率最高,在编译阶段就能确定方法调用位置,然后会直接调用,不需要复杂的寻址确定方法的过程。编译器也会在编译期间做一些优化。

2.函数表派发

每一个对象都会维护一个方法列表,在方法调用时,会找到方法列表相应的方法,然后进行方法调用。相对于直接派发会多出两次读内存和一次寻址的过程,因为首先需要读到方法列表的指针然后跳转到方法列表,然后读列表中的方法,找到对应方法才能跳转到实现,因此效率相对来说会低一些。

3.动态派发

动态派发会在运行时才确定方法的调用,因此也是效率最低的。但它为kvoMethod Swizzling提供了基石,可以说非常实用。

使用场景

在我看来,能够由编译器直接确定方法调用就会使用直接派发。比如说结构体由于其不可继承,那么对于一个结构体其方法也是确定的,所以对于结构体就是直接派发。相应的所有值类型,不可继承的类(加final)、扩展内类和协议方法、加private的类方法,都是唯一可以确定的所以都使用直接派发。

对于函数表派发:

class ParentClass {
    func method1() {}
    func method2() {}
}
class ChildClass: ParentClass {
    override func method2() {}
    func method3() {}
}
复制代码

对应的数据结构为:

而对于动态派发:

class ParentClass {
    dynamic func method1() {}
    dynamic func method2() {}
}
class ChildClass: ParentClass {
    override func method2() {}
    dynamic func method3() {}
}
复制代码

对应的数据结构:

由此可见动态派发相对于函数表派发其寻址到具体的方法更为复杂,调用一个父类方法可能要进行多次查找, 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。从以上结构中不难看出类可以动态添加方法、属性、协议,但是不可以动态添加变量。

所以总结得方法派发方式:

小例子

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…

转载于:https://juejin.im/post/5be2bf306fb9a04a006e7c64

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值