iOS底层学习 - 类的前世此生(一)

经过上一章节的学习,咱们已经了解了对象的底层实现,那么类,NSObject在底层是以什么方式存在的呢,属性,方法,协议,都是怎么存在于类里面的,这一章节就来深刻了解一下数组

传送门☞iOS底层探索-准备工做bash

传送门☞iOS底层学习-OC对象前世此生app

准备工做

建立以下类post

@interface LGPerson : NSObject{
    NSString *hobby;
}

@property (nonatomic, copy) NSString *nickName;

- (void)sayHello;
+ (void)sayHappy;

@end
复制代码
@implementation LGPerson

- (void)sayHello{
    NSLog(@"LGPerson say : Hello!!!");
}

+ (void)sayHappy{
    NSLog(@"LGPerson say : Happy!!!");
}


@end
复制代码

指针偏移

int a = 10; 
int b = 10; 
LGNSLog(@"%d -- %p",a,&a);
LGNSLog(@"%d -- %p",b,&b);   

输出:
KC打印: 10 -- 0x7ffeefbff4fc
KC打印: 10 -- 0x7ffeefbff4f8
复制代码
LGPerson *p1 = [LGPerson alloc];
LGPerson *p2 = [LGPerson alloc];
LGNSLog(@"%@ -- %p",p1,&p1);
LGNSLog(@"%@ -- %p",p2,&p2);    

输出:
KC打印: <LGPerson: 0x100544c30> -- 0x7ffeefbff4f8
KC打印: <LGPerson: 0x100545a20> -- 0x7ffeefbff4f0
复制代码

经过以上两个例子,能够得出学习

  • 常量的是值拷贝,只是对值就好了赋值,地址不一样
  • 对象的是引用拷贝,各自开辟了内存空间,对象的地址不一样,对象地址的指针地址也不相同

// 数组指针
    int c[4] = {1,2,3,4};
    int *d   = c;
    NSLog(@"%p - %p - %p",&c,&c[0],&c[1]);
    NSLog(@"%p - %p - %p",d,d+1,d+2);

    for (int i = 0; i<4; i++) {
    // int value = c[i];
    int value = *(d+i);
    LGNSLog(@"%d",value);
    }
    
    NSLog(@"指针 - 内存偏移");
复制代码

经过上述的例子,能够获得

  • 一个对象的地址就是首个元素的地址
  • 经过对指针的偏移,能够获得相对应的元素值

类的底层实现

类的实现实在编译期就申请好内存了,因此咱们须要能够在编译期查看类的底层实现 编译后经过MackOView能够看到,LGPerson类已经生成ui

LGPerson *person = [LGPerson alloc];
Class pClass     = object_getClass(person);
复制代码

经过clang命令编译main文件后获得cpp文件,能够发现,Class的底层结构是objc_class,里面有一个废弃的isa指针,因此不作研究.atom

在objc源码中咱们能够发现, objc_class结构以下,是继承自 objc_object,因此其实类也是一种对象,所谓 万物皆对象

其中Class中ISA对象是在 objc_object定义中,NSObject是OC对objc_object的仿写,结构是同样的

咱们能够看到objc_class成员变量主要有一下四个spa

  • Class ISA:主要关联类
  • Class superclass:父类
  • cache_t cache
  • class_data_bits_t bits

cache_t cache

经过源码咱们可得 cache_t cache为一个结构体,所占字节数也如图所示,共16字节,不足以存放类中的属性和方法,因此,属性方法等数据,必定存储在 class_data_bits_t bits结构中

class_data_bits_t bits

属性方法等数据是存储在class_data_bits_t bits中,而且能够看到class_rw_t中的数据都是来自于此。且结构中存在属性,方法,协议列表等,所以遇到跟踪class_data_bits_t bits,能够经过指针偏移来获取到class_data_bits_t bits中的数据,经过结构可知,在首地址偏移32字节便可获得3d

咱们能够经过LLDB命令和指针偏移,一步步来探索指针

首先打印出类的16进制存储

接着将首地址 0x1000023b0偏移32字节,在16进制下也就是 0x20,获得 0x00000001000023d0,因为 class_data_bits_t bits不是对象类型,因此须要强转一下,获得以下地址

继续,咱们想获得 class_rw_t结构的data,那么调用方法可获得 class_rw_t中的内容以下

属性变量存储

经过上面打印出的class_rw_t中,咱们能够看到,根据名称,属性应该是存储在properties中,经过命令打印可得下图

根据 list_array_tt结构可得此为二维数组,且根据结构可得数据应该存储在 list结构中
继续打印能够 property_list_t,可得其继承自 entsize_list_tt,打印其中的元素 Element first;能够获得保存的类中保存的属性

成员变量存储

在上述探寻属性的过程当中,咱们并无看到成员变量的保存,因此,成员变量不是保存在properties中,应该是存在其余的结构中,根据class_rw_t结构思考可得,ro中存储成员变量的可能性较大,因此按照上述流程探寻ro

到这里,咱们发现了 const class_ro_t中存在一个 ivars,成员变量应该在此
打印 ivars,发现其中first中已经有了咱们的属性hobby,验证成功
可是经过 count咱们发现为2,因此应该还有一个成员变量,咱们能够打印发现是 _nickName,因此说明 属性会自动生成对应的下划线成员变量

实例方法存储

经过对class_rw_t研究,咱们发现有一个methods元素,那么这个颇有可能就是存放方法的列表,经过打印咱们可得

此结构和属性的相似,因此咱们接着打印 list,能够发现咱们的实例方法 sayHello,此时已经验证了方法的存储

同理, count为4,咱们看一下剩下的是什么方法。咱们发现是属性的get和set方法已经默认的析构方法,验证了 属性会自动建立get和set

类方法存储

经过上面的存储探究,咱们发现并无类对象中并无类方法的存储,且类继承子NSObject,且NSObject中并无对应的类方法,因此,咱们能够猜想,类方法是存在于元类之中的,经过类对象的isa能够找到元类

经过对元类结构进行上述LLDB探索,能够清楚的发现类方法存在于其中

总结

经过上面的一系列探索,咱们可得以下结论

  • 万物皆对象,均继承自objc_object
  • 属性和成员变量都存放在类的class_rw_t结构体中
  • 属性会自动生成get和set方法,成员变量不会
  • 实例方法会存在于类中,而类方法会存在于元类之中,且是以实例方法的形式存在的
相关文章
相关标签/搜索