Swift的方法派发方式

按照类型划分

Value Type

像struct、enum这样的值类型,不支持继承,所以无需动态派发,它所有的方法调用,包括遵循的协议方法,都是直接调用

值类型的函数、引用类型的函数且函数修饰词为final、extention中实现的方法(无法被重写)都属于直接派发

在SIL(Swift Intermediate Language)中function_ref  指令用于生成值类型函数的引用。

Class Type

对于一个纯 Swift class 来说,默认使用 Table 派发,影响它方法调用的关键字有 final、 dynamic 和 extension

函数如果被标记成 final ,编译器就会知道这个方法不会被 override,并把它的调用方式标记成直接调用。而对于未标记成 final 并在 class 内部(非 extension)中定义的方法,Swift 会用一种叫作 Virtual Table 的机制来在运行时查找到这个方法并进行调用。

当一个方法被标记为 dymanic,你必须同时把它标记上 @objc,此时这个方法会使用 Message派发。

NSObject Subclass

影响这种类型的函数调用方式的关键字和上面一样,但是表现却不完全一样。

标记为 final 和 dynamic 的函数可以参考上面的 class。

在原生声明(非 extension)中定义的普通方法和标记为 @objc 的方法都使用 V-Table 机制派发。@objc 只是把方法暴露给 Objective-C,并没有改变方法派发的本质。

Extension 中的方法是直接派发的,但标记为 @objc 的函数需要对 Objc runtime 可见,就变成了 Message 派发。而且加不加 dynamic 生成的底层代码是一样的,这里怀疑是编译器隐式的加上了 dynamic 关键字。

函数表派发

引用类型中,未经 final/dynamic修饰,且并不是在extension中实现的方法、protocol中的方法,都是使用函数表派发

SIL 使用 class_method 指令去获取应用类型中 VTable 中的方法进行调用, 使用 witness_method 指令获取protocol类型在 WTable 中的方法进行调用

Message派发

添加 @objc dynamic修饰的方法、或者NSObject子类中extension里的@objc方法,会使用Message派发;

只添加@objc,并不会修改动态派发,只是生成了oc和swift可见版本的方法

在SIL中使用 objc_method 指令进行法的调用。

根据Type类型划分消息派发方式

对象/派发方式静态派发VTableWTable消息(动态)派发
Value Type默认行为:protocol
Swift Classfinal、extension默认行为:protocoldynamic
NSObjectfinal、extension默认行为:protocoldynamic
protocolextension默认行为:NSObjectProtocol @objc

注意:Witness Table 仅在调用对象类型为 Protocol 类型时,才会被引用。

根据声明所在位置划分

原始位置extension
Value Typestaticstatic
Class TypeV-Tablestatic
NSObject SubclassV-Table@objc为Message,其他为Static
protocolN/Astatic

派发方式总结

  • Struct的方法默认是Static派发
  • Extension内的方法默认是Static派发
  • Swift ClassNSObject子类内部的方法,默认是V-Table派发;
  • 实例类型是Protocol的类,调用protocol方法,是W-Table派发注意实例类型必须转换为Protocol);
  • Protocol的默认实现即Protocol在Extension中的方法,默认是Static派发
  • final修饰的方法,是Static派发
  • @objc dynamic修饰方法,会变为Message派发
  • NSObject子类的Extension方法,使用@objc修饰,会变为Message派发
全部评论

相关推荐

点赞 评论 收藏
转发
投递海康威视等公司10个岗位
点赞 评论 收藏
转发
点赞 收藏 评论
分享
牛客网
牛客企业服务