關(guān)于我的倉庫
- 這篇文章是我為面試準(zhǔn)備的iOS基礎(chǔ)知識(shí)學(xué)習(xí)中的一篇
- 我將準(zhǔn)備面試中找到的所有學(xué)習(xí)資料,寫的Demo,寫的博客都放在了這個(gè)倉庫里iOS-Engineer-Interview
- 歡迎star????
- 其中的博客在簡書,CSDN都有發(fā)布
- 博客中提到的相關(guān)的代碼Demo可以在倉庫里相應(yīng)的文件夾里找到
前言
- 本篇是解讀RunTime源碼中的第一篇文章,著重講述RunTime中的基本結(jié)構(gòu)體,了解類,對(duì)象中都有些什么東西
準(zhǔn)備工作
- 請(qǐng)準(zhǔn)備好750.1版本的objc4源碼一份【目前最新的版本】,打開它,找到文章中提到的方法,類型,對(duì)象
- 一切請(qǐng)以手中源碼為準(zhǔn),不要輕信任何人,任何文章,包括本篇博客
- 源碼建議從Apple官方開源網(wǎng)站獲取obj4
- 官網(wǎng)上下載下來需要自己配置才能編譯運(yùn)行,如果不想配置,可以在RuntimeSourceCode中clone
類與對(duì)象
對(duì)象
objc_object
- 對(duì)象是一個(gè)objc_object類型的結(jié)構(gòu)體
補(bǔ)充知識(shí):OC類的本質(zhì)
- OC語言在編譯期都會(huì)被編譯為C語言的Runtime代碼,二進(jìn)制執(zhí)行過程中執(zhí)行的都是C語言代碼。而OC的類本質(zhì)上都是結(jié)構(gòu)體,在編譯時(shí)都會(huì)以結(jié)構(gòu)體的形式被編譯到二進(jìn)制中。
找到它?
- 我們先在源碼中找到objc_object在哪,于是你打開全局搜索,找到了這么一段
#if !OBJC_TYPES_DEFINED
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
/// Represents an instance of a class.
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
/// A pointer to an instance of a class.
typedef struct objc_object *id;
#endif
- 于是你感覺里面就一個(gè)Class _Nonnull isa OBJC_ISA_AVAILABILITY;
- 然而,請(qǐng)注意上面的#if !OBJC_TYPES_DEFINED,點(diǎn)進(jìn)去會(huì)發(fā)現(xiàn)該宏是1,也就是說。。。根本不走這個(gè)?。。?/li>
- 真正的定義是在objc-private文件里【請(qǐng)著重關(guān)注這個(gè)文件,是個(gè)寶藏文件】
struct objc_object {
// isa結(jié)構(gòu)體
private:
isa_t isa; //這里講到了isa的類型是isa_t
public:
//省略方法
};
- 先不看方法,發(fā)現(xiàn)這里面只有一個(gè)isa_t類型的isa;
isa_t
- 點(diǎn)到isa_t里去【這次是唯一的,不會(huì)走錯(cuò)路了】,其定義為
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
};
- 關(guān)于isa,會(huì)在下面仔細(xì)講解
- 這里只要知道isa里存儲(chǔ)了對(duì)象所屬類的信息【Class cls】
- 我們把目光投向Class cls這里
類
objc_class
- 點(diǎn)到Class的定義里,此時(shí)你應(yīng)該學(xué)乖了,知道要找objc-private里的
- 發(fā)現(xiàn)Class的定義為typedef struct objc_class *Class;
- 再查看下objc_class的定義
struct objc_class : objc_object {
Class superclass;
cache_t cache;
class_data_bits_t bits;
//省略方法
};
- 這個(gè)時(shí)候你應(yīng)該會(huì)發(fā)現(xiàn)一個(gè)特別特別特別神奇的事,objc_class竟然繼承于objc_object,換句話說,類的本質(zhì)也是個(gè)對(duì)象
- objc_class中的的內(nèi)容【概念圖】

