Swift 5.0的Runtime机制浅析
每一个方法调用都是通过读取方法在虚表中的索引获取到了方法函数的真实地址,然后再执行间接调用。在这个过程虚表索引的值是在编译时就确定了,因此不再需要通过方法名来在运行时动态的去查找真实的地址来实现函数调用了。虽然索引的位置在编译时确定的,但是基类和派生类虚表中相同索引处的函数的地址确可以不一致,当派生类重写了父类的某个方法时,因为会分别生成两个类的虚表,在相同索引位置保存不同的函数地址来实现多态的能力。 每个方法函数名字都和源代码中不一样了,原因在于在编译链接是系统对所有的方法名称进行了重命名处理,这个处理称为命名修饰。之所以这样做是为了解决方法重载和运算符重载的问题。因为源代码中重载的方法函数名称都一样只是参数和返回类型不一样,因此无法简单的通过名字进行区分,而只能对名字进行修饰重命名。另外一个原因是Swift还提供了命名空间的概念,也就是使得可以支持不同模块之间是可以存在相同名称的方法或者函数。因为整个重命名中是会带上模块名称的。下面就是Swift中对类的对象方法的重命名修饰规则: _$s<模块名长度><模块名><类名长度><类名>C<方法名长度><方法名>yy<参数类型1>_<参数类型2>_<参数类型n>F 就比如上面的CA类中的foo1两个同名函数在编译链接时刻就会被分别重命名为:
下面这张图就清晰的描述了Swift类的对象方法调用以及类描述信息。 Swift类中成员变量的访问 虽然说OC类和Swift类的对象内存布局非常相似,每个对象实例的开始部分都是一个isa数据成员指向类的描述信息,而类中定义的属性或者变量则一般会根据定义的顺序依次排列在isa的后面。OC类还会为所有成员变量,生成一张变量表信息,变量表的每个条目记录着每个成员变量在对象内存中的偏移量。这样在访问对象的属性时会通过偏移表中的偏移量来读取偏移信息,然后再根据偏移量来读取或设置对象的成员变量数据。在每个OC类的get和set两个属性方法的实现中,对于属性在类中的偏移量值的获取都是通过硬编码来完成,也就是说是在编译链接时刻决定的。 对于Swift来说,对成员变量的访问得到更加的简化。系统会对每个成员变量生成get/set两个函数来实现成员变量的访问。系统不会再为类的成员变量生成变量偏移信息表,因此对于成员变量的访问就是直接在编译链接时确定成员变量在对象的偏移位置,这个偏移位置是硬编码来确定的。下面展示Swift源代码和C伪代码对数据成员访问的实现:
(编辑:PHP编程网 - 黄冈站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |