1 异变方法
- swift中class和struct都能定义方法,但是有一点区别是默认情况下,值类型属性不能被自身的实例方法修改。
- 要想被自身修改 只需要在函数前加个 mutating 关键字就可以了 下面我们从sil角度分析下为什么加上这个关键字就可以了呢,首先定义两个函数 区别就是加不是这个关键字 然后从sil角度分析下到底它们内部有什么区别。
- 可以看出 加mutating的关键字的函数多了一个input(输入输出参数 传递的变量的地址)关键字 另一个的区别是let和var的区别,let很直观 是个常量 所以不能修改。而mutating修饰的函数是var 而且返回值是地址,所以它能修改,用伪代码写就是这样。
- mutating关键字的本质,传入的self被标记为inout参数,无论在mutating方法内部发生什么,都会影响外部依赖类型的一切。
2 方法调度
- oc是通过objc_mgsend调度的,swift是怎么调用的呢 简单总结就是: 先找到函数的Metadata,确定函数地址(metadata+领衔量),然后执行函数 这是基于函数表(v-table)的调度(还是一种是地址调用,下面会说),把sil文件里是这么显示的。
- 前一节中我们知道了metadata结构体,在它里面有一个typeDescriptor,这是对类的一个详细描述,不管是Class,Struct,还是Enum都有自己的Descriptor。它的结构体如下。
2 静态派发
- 上面知道class类中的方法是函数表派发,那么struct中的方法是什么派发方式呢,因为结构体没有继承关系,所以它的方法是静态派发,同时不管类还是结构体的extention中的方法也是静态派发。
-
为什么extention也是静态派发呢,类在编译结束后,v-table就已经确定了,如果后面这个类又通过extention加了一些方法,如果这个时候在把这些方法在加到v-table中,内存操作肯定会有开销,所以这个时候用静静态派发是更好的一种优化。
-
简单总结如下图
类型 --- 调度方式 --- extension
- 值类型 --- 静态派发 --- 静态派发
- 类 --- 函数表派发 --- 静态派发
- nsobject子类 --- 函数表派发 --- 静态派发
3 影响函数派发方式
- final:允许类里面的函数使用直接派发,这个修饰符会让函数失去动态性。任何函数都可以使用这个修饰符,就算是 extension 里本来就是直接派发的函数。这也会让Objective-C 的运行时获取不到这个函数,不会生成相应的 selector。
- dynamic:函数均可添加这个关键字,为非objc类和值类型的函数赋予动态性,但派发方式还是函数表派发。
- @objc 该关键字可以将swift函数暴露给object运行时,依旧是函数表派发。
- @objc+dynamic:消息源发方式 用这两关键字修饰的函数就可以用runtime的API实现method-swizzling. 总结 在开发实际中 final和objc+dynamic这两种方式是最常用的。