對象在C++層面的表現(xiàn)
我們先準備以下代碼
@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;
};
我們繼續(xù)搜索ELPerson_IMPL
struct NSObject_IMPL {
Class isa;
};
他在c++層面就是這樣一個結(jié)構(gòu)體油坝,里面并沒有存儲我們定義的方法盒让。就是結(jié)構(gòu)體指針isa
搜锰。
NSObject
在runtime
中對應(yīng)的源碼為
/// Represents an instance of a class.
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
類對象的源碼應(yīng)該為
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
汗贫。身坐。秸脱。函數(shù)
}
objc_class
繼承objc_object
,也就繼承了isa部蛇。這里也說明:對象包含類對象和實例對象摊唇。
探尋ISA的實現(xiàn)
我們繼續(xù)探究ISA
,他的源碼應(yīng)該為如下:(x86_64架構(gòu)精簡代碼說明)
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
指針開啟指針優(yōu)化岛请。0:純isa
指針,這里面直接存儲Class
警绩、Meta-Class
對象的地址(沒有使用位域)1:代表優(yōu)化過的崇败,使用了位域,里面包含位域里面的這些信息(類對象地址肩祥,isa
包含了類信息后室、對象的引用計數(shù)等),想要得到類對象
搭幻、元類對象
咧擂,需要&上ISA_MASK
(也可以使用地址平移)才能得到逞盆。
has_assoc
在1位檀蹋,表示關(guān)聯(lián)對象標志位,0:沒有云芦,1:有俯逾。
has_cxx_dtor
在2位,表示該對象是否有C++
或者Objc
的析構(gòu)器舅逸,如果有析構(gòu)函數(shù)桌肴,則需要做析構(gòu)邏輯,如果沒有琉历,則可以更快的釋放對象坠七。
shiftcls
在x86
架構(gòu)中占用3~46
位,表示存儲類指針的值旗笔。開啟指針優(yōu)化的情況下彪置,在arm64
架構(gòu)中占用3~35
位。
magic
在x86
架構(gòu)中占用47~52
位蝇恶,在arm64
架構(gòu)中占用36~41
位拳魁,用于調(diào)式器判斷當前對象是真的對象還是沒有初始化的空間。
weakly_referenced
在x86
架構(gòu)中占用第53
位撮弧,在arm64
架構(gòu)中占用第42
位潘懊,標志對象是否被指向或者曾經(jīng)指向一個ARC
的弱變量姚糊,沒有弱引用的對象可以更快釋放。
unused
在x86
架構(gòu)中占用第54
位授舟,在arm64
架構(gòu)中占用第43
位救恨,標志對象是否正在釋放內(nèi)存。
has_sidetable_rc
在x86
架構(gòu)中占用第55
位释树,在arm64
架構(gòu)中占用第44
位忿薇,表示當對象引用計數(shù)大于10
時,則需要借用該變量存儲進位躏哩。
extra_rc
在x86
架構(gòu)中占用56~63
位署浩,在arm64
架構(gòu)中占用45~63
位,當表示該對象的引用計數(shù)值時扫尺,實際上是引用計數(shù)值減1
筋栋,例如:如果對象的引用計數(shù)為10
,那么extra_rc
為9
正驻,如果引用計數(shù)大于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)
位運算圖解:
再打印一下二進制的內(nèi)存情況襟交,方便對照,如下:
(lldb) x/1gt per0x10065c0d0: 0b0000000100011101100000000000000100000000000000001000001011100101(lldb)