objc-isa-class-pointer
- objc_class里一共三個(gè)東西,第一個(gè)Class superclass顯然是我們能理解的,是該類的父類
- 好,此時(shí)我們思考一個(gè)問題,類也是一個(gè)對(duì)象,類也應(yīng)該有一個(gè)isa,可對(duì)象的isa存儲(chǔ)的是所屬類的信息,類的isa存儲(chǔ)的會(huì)是什么呢?
說明:isa指針
- 學(xué)習(xí)過程中,會(huì)發(fā)現(xiàn)很多人將isa稱之為isa指針
- 這個(gè)因?yàn)閕sa雖然是一個(gè)結(jié)構(gòu)體,但其中會(huì)存儲(chǔ)所屬類的地址【類也是有地址的,不在堆也不在棧,類似于單例】
- 因此很多文章會(huì)說isa指針指向xxx,但請(qǐng)理解,isa不是一個(gè)指針,后面會(huì)講它是如何獲取地址的
- 為方便講述,下面也開始使用isa指向xxx這種說法
引入概念:元類與根類
- 元類【meta class】:類的所屬類,類的isa會(huì)指向其元類
- 根類【root class】:根類,一般情況下就是指NSObject
isa閉環(huán)
- 首先來看一張閉環(huán)圖

isa閉環(huán)指針圖
- 圖里把繼承關(guān)系和isa指向關(guān)系講的很清楚,這里請(qǐng)注意兩點(diǎn)
- Root class根類,它是繼承關(guān)系的頂點(diǎn),它不繼承與任何類
- Root meta class根元類,它是isa指向的頂點(diǎn),其isa直接指向自己
補(bǔ)充知識(shí):類方法與實(shí)例方法的存儲(chǔ)
- 假設(shè)我們每生成一個(gè)對(duì)象就要開辟空間存儲(chǔ)他的所有方法,顯然是對(duì)空間的浪費(fèi)
- 因此使用對(duì)象的實(shí)例方法都是存放在所屬類當(dāng)中
- 但是類方法怎么辦呢,因此有了元類,將類方法存儲(chǔ)在元類當(dāng)中,讓兩者的存儲(chǔ)統(tǒng)一起來,使得無論是類還是對(duì)象都能通過相同的機(jī)制查找方法的實(shí)現(xiàn)。
- 類被定義為
objc_class結(jié)構(gòu)體,objc_class結(jié)構(gòu)體繼承自objc_object,所以類也是對(duì)象。在應(yīng)用程序中,類對(duì)象只會(huì)被創(chuàng)建一份。在objc_class結(jié)構(gòu)體中定義了對(duì)象的method list、protocol、ivar list等,表示對(duì)象的行為。既然類是對(duì)象,那類對(duì)象也是其他類的實(shí)例。所以Runtime中設(shè)計(jì)出了meta class,通過meta class來創(chuàng)建類對(duì)象,所以類對(duì)象的isa指向?qū)?yīng)的meta class。而meta class也是一個(gè)對(duì)象,所有元類的isa都指向其根元類,根原類的isa指針指向自己。通過這種設(shè)計(jì),isa的整體結(jié)構(gòu)形成了一個(gè)閉環(huán)。
isa詳解
- 下面來詳細(xì)講解isa即isa_t結(jié)構(gòu)體
完整定義
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
};
uintptr_t bits【unsigned long bits】
- bits中存儲(chǔ)了所屬類地址等信息
union isa_t
- 請(qǐng)注意,雖然前面將isa_t稱之為結(jié)構(gòu)體,但注意定義里將其定位的是union【共用體】
- 也就是說,里面的isa_t、cls、 bits 還有結(jié)構(gòu)體共用同一塊地址空間。而 isa 總共會(huì)占據(jù) 64 位的內(nèi)存空間(決定于其中的結(jié)構(gòu)體)
- 請(qǐng)牢記這一點(diǎn),不然后面會(huì)有困惑
結(jié)構(gòu)分析
- 在isa_t內(nèi)部定義了一個(gè)結(jié)構(gòu)體,里面只有一個(gè)宏ISA_BITFIELD
- 查看里面的內(nèi)容
struct {
ISA_BITFIELD; // defined in isa.h
};
//ISA_BITFIELD內(nèi)容以及注解
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
# define ISA_MAGIC_MASK 0x001f800000000001ULL
# define ISA_MAGIC_VALUE 0x001d800000000001ULL
# define ISA_BITFIELD
uintptr_t nonpointer : 1; //說明是不是指針【這是一個(gè)對(duì)象還是一個(gè)類,下文會(huì)繼續(xù)介紹】
uintptr_t has_assoc : 1; // 是否曾經(jīng)或正在被關(guān)聯(lián)引用,如果沒有,可以快速釋放內(nèi)存
uintptr_t has_cxx_dtor : 1; // 這個(gè)字段存儲(chǔ)類是否有c++析構(gòu)器
uintptr_t shiftcls : 44; //存儲(chǔ)cls類地址 /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/
uintptr_t magic : 6; //校驗(yàn)
uintptr_t weakly_referenced : 1; // 對(duì)象是否曾經(jīng)或正在被弱引用,如果沒有,可以快速釋放內(nèi)存
uintptr_t deallocating : 1; // 對(duì)象是否正在釋放內(nèi)存
uintptr_t has_sidetable_rc : 1; // 是否需要散列表存儲(chǔ)引用計(jì)數(shù)。當(dāng)extra_rc存儲(chǔ)不下時(shí),該值為1
uintptr_t extra_rc : 8 // 引用計(jì)數(shù)數(shù)量,實(shí)際的引用計(jì)數(shù)減一
# define RC_ONE (1ULL<<56)
# define RC_HALF (1ULL<<7)
補(bǔ)充知識(shí):arm 64與x86_64
- 這里都是處理器型號(hào)
- iOS5之后,CPU數(shù)據(jù)吞吐量為64bit(64個(gè)二進(jìn)制位,表示8個(gè)字節(jié)),相較于32位處理器效率提升了一倍,此時(shí)對(duì)應(yīng)寄存器也變成了64位,可以處理更大的數(shù)據(jù)顯示更多的狀態(tài)。
- i386是針對(duì)intel通用微處理器32位處理器,x86_64是針對(duì)x86架構(gòu)的64位處理器
- 模擬器32位處理器測(cè)試需要i386架構(gòu),模擬器64位處理器測(cè)試需要x86_64架構(gòu),真機(jī)32位處理器需要armv7,或者armv7s架構(gòu),真機(jī)64位處理器需要arm64架構(gòu)。
- 這里就以x86_64為例
各個(gè)部分存儲(chǔ)空間
- 就是ISA_BITFIELD這里的內(nèi)容存儲(chǔ)了該類的信息

123俄
- 下面我們看一下具體的存儲(chǔ)地址

5566
isa_t的初始化
initIsa
inline void
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
assert(!isTaggedPointer());
if (!nonpointer) {
isa.cls = cls;
} else {
assert(!DisableNonpointerIsa);
assert(!cls->instancesRequireRawIsa());
isa_t newisa(0);
#if SUPPORT_INDEXED_ISA
assert(cls->classArrayIndex() > 0);
newisa.bits = ISA_INDEX_MAGIC_VALUE;
newisa.has_cxx_dtor = hasCxxDtor;
newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
newisa.bits = ISA_MAGIC_VALUE; //0x001d800000000001ULL
newisa.has_cxx_dtor = hasCxxDtor;
newisa.shiftcls = (uintptr_t)cls >> 3;
#endif
isa = newisa;
}
}
- 看到這么大的一段代碼先不要慌,先從三個(gè)參數(shù)開始
補(bǔ)充知識(shí):assert()函數(shù)
- 其作用是如果它的條件返回錯(cuò)誤,則終止程序執(zhí)行。
- assert的作用是先計(jì)算表達(dá)式 expression ,如果其值為假(即為0),那么它先向stderr打印一條出錯(cuò)信息,然后通過調(diào)用 abort 來終止程序運(yùn)行。
三個(gè)參數(shù)【Class cls】【bool nonpointer】【bool hasCxxDtor】
- cls就是其存儲(chǔ)的類
- nonpointer這個(gè)我們看initInstanceIsa
inline void
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
assert(!cls->instancesRequireRawIsa());
assert(hasCxxDtor == cls->hasCxxDtor());
initIsa(cls, true, hasCxxDtor);
}
- 對(duì)于實(shí)例對(duì)象,傳入的nonpointer是true【翻譯一下就是確實(shí)不是一個(gè)指針,有其他內(nèi)容】
- 我們?cè)诜祷厣厦娴膇nitIsa
inline void
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
assert(!isTaggedPointer());
if (!nonpointer) {
isa.cls = cls;
} else {
//省略代碼
}
}
- 也就是說如果傳入的是false【不是不是指針,是指針】也就是說是在直接訪問類,那么就直接返回cls內(nèi)容就行
- 回憶下上面說的isa_t是一個(gè)共用體,給cls賦值,也就意味著在結(jié)構(gòu)體里面填完了內(nèi)容
補(bǔ)充知識(shí):ULL
- ULL:unsiged long long 64bit
- 在16進(jìn)制數(shù)據(jù)后面加上數(shù)據(jù)類型限制,因?yàn)槟J(rèn)的數(shù)據(jù)類型是int,加數(shù)據(jù)類型是為了防止數(shù)據(jù)越界。32和64位下long long int總是64位的, long int總是32位的。
真正初始化
- 真正初始化的代碼就只有幾行
isa_t newisa(0);
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;
isa = newisa;
- 這個(gè)newisa就是初始化出來的結(jié)果
- 我們要好好理解的是newisa.bits = ISA_MAGIC_VALUE;
- 首先ISA_MAGIC_VALUE的值為0x001d800000000001ULL,將它換成二進(jìn)制數(shù)11101100000000000000000000000000000000000000000000001【記住前面的高低位】,輸入到bits中【再強(qiáng)調(diào)一遍isa_t是一個(gè)共用體,輸入到bits里就等于到了結(jié)構(gòu)體里】
- 結(jié)果如下圖【將indexed改成nonpointer】

