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.聯合體的好處
使代碼存儲數據高效率的同時,有較強的可讀性,可以使用共用體來增強代碼可讀性,同時使用位運算來提高
數據存取的效率