对象在底层的本质就是结构体
先看一个简单的.m文件,然后我们通过clang进行编译还原成OC的上层代码C/C++。
然后还原成.cpp文件我们可以看到对象Person其在底层其实是一个结构体
从上图我们可以看到Person其实就是一个定义的结构体,而他的属性、成员变量。其实就是它的结构体的成员,而且我们还会发现,属性age、和nickName比着成员变量 _name、height多了一些其他东西。(这就可以牵扯出属性与成员变量的不同点在哪)而除了这些,还有一个结构体
NSObject_IMPL。
结构体NSObject_IMPL
我们通过这个.cpp文件继续发现到结构体NSObject_IMPL其实就是我们经常说的isa指针。但是Class又是什么呢,我们可以大胆的猜测它是不是也是个结构体呢。
Class isa其实就是 objc_class *isa。结构体指针类型
而且我们平时习惯用的id,其实就是objc_object *类型的。
其实我们可以通过objc源码探索就会发现objc_class其实是继承于结构体objc_object
属性和成员变量
我们之前已经看到一个对象的属性和成员变量是有所不同的,但他们都是结构体的成员。但是发现属性比着成员变量其实多了ivar、setter方法、getter方法。
而且setter方法和getter方法,多了两个参数(
Person *self, SEL _cmd),两个隐藏参数
而成员变量没有这两个方法。 而OBJC_IVAR_$_Person$_nickName就是上面提到属性比成员变量多的东西ivar
怎么得到一个属性的值呢,其实就是 指针地址 + 偏移量得到了该属性的指针地址,然后进行*(NSString)解引用
总结
1、我们发现对象在底层的实现是结构体,而且其实是objc_object类型的
2、每一个对象都有isa指针,而他其实就是Class类型的,而Class其实就是objc_class *结构体指针类型
3、我们平时用的id其实是objc_object *结构体指针类型
4、对象的属性与成员变量的区别就是 属性有ivar + setter + getter。
isa 的探究
我们通过上面已经知道,isa是对象不可分割的一部分,而且是很重要的一部分,那么究竟什么是isa呢,我们先引入两个知识点位域与联合体(共用体)
位域
例如一个结构体person
struct Person {
BOOL left;
BOOL right;
BOOL top;
BOOL bottom;
} person;
NSLog(@"----%lu", sizeof(person));
它的结果显而易见是4,也就是说结构体person占用了4字节(32位)内存。但是我们如果实际上存储根本用不到这么大的内存,0000分别代表4个成员就足够了,也就是0.5字节。然后就有了位域
struct Person {
BOOL left : 1;
BOOL right: 1;
BOOL top : 1;
BOOL bottom:1;
} person;
NSLog(@"----%lu", sizeof(person));
而这次的结果就是1了,因为内存最小就是1字节。就可以分别代表四个成员了
联合体(共用体)
联合体的特性是互斥,而结构体的特性是共存。也就是说联合体内的成员只有一个才是有效值。联合体和位域一般是一起使用的
联合体内如果修改一个成员的值得时候就会影响到其他成员。因为它的所有成员都占用的是一段内存。它的内存是成员内最大内存的数据类型影响。同一时刻只保存当前修改的成员值。例如:
union Way{
int a;
short b;
char c;
} way;
NSLog(@"----%lu", sizeof(way));
结果是最大内存的int,为4字节。
isa 的探索
我们在探究alloc底层实现的时候可以发现在进行class与内存地址绑定的时候会进行以下代码
然后我们会发现一个名字叫做
isa_t的联合体,它其实就是所为的isa。
那么为什么会这样呢,是因为苹果在存储类型地址的时候就想到,这个地址根本不会用到8个字节(64位)那么多,它就想存储更多的信息,例如:引用计数,弱引用,关联对象,析构函数等等。所以才会用到了这个联合体。但是最重要的是如何分配这个内存从而能存储这么多数据呢,就会用到了位域。所以我们可以看到其实真正重要的是
ISA_BITFIELD。我们看看里面是包含了什么。只看64位情况
nonpointer 纯isa
我们有时候会看到nonpointer,但是这个是什么呢。它代表的是纯isa意思是只包含了类的指针地址值和类信息。但是默认都是不纯的