iOS OC 对象原理探索三
前言:
前面分析了对象创建时alloc
是怎么申请内存空间,通过研究Class
的第一个属性是isa
,我们都知道isa
的指向其类,对象的isa
指向类Class
, 那么对象创申请完内存空间之后是怎么和isa
关联起来的呢?isa
指针是怎么创建的呢?
isa
指针的具体走向是什么呢?isa
指针所占的8字节64位中具体存储了那些信息呢?接下来我们来探索一下:
1. isa
指针结构
isa
源码
#include "isa.h"
// 联合体 公用 联合体里面最大的那个内存
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
#if defined(ISA_BITFIELD)
struct {
ISA_BITFIELD; // defined in isa.h
};
#endif
};
isa
底层是联合体,ISA_BITFIELD
源码如下:
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
# define ISA_MAGIC_MASK 0x001f800000000001ULL
# define ISA_MAGIC_VALUE 0x001d800000000001ULL
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t deallocating : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 8
# define RC_ONE (1ULL<<56)
# define RC_HALF (1ULL<<7)
可以看出在不通架构中isa
内成员所占字节也不相同,这些成员所占字节之和刚好是64位,8字节。每个成员多占位数和存储内存如下:
nonpointer : 1字节;
表示是否对isa指针开启优化
0:纯isa指针 1:不止是类对象地址,包含了类信息、对象的引用计数等
has_assoc : 1字节;
关联对象标志,0:没有 1:存在
has_cxx_dtor : 1字节;
该对象是有有C++或objc的析构器,
有析构函数:做逻辑出来 无: 可以更快的释放对象
shiftcls : 33字节; MACH_VM_MAX_ADDRESS 0x1000000000
存储类指针的值 开启指针优化情况下,在arm64架构中用s33位来存储类指针
magic : 6;
调试器判断f当前对象是真的对象还是没有初始化的空间
weakly_referenced : 1;
标志对象是否指向或曾经指向一个ARC的弱引用,无弱引用可更快释放
deallocating : 1;
标志对象是否正在是否内存
has_sidetable_rc : 1;
当前对象引用计数大于10时,则需要借用该变量的存储进位
extra_rc : 19
表示该对象的引用计数值,实际是引用计数减1,
例: 当前对象引用计数为10,extra_rc = 9,如果引用计数大于10,
则需要使用has_sidetable_rc进位进位
2. isa
的初始化
ias
指针初始化分析如下:
isa
是联合体,具有互斥性,对cls赋值,就不会对bits和isa
内的结构体成员进行赋值。
在对新创建的isa_t newisa(0)
进行赋值时,根据SUPPORT_INDEXED_ISA
判断,对其进行不通的赋值。
isa_t newisa(0);
#if SUPPORT_INDEXED_ISA
assert(cls->classArrayIndex() > 0);
newisa.bits = ISA_INDEX_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
newisa.has_cxx_dtor = hasCxxDtor;
newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
newisa.bits = ISA_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
newisa.has_cxx_dtor = hasCxxDtor;
newisa.shiftcls = (uintptr_t)cls >> 3;
#endif
// This write must be performed in a single store in some cases
// (for example when realizing a class because other threads
// may simultaneously try to use the class).
// fixme use atomics here to guarantee single-store and to
// guarantee memory order w.r.t. the class index table
// ...but not too atomic because we don't want to hurt instantiation
isa = newisa;
3. isa
关联对象和类
上面提到isa
指针中shiftcls
存储类指针的值,通过打印类.class
的值然后>>3
,得到的值和通过对isa
的shiftcls
成员>>3
,再>>20 <<20
得到的值相等。
通过研究API object_getClass(object)
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
最终的返回结果是(Class)(isa.bits & ISA_MASK);
源码如下:
inline Class
objc_object::ISA()
{
assert(!isTaggedPointer());
#if SUPPORT_INDEXED_ISA
if (isa.nonpointer) {
uintptr_t slot = isa.indexcls;
return classForIndex((unsigned)slot);
}
return (Class)isa.bits;
#else
return (Class)(isa.bits & ISA_MASK);
#endif
}
代码验证:
LGPerson *object = [LGPerson alloc];
打印结果:
通过打印对象结构,第一个必然是isa
指针,通过对isa
& 掩码 ISA_MASK
得到的值和打印对象的class
一致。由此可以看出isa
是通过shiftcls
和关联到类的
4. isa
的走位
同一个对象可以创建多个?同一个类可以创建多个吗?类只可以创建一个
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ckJs0ukG-1586242843847)(https://user-gold-cdn.xitu.io/2019/12/24/16f38705b086af5f?w=604&h=265&f=png&s=95565)]
类的内存,第一个位置必然是`isa`,指向`LGPerson`元类
对象是程序猿根据类实例化的
类是代码编写的,内存中只有一份,是系统创建的
元类是系统编译时,系统编译器创建的,便于方法的编译
对象的isa
指向类对象
,类对象
的isa
指向元类,元类isa
指向什么呢?
通过打印类结构,分析如下:
对象isa
-->类对象
—>元类
–>根元类
–>根元类
NSObject 根类 -> 根元类
经典isa
走位图
isa
:对象
-->类对象
—>元类
–>根元类
–>根元类
superClass继承关系
:NSObject
父类为nil
,根元类
的父类NSObject
5. 对象的本质
通过对下面代码的clang -rewrite-objc main.m -o mian.cpp
生成的c++
文件进行分析
@interface LGPerson : NSObject{
NSString *name;
}
@property (nonatomic, copy) NSString *name;
@end
@implementation LGPerson
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"123");
}
return 0;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CblpEreT-1586242843857)(https://user-gold-cdn.xitu.io/2019/12/25/16f3bd726ade9bb5?w=955&h=560&f=png&s=132211)]
可以看出对象的底层本质是结构体struct
,通过property
的属性,底层会自动生成setter
和getter
,并且会生成一个_属性
的成员变量,底层编译不会生成相应的 setter
和getter
6. union
联合体补充
1. 什么是联合体
1. union中可以定义多个成员,union的大小由最大的成员的大小决定。
2. union成员共享同一块大小的
内存,一次只能使用其中的一个成员。
3. 对某一个成员赋值,会覆盖其他成员的值(也不奇怪,因为他们共享一块内存。但前提是成员所占字节数
相同,当成员所占字节数不同时只会覆盖相应字节上的值,比如对char成员赋值就不会把整个int成员覆
盖掉,因为char只占一个字节,而int占四个字节)
4. union的存放顺序是所有成员都从低地址开始存放的。
2.联合体的好处
使代码存储数据高效率的同时,有较强的可读性,可以使用共用体来增强代码可读性,同时使用位运算来提高
数据存取的效率