美文网首页
iOS底层原理 - 探寻Runtime本质(二)

iOS底层原理 - 探寻Runtime本质(二)

作者: hazydream | 来源:发表于2018-05-09 09:45 被阅读25次

1. Class结构的本质

上一章对isa结构的本质做了探究,下面探究Class的内部结构。

iOS底层原理 - OC对象的本质(二)可知:

isa指针指向图示
  • class对象和meta-class对象结构相似,meta-class对象是特殊的class对象;
  • 关于存储方法:
    class对象存储对象方法,meta-class对象存储类方法。

(1) Class的结构

OC源码可以找出Class的结构:

Class的结构

Class的结构可知:

  • 通过bits & FAST_DATA_MASK可以得到class_rw_t结构体;
  • class_rw_t结构体中存放方法列表、属性列表、协议列表等,并包括一个指向class_ro_t结构体的指针rorwreadwrite可读可写)
  • class_ro_t结构体中存放类的初始内容,包括方法列表、属性列表、协议列表等,并包括成员变量列表。roreadonly只读)

(2) class_rw_t 结构

class_rw_t结构体中存放方法列表(method_array_t类型)、属性列表 (property_array_t类型)、协议列表(protocol_array_t类型),接下来分析三者的结构:

method_array_t、property_array_t、protocol_array_t 结构

通过以上源码可知:
method_array_tproperty_array_tprotocol_array_t结构相同;

下面只对method_array_t的内部结构进行分析,property_array_tprotocol_array_t的可以类推。

method_array_t内部结构

method_array_t内部结构可知:
method_array_t是一个二维数组,每个元素是method_list_t
method_list_t是一维数组,每个元素是method_t
class_rw_t里面的methodspropertiesprotocols是二维数组,是可读可写的,包含了类的初始内容、分类的内容。

(3) class_ro_t结构

class_ro_t结构体中存放方法列表(method_list_t类型)、属性列表 (property_list_t类型)、协议列表(protocol_list_t类型),所以method_list_t的内部结构如下:

method_list_t内部结构

method_list_t内部结构可知:
method_list_t是一维数组,每个元素是method_t
class_ro_t里面的baseMethodListbaseProtocolsivarsbaseProperties是一维数组,是只读的,包含了类的初始内容。

(4) class_rw_t结构与class_ro_t结构

iOS底层原理 - 探寻Category本质(一)可知:

class_rw_t结构体通过attachList函数内的memmovememcpy操作将所有分类的对象方法附加到类对象的方法列表中。

通过源码进入realizeClass函数,查看class_rw_tclass_ro_t两者之间的关系:

realizeClass函数

realizeClass函数可知:

类的初始信息(方法、属性、协议、成员变量等)存放在class_ro_t中;
程序运行时,class_ro_t中的列表和分类中的列表合并起来存放在class_rw_t中。

(5) method_t结构

由以上分析可知:
class_rw_t结构体和class_ro_t结构体的方法列表,其最小单位都是method_t结构体。

通过源码进入method_t结构体:

method_t结构

由源码可知:method_t结构体包含三个成员变量,接下对三者进行分析:

1> SEL name;函数名
  • SEL代表方法/函数名,即选择器,底层结构和char指针类似;
    通过@selector()sel_registerName()获得;
    通过sel_getName()NSStringFromSelector()SEL转成字符串;
  • 不同类中相同名字的方法,所对应的方法选择器是相同的。

验证如下:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        SEL sel1 = @selector(init);
        SEL sel2 = sel_registerName("init");
        NSLog(@"%p %p", sel1, sel2);

        const char *str1 = sel_getName(sel1);
        NSString *str2 = NSStringFromSelector(sel2);
        NSLog(@"%s %@", str1, str2);
    }
    return 0;
}
打印结果

打印的地址与方法名都相同,验证成功。

2> const char *types;编码(返回值类型,参数类型)

Person类中写一个方法:

// TODO: -----------------  Person类  -----------------
@interface Person : NSObject
@end

@implementation Person
- (int)testHeight:(float)height age:(int)age {
    return 0;
}
@end

Person.m文件转为C++语言,找到_class_ro_t结构体,因为它存着类的初始信息:

_class_ro_t源码

由以上源码可知:
方法名为:testHeight:age:
编码types为:"i24@0:8f16i20"
函数地址为:_I_Person_testHeight_age_

编码types为:"i24@0:8f16i20",采用了iOS的@encode的指令,该指令将具体的类型表示成字符串编码。部分编码如下:

Objective-C type encodings

我们通过该表进行分析:

// 函数默认会带有self和_cmd两个参数
- (int)testHeight:(float)height age:(int)age;
types - i24@0:8f16i20
types - i    24    @    0    :    8    f    16    I    20
        int        id        SEL      float      int
       返回值      self      _cmd      height     age
// 分析数字意义:
24 - 所有参数占24个字节
0 - self是从第0个字节开始存储,id类型占8个字节
8 - _cmd是从第8个字节开始存储,SEL类型占8个字节
16 - height是从第16个字节开始存储,float类型占4个字节
20 - age是从第20个字节开始存储,int类型占4个字节
3> IMP imp;指向函数的指针(函数地址)

IMP的内部实现如下:
typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...);

IMP代表函数的具体实现,存储着函数地址。

2. 方法缓存cache_t

iOS底层原理 - OC对象的本质(二)可知:

isa指针、superclass指针指向汇总图示

如果调用对象方法:

  1. 首先通过instance的isa指向Class,
    然后在Class对象中class_rw_t结构体的methods数组中查找方法;
  2. 如果Class对象中找不到该方法,
    需要通过superclass指针找到父类的Class对象,
    然后在父类的Class对象中class_rw_t结构体的methods数组中查找方法
  3. 如果父类的Class对象中找不到该方法,再次通过superclass指针找上一级的父类,如此循环。

如果一个方法需要调用许多次的话,需要循环遍历多次以上步骤;
iOS采用方法缓存cache_t,用散列表(哈希表)缓存曾经调用过的方法,可以提高方法的查找速度。

调用方法时优先去缓存中查找此方法,缓存中没有再去类对象中查找,然后将其加入缓存中方便下次调用。

(1) cache_t缓存方式

OC源码可以找出cache_t的结构:

cache_t结构

cache_t结构可知:

  • cache_t结构体存储_buckets_mask_occupied
    1> _buckets散列表,存放方法选择器充当的Key值,以及函数的内存地址IMP
    2> _mask散列表的长度减1,任何数通过与_mask进行按位与运算之后获得的值都会小于等于_mask,不会出现数组溢出的情况;
    3> _occupied已经缓存的方法数量。

cache_t结构可以归纳如下:

cache_t结构

接下来查看OC内部如何处理缓存:

cache_t::find函数

相关文章

网友评论

      本文标题:iOS底层原理 - 探寻Runtime本质(二)

      本文链接:https://www.haomeiwen.com/subject/gwfnrftx.html