000
- 因此初始化的時(shí)候只設(shè)置了nonpointer,magic兩個(gè)部分
-
magic的值為0x3b用于調(diào)試器判斷當(dāng)前對(duì)象是真的對(duì)象還是沒有初始化的空間 - 在設(shè)置
indexed和magic值之后,會(huì)設(shè)置isa的has_cxx_dtor,這一位表示當(dāng)前對(duì)象有 C++ 或者 ObjC 的析構(gòu)器(destructor),如果沒有析構(gòu)器就會(huì)快速釋放內(nèi)存。

小心心
- newisa.shiftcls = (uintptr_t)cls >> 3這句是為了將 Class 指針中無用的后三位清除減小內(nèi)存的消耗,因?yàn)轭惖闹羔樢凑兆止?jié)(8 bits)對(duì)齊內(nèi)存,其指針后三位都是沒有意義的 0。這里首先一個(gè)OC的指針長度是47,而這里的shiftcls占44個(gè)字節(jié),本來
isa諸多用處
獲取cls地址:ISA() 方法
- 由于現(xiàn)在isa不在只存放地址了,還多了很多附加內(nèi)容,因此需要一個(gè)專門的方法獲取shiftcls中的內(nèi)容
#define ISA_MASK 0x00007ffffffffff8ULL
inline Class
objc_object::ISA()
{
return (Class)(isa.bits & ISA_MASK);
}
- 進(jìn)行&操作獲取地址
class方法
- 進(jìn)入源碼以后,可以查看很多內(nèi)容的源碼
+ (Class)class {
return self;
}
- (Class)class {
return object_getClass(self);
}
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
- class既是類方法又是實(shí)例方法,類方法直接返回自身,實(shí)例方法返回的就是isa中的內(nèi)容
isMemberOfClass&&isKindOfClass
+ (BOOL)isMemberOfClass:(Class)cls {
return object_getClass((id)self) == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
- 這也沒啥好解釋的了,結(jié)合class的內(nèi)容應(yīng)該很好理解了
objc_class擴(kuò)充:bits
- 回顧下objc_class的內(nèi)容
struct objc_class : objc_object {
Class superclass;
cache_t cache;
class_data_bits_t bits;
//省略方法
};
- 其中bits是絕對(duì)的主角,里面存儲(chǔ)了類的方法列表等等的信息
- 看下class_data_bits_t里的內(nèi)容,發(fā)現(xiàn)就一個(gè)uintptr_t bits
- 看到這很容易聯(lián)想到和前面isa里的bits,事實(shí)上,兩者確實(shí)很想,這也是我把它放在后面來講的原因之一
class_rw_t與class_ro_t
- bits中最重要的部分就是class_rw_t,class_ro_t兩兄弟
- 在哪里找到他們呢?返回objc_class的定義,發(fā)現(xiàn)其中有一個(gè)data函數(shù)
class_rw_t *data() {
return bits.data();
}
//data()
class_rw_t* data() {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
// class_rw_t定義
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint32_t version;
const class_ro_t *ro;
// 方法信息
method_array_t methods;
// 屬性信息
property_array_t properties;
// 協(xié)議信息
protocol_array_t protocols;
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
//省略方法
};

讓我
- 就如同注釋里說明的,其中有方法,屬性,協(xié)議等等信息【后面在信息通訊等內(nèi)容里我們會(huì)在仔細(xì)研究這一塊】
- 但這里有一個(gè)我們不太懂的const class_ro_t是個(gè)啥?
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
const uint8_t * ivarLayout;
const char * name;
// 方法列表
method_list_t * baseMethodList;
// 協(xié)議列表
protocol_list_t * baseProtocols;
// 變量列表
const ivar_list_t * ivars;
const uint8_t * weakIvarLayout;
// 屬性列表
property_list_t *baseProperties;
method_list_t *baseMethods() const {
return baseMethodList;
}
};

