加入收藏 | 设为首页 | 会员中心 | 我要投稿 晋中站长网 (https://www.0354zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 站长资讯 > 评论 > 正文

Swift 5.0的Runtime机制浅析

发布时间:2019-07-19 12:15:07 所属栏目:评论 来源:欧阳大哥2013
导读:导读:你想知道Swift内部对象是如何创建的吗?方法以及函数调用又是如何实现的吗?成员变量的访问以及对象内存布局又是怎样的吗?这些问题都会在这篇文章中得到解答。为了更好的让大家理解这些内部实现,我会将源代码翻译为用C语言表示的伪代码来实现。 Objec

从上面的代码可以看出,Swift类会为每个定义的成员变量都生成一对get/set方法并保存到虚函数表中。所有对对象成员变量的方法的代码都会转化为通过虚函数表来执行get/set相对应的方法。 下面是Swift类中成员变量的实现和内存结构布局图:

Swift 5.0的Runtime机制浅析

结构体中的方法

在Swift结构体中也可以定义方法,因为结构体的内存结构中并没有地方保存结构体的信息(不存在isa数据成员),因此结构体中的方法是不支持多态的,同时结构体中的所有方法调用都是在编译时硬编码来实现的。这也解释了为什么结构体不支持派生,以及结构体中的方法不支持override关键字的原因。

类的方法以及全局函数

Swift类中定义的类方法和全局函数一样,因为不存在对象作为参数,因此在调用此类函数时也不会存在将对象保存到x20寄存器中这么一说。同时源代码中定义的函数的参数在编译时也不会插入附加的参数。Swift语言会对所有符号进行重命名修饰,类方法和全局函数也不例外。这也就使得全局函数和类方法也支持名称相同但是参数不同的函数定义。简单的说就是类方法和全局函数就像C语言的普通函数一样被实现和定义,所有对类方法和全局函数的调用都是在编译链接时刻硬编码为函数地址调用来处理的。

OC调用Swift类中的方法

如果应用程序是通过OC和Swift两种语言混合开发完成的。那就一定会存在着OC语言代码调用Swift语言代码以及相反调用的情况。对于Swift语言调用OC的代码的处理方法是系统会为工程建立一个桥声明头文件:项目工程名-Bridging-Header.h,所有Swift需要调用的OC语言方法都需要在这个头文件中声明。而对于OC语言调用Swift语言来说,则有一定的限制。因为Swift和OC的函数调用ABI规则不相同,OC语言只能创建Swift中从NSObject类中派生类对象,而方法调用则只能调用原NSObject类以及派生类中的所有方法以及被声明为@objc关键字的Swift对象方法。如果需要在OC语言中调用Swift语言定义的类和方法,则需要在OC语言文件中添加:#import "项目名-Swift.h"。当某个Swift方法被声明为@objc关键字时,在编译时刻会生成两个函数,一个是本体函数供Swift内部调用,另外一个是跳板函数(trampoline)是供OC语言进行调用的。这个跳板函数信息会记录在OC类的运行时类结构中,跳板函数的实现会对参数的传递规则进行转换:把x0寄存器的值赋值给x20寄存器,然后把其他参数依次转化为Swift的函数参数传递规则要求,最后再执行本地函数调用。整个过程的实现如下:

  1. ////////Swift源代码 
  2.  
  3. //Swift类定义 
  4. class MyUIView:UIView { 
  5.   @objc     
  6.   open func foo(){} 
  7.  
  8. func main() { 
  9.   let obj = MyUIView() 
  10.   obj.foo() 
  11.  
  12. //////// OC源代码 
  13. #import "工程-Swift.h" 
  14.  
  15. void main() { 
  16.   MyUIView *obj = [MyUIView new]; 
  17.   [obj foo]; 
  18.  
  19. ////////C伪代码 
  20.  
  21. //...........................................运行时定义部分 
  22.  
  23. //OC类的方法结构体 
  24. struct method_t { 
  25.     SEL name; 
  26.     IMP imp; 
  27. }; 
  28.  
  29. //Swift类描述 
  30. struct swift_class { 
  31.     ...   //其他的属性,因为这里不关心就不列出了。 
  32.     struct method_t  methods[1]; 
  33.     ...   //其他的属性,因为这里不关心就不列出了。 
  34.     //虚函数表刚好在结构体的第0x50的偏移位置。 
  35.     IMP vtable[1]; 
  36. }; 
  37.  
  38. //...........................................源代码中类的定义和方法的定义和实现部分 
  39.  
  40. //类定义 
  41. struct MyUIView { 
  42.       struct swift_class *isa; 
  43.  
  44. //类的方法函数的实现 
  45.  
  46. //本体函数foo的实现 
  47. void foo(){} 
  48. //跳板函数的实现 
  49. void trampoline_foo(id self, SEL _cmd){ 
  50.      asm("mov x20, x0"); 
  51.      self->isa->vtable[0](); //这里调用本体函数foo 
  52.  
  53. //类的描述信息构建,这些都是在编译代码时就明确了并且保存在数据段中。 
  54. struct swift_class classMyUIView; 
  55. classMyUIView.methods[0] = {"foo", &trampoline_foo}; 
  56. classMyUIView.vtable[0] = {&foo}; 
  57.  
  58.  
  59. //...........................................源代码中程序运行的部分 
  60.  
  61. //Swift代码部分 
  62. void main() 
  63.   MyUIView *obj = MyUIView.__allocating_init(classMyUIView); 
  64.   obj->isa = &classMyUIView; 
  65.    asm("mov x20, obj"); 
  66.    //Swift方法foo的调用采用间接调用实现。 
  67.    obj->isa->vtable[0](); 
  68.  
  69. //OC代码部分 
  70. void main() 
  71.   MyUIView *obj = objc_msgSend(objc_msgSend(classMyUIView, "alloc"), "init"); 
  72.   obj->isa = &classMyUIView; 
  73.   //OC语言对foo的调用还是用objc_msgSend来执行调用。 
  74.   //因为objc_msgSend最终会找到methods中的方法结构并调用trampoline_foo  
  75.   //而trampoline_foo内部则直接调用foo来实现真实的调用。 
  76.   objc_msgSend(obj, @selector(foo)); 

(编辑:晋中站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读