对象在C++层面的表现
我们先准备以下代码
@interface ELPerson : NSObject
@property (nonatomic, strong) NSString *ELname;
- (void)testfunction;
@end
用如下代码把他编译成c++代码
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
打开生成的.cpp文件,我们搜索一下,找到
struct ELPerson_IMPL {
struct NSObject_IMPL NSObject_IVARS;
NSString *_ELname;
};
我们继续搜索ELPerson_IMPL
struct NSObject_IMPL {
Class isa;
};
他在c++层面就是这样一个结构体,里面并没有存储我们定义的方法。就是结构体指针isa
。
NSObject
在runtime
中对应的源码为
/// Represents an instance of a class.
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
类对象的源码应该为
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
。。。函数
}
objc_class
继承objc_object
,也就继承了isa。这里也说明:对象包含类对象和实例对象。
探寻ISA的实现
我们继续探究ISA
,他的源码应该为如下:(x86_64架构精简代码说明)
union isa_t {
。。。//省略
struct {
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 unused : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 8
}
};
nonpointer
在0位,表示是否对isa
指针开启指针优化。0:纯isa
指针,这里面直接存储Class
、Meta-Class
对象的地址(没有使用位域)1:代表优化过的,使用了位域,里面包含位域里面的这些信息(类对象地址,isa
包含了类信息、对象的引用计数等),想要得到类对象
、元类对象
,需要&上ISA_MASK
(也可以使用地址平移)才能得到。
has_assoc
在1位,表示关联对象标志位,0:没有,1:有。
has_cxx_dtor
在2位,表示该对象是否有C++
或者Objc
的析构器,如果有析构函数,则需要做析构逻辑,如果没有,则可以更快的释放对象。
shiftcls
在x86
架构中占用3~46
位,表示存储类指针的值。开启指针优化的情况下,在arm64
架构中占用3~35
位。
magic
在x86
架构中占用47~52
位,在arm64
架构中占用36~41
位,用于调式器判断当前对象是真的对象还是没有初始化的空间。
weakly_referenced
在x86
架构中占用第53
位,在arm64
架构中占用第42
位,标志对象是否被指向或者曾经指向一个ARC
的弱变量,没有弱引用的对象可以更快释放。
unused
在x86
架构中占用第54
位,在arm64
架构中占用第43
位,标志对象是否正在释放内存。
has_sidetable_rc
在x86
架构中占用第55
位,在arm64
架构中占用第44
位,表示当对象引用计数大于10
时,则需要借用该变量存储进位。
extra_rc
在x86
架构中占用56~63
位,在arm64
架构中占用45~63
位,当表示该对象的引用计数值时,实际上是引用计数值减1
,例如:如果对象的引用计数为10
,那么extra_rc
为9
,如果引用计数大于10
,则需要使用到has_sidetable_rc
。
我们直接测试一下shiftcls
位置是否是类的信息
(lldb)x/4gx per //打印对象地址,首地址为isa
0x10065c0d0: 0x011d8001000082e5 0x0000000000000000
0x10065c0e0: 0x0000000000000000 0x0000000000000000
(lldb) p/x per.class //打印类对象地址
(Class) $8 = 0x00000001000082e0 ELPerson
(lldb)p/x 0x011d8001000082e5 >> 3 << 20 >> 17 //算法在下面说明
(long) $9 = 0x00000001000082e0 //说明对象的isa里面包含类对象的地址信息
(lldb)
位运算图解:
再打印一下二进制的内存情况,方便对照,如下:
(lldb) x/1gt per0x10065c0d0: 0b0000000100011101100000000000000100000000000000001000001011100101(lldb)
网友评论