肉
可以看到兩者內(nèi)容基本一樣,就是class_ro_t里的協(xié)議,方法等前面多了個(gè)base
這里先注意中class_rw_t定義的class_ro_t是帶const修飾符的,是不可變的
下面我們通過class的初始化來研究下這兩個(gè)都是這么運(yùn)作的
realizeClass方法
//精取代碼
static Class realizeClass(Class cls)
{
const class_ro_t *ro;
class_rw_t *rw;
Class supercls;
Class metacls;
bool isMeta;
ro = (const class_ro_t *)cls->data(); //編譯期間,cls->data指向的是class_ro_t結(jié)構(gòu)體。cls->data()存放的是即ro中存儲(chǔ)的是編譯時(shí)就已經(jīng)決定的原數(shù)據(jù)
rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1); //初始化rw
rw->ro = ro; //rw中的ro賦值
rw->flags = RW_REALIZED|RW_REALIZING;
cls->setData(rw);
rw->version = isMeta ? 7 : 0;
cls->chooseClassArrayIndex();
methodizeClass(cls); //運(yùn)行期間,將ro中的內(nèi)容賦值給rw
return cls;
}
補(bǔ)充知識(shí):addRootClass和addSubclass
static void addSubclass(Class supercls, Class subcls)
{
runtimeLock.assertLocked();
if (supercls && subcls) {
assert(supercls->isRealized());
assert(subcls->isRealized());
subcls->data()->nextSiblingClass = supercls->data()->firstSubclass;
supercls->data()->firstSubclass = subcls;
if (supercls->hasCxxCtor()) {
subcls->setHasCxxCtor();
}
if (supercls->hasCxxDtor()) {
subcls->setHasCxxDtor();
}
if (supercls->hasCustomRR()) {
subcls->setHasCustomRR(true);
}
if (supercls->hasCustomAWZ()) {
subcls->setHasCustomAWZ(true);
}
// Special case: instancesRequireRawIsa does not propagate
// from root class to root metaclass
if (supercls->instancesRequireRawIsa() && supercls->superclass) {
subcls->setInstancesRequireRawIsa(true);
}
}
}
static void addRootClass(Class cls)
{
runtimeLock.assertLocked();
assert(cls->isRealized());
cls->data()->nextSiblingClass = _firstRealizedClass;
_firstRealizedClass = cls;
}
- 這兩個(gè)方法的職責(zé)是將某個(gè)類的子類串成一個(gè)列表,大致是superClass.firstSubclass -> subClass1.nextSiblingClass -> subClass2.nextSiblingClass -> ...
- 而存儲(chǔ)這些的同樣是data,也就是說、以通過class_rw_t,獲取到當(dāng)前類的所有子類
methodizeClass方法
//精取代碼
// 設(shè)置類的方法列表、協(xié)議列表、屬性列表,包括category的方法
static void methodizeClass(Class cls)
{
runtimeLock.assertLocked();
bool isMeta = cls->isMetaClass();
auto rw = cls->data();
auto ro = rw->ro;
method_list_t *list = ro->baseMethods();
if (list) {
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
//添加方法
rw->methods.attachLists(&list, 1);
}
property_list_t *proplist = ro->baseProperties;
if (proplist) {
//添加屬性
rw->properties.attachLists(&proplist, 1);
}
protocol_list_t *protolist = ro->baseProtocols;
if (protolist) {
//添加協(xié)議
rw->protocols.attachLists(&protolist, 1);
}
//添加協(xié)議【略】
}
- 也就是說是在methodizeClass中,將rw的內(nèi)容填充完
流程總結(jié)
- 編寫代碼運(yùn)行后,開始類的方法,成員變量 屬性 協(xié)議等信息都存放在 const class_ro_t中,運(yùn)行過程中,會(huì)將信息整合,動(dòng)態(tài)創(chuàng)建 class_rw_t,然后會(huì)將 class_ro_t中的內(nèi)容(類的原始信息:方法 屬性 成員變量 協(xié)議信息) 和 分類的方法 屬性 協(xié)議 成員變量的信息 存儲(chǔ)到 class_rw_t中.并通過數(shù)組進(jìn)行排序,分類方法放在數(shù)組的前端,原類信息放在數(shù)組的后端.運(yùn)行初始 objc-class中的 bits是指向 class_ro_t的,bits中的data取值是從class_ro_t中獲得,而后創(chuàng)建 class_rw_t,class_rw_t中的 class_ro_t從初始的 class_ro_t中取值,class_rw_t初始化完成后,修改 objc_class中的bits指針指向class_rw_t
- 所以ro中存儲(chǔ)的是編譯時(shí)就已經(jīng)決定的原數(shù)據(jù),rw才是運(yùn)行時(shí)動(dòng)態(tài)修改的數(shù)據(jù)。

