领略一下swift函数派发机制流程
函数派发
Swift中函数的派发机制有三种:静态派发,函数表派发,消息派发。
静态派发
静态派发是指在运行时不需要查表,直接跳转到方法进行执行。静态派发的性能也是最高的。c语言采用的是直接派发。
函数表派发
class类型采用函数表派发。当一个对象调用一个函数时,会从对象的头8字节找到该对象的元信息。从元信息的函数表中找到执行的函数地址,并执行函数。
对象的元信息会在编译时写入macho文件中。
class Animal {
func speak() {
print("Animal speak")
}
func eat() {
print("Animal eat")
}
func sleep() {
print("Animal sleep")
}
}
class Dog: Animal {
override func speak() {
print("Dog speak")
}
override func eat() {
print("Dog eat")
}
func run() {
print("Dog run")
}
}class Animal {
func speak() {
print("Animal speak")
}
func eat() {
print("Animal eat")
}
func sleep() {
print("Animal sleep")
}
}
class Dog: Animal {
override func speak() {
print("Dog speak")
}
override func eat() {
print("Dog eat")
}
func run() {
print("Dog run")
}
}
Dog继承于Animal。let dog: Dog = Dog()
dog 变量指向Dog对象。Dog对象在堆内存中。Dog对象的前8字节存有Dog对象的元信息。Dog对象的元信息中有该类的函数指针表。Dog中override父类Animal的函数会替换掉父类的函数存于Dogd的函数指针中。
### 消息派发
继承于NSObject的采用的是消息派发。Swift可以通过dynamic修饰来支持消息派发机制。
函数派发场景分析
在选择派发方式时,在编译期间就能决定执行哪个函数就采用静态派发。需要在运行期间决定执行哪个函数的就需要用函数表派发或者消息派发。不具动态性的的场景默认采用静态派发,这样派发效率更高。
struct无法继承,也就不具有动态性,函数派发在编译期间就能确定。
class和协议的 extension无法被子类继承,函数派发在编译期间就能确定。
class和协议的初始化方法,因为绝大多数时候需要被override的,所以采用函数表派发。
class的其他方法,如果没有被override,一般是函数表派发,但编译器也可能优化成直接派发。
class的 @objc
extension能够继承,函数派发在执行期决定,并且是采用的是消息派发。
不继承NSObject的纯Swift,@objc
的extension,采用的消息派发有点迷。消息派发机制需要有Objective-C的运行时,不继承与NSObject能有运行时的信息吗。该类的对象,
class FunctionDispatchObject {
func test1() {
print("test1")
}
}
extension FunctionDispatchObject {
@objc public func test2() {
print("test2")
}
}
// test1采用函数表派发
// test2采用消息机制派发:test2虽然写在extension里,当家里@objc,具有了动态性,可以继承了。class FunctionDispatchObject {
func test1() {
print("test1")
}
}
extension FunctionDispatchObject {
@objc public func test2() {
print("test2")
}
}
// test1采用函数表派发
// test2采用消息机制派发:test2虽然写在extension里,当家里@objc,具有了动态性,可以继承了。