rw與ro
(class_rw_t *)(bits & FAST_DATA_MASK)
- 這句和isa里的非常像,想必應(yīng)該看了很有感覺了
- FAST_DATA_MASK0x00007ffffffffff8
- 這句取出bits中47-3的位置,就是class_rw_t
2019.7.26更新
對(duì)于class_rw_t 以及class_ro_t進(jìn)一步理解
- 這里其實(shí)從名字上就可以理解這兩個(gè)結(jié)構(gòu)體,rw是指readwrite【可讀寫】,ro是指readonly【只讀】,因此ro是在編譯階段決定的,無法修改,rw是在運(yùn)行時(shí)可以進(jìn)行添加的
- 這里可以先補(bǔ)充下方法的添加流程,我們知道類的擴(kuò)充可以用extension,而extension中的東西也是添加在ro中,在編譯階段決定的;而category是在運(yùn)行期決定的,添加在rw中
- 這也解釋上面途中,為什么ro中的methods只要一個(gè)一維數(shù)組就行【因?yàn)槔锩嬷灰4姹绢惖姆椒?,協(xié)議,屬性同理】,rw中就得是個(gè)二維數(shù)組,里面存放著本類以及category中添加的內(nèi)容,具體是怎么操作的到后面分析category中再說
關(guān)于cache_t
- cache_t中存儲(chǔ)著方法的緩存,通過一個(gè)散列表來存儲(chǔ),方便查找【這里不要怕這個(gè)散列,后面很多內(nèi)容都是散列的】

cache
- 這里要注意,我們把sel【函數(shù)名】來進(jìn)行hash,查找
- 同時(shí)存放時(shí)又把sel,與imp都存放了
方法調(diào)用流程
CHPerson *person = [[CHPerson alloc] init];
[person test]
- 首先通過isa找到person對(duì)應(yīng)的CHPerson類,先查看其中的cache_t里的緩存方法
- 如果找不到該test方法,就去查看bits中的rw的methods查找方法【如果找到了,調(diào)用,且添加到cache_t中】
- 如果在CHPerson里找不到,會(huì)查看CHPerson的父類,同樣是先cache_t,后bits,如果查找到了,是存放在自己的cache里,不是父類的【注意!】
- 最后一直查看父類會(huì)到達(dá)NSObject根類,如果依然找不到就拋出異常
2019.8.3更新【ivar】
- 想不到這篇文章越寫越長,也不想重新開博客了,就越寫越多
- 這部分主要涉及了property和ivar以及Non Fragile ivars
property和ivar
- 在剛學(xué)習(xí)OC的時(shí)候我們就了解過了屬性與實(shí)例變量的區(qū)別
- property = ivar + get + set
- 下面我們探討兩者真正的關(guān)系
ivar
- 在class_ro_t結(jié)構(gòu)體中,有const ivar_list_t * ivars;
- 這里存儲(chǔ)著所有ivar【實(shí)例變量】
//ivar_t
struct ivar_t {
int32_t *offset;
const char *name;
const char *type;
uint32_t alignment_raw;
uint32_t size;
};
- 當(dāng)我們編譯的時(shí)候會(huì)決定ro結(jié)構(gòu)體,也就是會(huì)將變量確定下來
property
- 在ro,rw中都會(huì)有屬性列表,查看property_t結(jié)構(gòu)體
struct property_t {
const char *name;
const char *attributes;
};
- 這里我們可以看到存儲(chǔ)一個(gè)屬性的時(shí)候,我們只存儲(chǔ)了其name和屬性關(guān)鍵字,其余信息都不知道
兩者關(guān)系
- 在編譯時(shí),對(duì)于我們的屬性,其實(shí)會(huì)做兩次添加,假設(shè)我們寫了一個(gè)nameLabel屬性,第一次是在屬性列表里添加,內(nèi)容為nameLabel,之后會(huì)在ivars中同步添加,加入名為_nameLabel的變量
- 因此在真正訪問的時(shí)候,其實(shí)真正訪問到的還是變量
- 與此同時(shí),會(huì)把屬性自動(dòng)生成的set,get方法寫入方法列表中
- 這就是為什么我們剛學(xué)OC的時(shí)候有這么一句話:property = ivar + get + set
Non Fragile ivars
- Non Fragile ivars是Apple在OC2.0時(shí)推出的新特性
- 什么意思呢?我們現(xiàn)在有一個(gè)類A,在A里面的ivars中有兩個(gè)ivar【a, b】,現(xiàn)在我們寫了一個(gè)類B繼承于A,在B中我們寫了兩個(gè)ivar【c,d】
- 我們假設(shè)A是頂點(diǎn),不繼承于任何類了
- B里面會(huì)只有兩個(gè)ivar嗎,只要有些許OC基礎(chǔ)知識(shí)的人都會(huì)知道,不會(huì),ab兩個(gè)ivar也會(huì)跟著在列表里
- 下面是圖解:

img
遇到問題了
- 看起來非常合理,但其實(shí)存在一個(gè)問題
- 我們知道內(nèi)存的分配是在編譯器決定的,假如現(xiàn)在我們?cè)贏里面又加了兩個(gè)ivar【e,f】,在沒有重新分配內(nèi)存的情況下就會(huì)出現(xiàn)B中我們自己添加的cd兩個(gè)ivar正好疊在了父類的ef兩個(gè)新的ivar上,當(dāng)我們根據(jù)內(nèi)存地址去訪問的時(shí)候就會(huì)出現(xiàn)錯(cuò)誤

img
- 當(dāng)然,這時(shí)候你肯定會(huì)說fnndp,修改完A后進(jìn)行編譯運(yùn)行肯定會(huì)重新分配內(nèi)存,那會(huì)出現(xiàn)這么愚蠢的情況?
- 可問題是,OC作為一門動(dòng)態(tài)語言,對(duì)于靜態(tài)庫和蘋果的官方庫只是會(huì)進(jìn)行加載
- 在這個(gè)問題沒有解決的時(shí)代,只要靜態(tài)庫更新下,所有使用該靜態(tài)庫的app都得下架重新編譯
- 于是我們的新特性Non Fragile ivars就閃亮登場(chǎng)了
動(dòng)態(tài)偏移
-
runtime在加載MyObject類的時(shí)候(注:runtime加載類是在main函數(shù)跑起來之前),會(huì)計(jì)算基類的大小.runtime在運(yùn)行期判斷子類的instanceStart大小和父類instanceSize大小(關(guān)于這兩個(gè)成員請(qǐng)看文章開頭展示的結(jié)構(gòu)體內(nèi)容),如果子類的instanceStart小于父類的instanceSize,說明父類新增了成員變量,子類的成員變量需要進(jìn)行偏移. - 在上圖的例子中,當(dāng)
MyObject的instanceStart小于NSObject的instanceSize,MyObject在編譯器確定的結(jié)構(gòu)體將會(huì)動(dòng)態(tài)調(diào)整成員變量偏移,因此程序無需重新編譯,就能在新版本的系統(tǒng)上運(yùn)行.

img