內(nèi)存對(duì)齊
1、數(shù)據(jù)成員對(duì)?規(guī)則:結(jié)構(gòu)(struct)(或聯(lián)合(union))的數(shù)據(jù)成員,第一個(gè)數(shù)據(jù)成員放在offset為0的地方,以后每個(gè)數(shù)據(jù)成員存儲(chǔ)的起始位置要從該成員大小或者成員的子成員大小(只要該成員有子成員,比如說是數(shù)組,結(jié)構(gòu)體等)的整數(shù)倍開始(比如int為4字節(jié),則要從4的整數(shù)倍地址開始存儲(chǔ))。
2、結(jié)構(gòu)體作為成員:如果一個(gè)結(jié)構(gòu)里有某些結(jié)構(gòu)體成員,則結(jié)構(gòu)體成員要從其內(nèi)部最大元素大小的整數(shù)倍地址開始存儲(chǔ).(struct a里存有struct b,b里有char,int ,double等元素,那b應(yīng)該從8的整數(shù)倍開始存儲(chǔ).)
3、收尾工作:結(jié)構(gòu)體的總大小,也就是sizeof的結(jié)果,.必須是其內(nèi)部最大成員(基本數(shù)據(jù)類型)的整數(shù)倍.不足的要補(bǔ)?。
class_getInstanceSize(Class cls)方法, 查看一個(gè)對(duì)象中, 所有成員變量占用的內(nèi)存大小, (內(nèi)存對(duì)齊后的)
malloc_size(const void *ptr)方法, 方法查看操作系統(tǒng)實(shí)際分配的內(nèi)存空間. 且一定是16的整數(shù)倍.
Type Encodings
v24@0:8@16的含義也就是:
- v:返回值void
- 24:參數(shù)一共占用24字節(jié).
- @: 對(duì)象類型參數(shù)self
- 0:上面參數(shù)從0位置開始
- :: SEL
- 8:SEL從8位置開始
- @:對(duì)象類型,實(shí)際傳入的第一個(gè)參數(shù)
- 16:從16位置開始
superClass 和 isa

- instance調(diào)用對(duì)象方法的軌跡
- isa找到class, 方法不存在, 就通過superclass找父類
- class調(diào)用類方法的軌跡
- isa找meta-class, 方法不存在, 就通過superclass找父類
@interface NSObject (Test)
+ (void)test;
@end
@implementation NSObject (Test)
- (void)test {
NSLog(@"- [NSObject test] - %p", self);
}
@end
[Person test];
源碼
typedef struct objc_class *Class;
typedef struct objc_object *id;
typedef struct objc_selector *SEL;
typedef struct objc_method *Method;
typedef struct objc_ivar *Ivar;
typedef id (*IMP)(id, SEL, ...);
struct objc_object {
private:
isa_t isa;
}
union isa_t {
Class cls;
uintptr_t bits;
struct {
ISA_BITFIELD;
};
};
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; 表示是否對(duì) isa 指針開啟指針優(yōu)化 0:純isa指針,1:不止是類對(duì)象地址**,isa** 中包含了類信息、對(duì)象的引用計(jì)數(shù)等
uintptr_t has_assoc : 1; 關(guān)聯(lián)對(duì)象標(biāo)志位,0沒有,1存在
uintptr_t has_cxx_dtor : 1;該對(duì)象是否有 C++ 或者 Objc 的析構(gòu)器**,如果有析構(gòu)函數(shù),則需要做析構(gòu)邏輯,** 如果沒有**,**則可以更快的釋放對(duì)象
uintptr_t shiftcls : 33;存儲(chǔ)類指針的值。開啟指針優(yōu)化的情況下,在 arm64 架構(gòu)中有 33 位用來存儲(chǔ)類指針。
uintptr_t magic : 6; 用于調(diào)試器判斷當(dāng)前對(duì)象是真的對(duì)象還是沒有初始化的空間
uintptr_t weakly_referenced : 1;對(duì)象是否被指向或者曾經(jīng)指向一個(gè) ARC 的弱變量,沒有弱引用的對(duì)象可以更快釋放
uintptr_t deallocating : 1; 標(biāo)志對(duì)象是否正在釋放內(nèi)存
uintptr_t has_sidetable_rc : 1; :當(dāng)對(duì)象引用技術(shù)大于 10 時(shí),則需要借用該變量存儲(chǔ)進(jìn)位
uintptr_t extra_rc : 19 當(dāng)表示該對(duì)象的引用計(jì)數(shù)值,實(shí)際上是引用計(jì)數(shù)值減 1, 例如,如果對(duì)象的引用計(jì)數(shù)為 10,那么 extra_rc 為 9。如果引用計(jì)數(shù)大于 10, 則需要使用到下面的 has_sidetable_rc。
struct objc_class : objc_object {
Class superclass;
cache_t cache;
class_data_bits_t bits;
class_rw_t *data() {
return bits.data();
}
}
struct class_data_bits_t {
class_rw_t* data() const {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
const class_ro_t *safe_ro() const {
class_rw_t *maybe_rw = data();
if (maybe_rw->flags & RW_REALIZED) {
// maybe_rw is rw
return maybe_rw->ro();
} else {
// maybe_rw is actually ro
return (class_ro_t *)maybe_rw;
}
}
}
struct class_rw_t {
const method_array_t methods() const {}
const property_array_t properties() const {}
const protocol_array_t protocols() const {}
}
struct class_ro_t {
void *baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars;
const uint8_t * weakIvarLayout;
property_list_t *baseProperties
}
template <typename Element, typename List, template<typename> class Ptr>
class list_array_tt {
union {
Ptr<List> list; // - list.ptr 中存儲(chǔ)著 Element類型的數(shù)據(jù)
uintptr_t arrayAndFlag;
};
};
template <typename Element, typename List, uint32_t FlagMask, typename PointerModifier = PointerModifierNop>
struct entsize_list_tt {
Element& get(uint32_t i) const { }
}
class method_array_t : public list_array_tt <method_t, method_list_t, method_list_t_authed_ptr>
class property_array_t : public list_array_tt<property_t, property_list_t, RawPtr>
class protocol_array_t : public list_array_tt<protocol_ref_t, protocol_list_t, RawPtr>
struct method_list_t : entsize_list_tt<method_t, method_list_t, 0xffff0003, method_t::pointer_modifier> {}
struct property_list_t : entsize_list_tt<property_t, property_list_t, 0> {};
struct ivar_list_t : entsize_list_tt<ivar_t, ivar_list_t, 0> {}
struct property_t {
const char *name;
const char *attributes;
};
struct ivar_t {
int32_t *offset;
const char *name;
const char *type;
uint32_t alignment_raw;
uint32_t size;
}
struct method_t {
struct big {
SEL name;
const char *types;
MethodListIMP imp;
}
}
假設(shè)類對(duì)象的地址是 p
1. 那么 p + 0x20(isa 8 字節(jié), superclass 8 字節(jié), cache : 16字節(jié), 共 32 = 0x20 字節(jié)) == bits地址;
2. bits->data () 拿到 class_rw_t* rw .
1. 屬性
1. (*rw).properties() 返回 protocol_array_t protoclsArray;
2. protoclsArray.list 返回 RawPtr<property_list_t> rowPtr;
3. rowPtr.ptr 返回 property_list_t* list;
4. (*list).get(0) 返回property_t類型
property_t : (name = "name", attributes = "T@\"NSString\",C,N,V_name")
2. 實(shí)例方法
1. (*rw).methods() 返回 method_array_t methodsArray;
2. methodsArray.list 返回 method_list_t_authed_ptr<method_list_t>) authedPtr;
3. authedPtr.ptr 返回 method_list_t* list;
4. list.get(0) 返回method_t類型
5. method_t.big() 查看結(jié)構(gòu)體
method_t::big: { name = "sayNB", types = 0x0000000100003f71 "v16@0:8", imp = 0x0000000100003b50 (KCObjcBuild`-[JSPerson sayNB])}
3. bits-> safe_ro () 拿到 class_ro_t* ro .
1. 成員變量
1. (*ro).ivars 返回 ivar_list_t *ivarList;
2. (*ivarList).get(0) 返回 ivar_t
ivar_t : {offset = 0x00000001000084d8, name = 0x0000000100003f18 "nickName", type = 0x0000000100003f79 "@\"NSString\"", alignment_raw = 3, size = 8}
4. 元類對(duì)象地址 *p;那么元類對(duì)象的地址就是 *p + 0x20 = bits.
1. bits->data () 拿到 class_rw_t* rw .
1. 類方法
1. (*rw).methods() 返回 method_array_t methodsArray;
2. methodsArray.list 返回 method_list_t_authed_ptr<method_list_t>) authedPtr;
3. authedPtr.ptr 返回 method_list_t* list;
4. list.get(0) 返回method_t類型
5. method_t.big() 查看結(jié)構(gòu)體
method_t::big: { name = "sayNB", types = 0x0000000100003f71 "v16@0:8", imp = 0x0000000100003b50 (KCObjcBuild`-[JSPerson sayNB])}
class_rw_t
- 在運(yùn)行時(shí)生成, 它會(huì)先將class_ro_t的內(nèi)容拷貝過去,然后再將當(dāng)前類的分類的這些屬性、方法等拷貝到其中。所以可以說class_rw_t是class_ro_t的超集,當(dāng)然實(shí)際訪問類的方法、屬性等也都是訪問的class_rw_t中的內(nèi)容。
- 存儲(chǔ)屬性、方法、協(xié)議等信息
- 可讀可寫
class_ro_t
- 存儲(chǔ)了當(dāng)前類在編譯期就已經(jīng)確定的屬性、方法和遵循的協(xié)議,里面沒有category的方法
- 存儲(chǔ)了成員變量、baseMethodList、baseProtocols、baseProperties等信息
- 只讀
總結(jié)
- 實(shí)例方法, 成員變量存儲(chǔ)在類中.
- 類方法, 存儲(chǔ)在元類中.
category
struct category_t {
const char *name;
classref_t cls;
WrappedPtr<method_list_t, PtrauthStrip> instanceMethods;
WrappedPtr<method_list_t, PtrauthStrip> classMethods;
struct protocol_list_t *protocols;
struct property_list_t *instanceProperties;
struct property_list_t *_classProperties;
};
- 可以添加實(shí)例方法,類方法,甚至可以實(shí)現(xiàn)協(xié)議,添加屬性
- 無法添加實(shí)例變量
將分類中的方法/屬性/合并到類對(duì)象中
- 調(diào)用 attachCategories
1.獲取所有的分類列表的count- 創(chuàng)建三個(gè)數(shù)組,
method_list_t **mlists; property_list_t **proplists; protocol_list_t **protolists; - 使用count, 遍歷到每個(gè)分類, 將分類中的方法, 屬性, 協(xié)議添加到三個(gè)數(shù)組中.
- 創(chuàng)建三個(gè)數(shù)組,
- 調(diào)用 attachLists
- 將原本 rw的 rw->methods, rw->properties, rw->protocols 后移 count 個(gè)字節(jié).
- 將mlists, proplists, protolists 中的方法, 屬性, 協(xié)議添加到rw->methods, rw->properties, ->protocols 前.
結(jié)論
所以, 如果分類實(shí)現(xiàn)了主類的方法, 會(huì)調(diào)用分類的方法, 而不調(diào)用主類的方法, 因?yàn)榉诸惖姆椒ㄔ谇斑? 方法查找的時(shí)候, 先找到了分類的方法就直接執(zhí)行了, 不會(huì)再繼續(xù)查找了. 而如果多個(gè)分類同時(shí)實(shí)現(xiàn)了同名方法, 就會(huì)查找到后編譯的分類的方法.
將 父類, 父類分類, 子類, 子類分類 同時(shí)實(shí)現(xiàn)了+load方法
- 先調(diào)用類的+load方法,然后調(diào)用子類的+load方法, 然后調(diào)用父類分類和子類分類的+load方法;(分類的+load方法, 按照先后順序, 先編譯的先調(diào)用, 后編譯的后調(diào)用);
- 如果子類沒有實(shí)現(xiàn)load, 但是子類的分類實(shí)現(xiàn)了load方法, 那么就會(huì)先調(diào)用父類的load方法, 然后按照子類的分類和父類的分類的編譯順序, 調(diào)用load方法.
- 子類和子類的分類不實(shí)現(xiàn)load方法, 然后用子類主動(dòng)調(diào)用load方法 [Son load], 首先會(huì)走流程2, 即 父類,的load方法, 父類的分類(先編譯)的load方法, 父類的分類(后編譯)的load方法, 然后走msg_send()流程調(diào)用 父類的分類(后編譯)的load方法
將 父類, 父類分類, 子 類, 子類分類同時(shí)實(shí)現(xiàn)了+initialize方法
+initialize調(diào)用時(shí)機(jī) : 當(dāng)一個(gè)類在查找方法的時(shí)候, 會(huì)先判斷當(dāng)前類是否初始化, 如果沒有初始化就會(huì)去掉用initialize方法, 如果這個(gè)類的父類沒有初始化, 就會(huì)先調(diào)用父類的initialize方法, 再調(diào)用自己的initialize方法,類在調(diào)用initialize時(shí), 使用的是objc_msgSend消息機(jī)制調(diào)用.
alloc -> class_getInstanceMethod -> lookUpImpOrForward -> _class_initialize(不斷向父類遞歸) -> callInitialize -> objc_msgSend(cls, SEL_initialize);
- 如果父類調(diào)用 +initialize, 如果父類的+initialize方法同時(shí)被主類和分類實(shí)現(xiàn), 那么就只會(huì)調(diào)用父類分類的+initialize ,如果多個(gè)父類分類同時(shí)實(shí)現(xiàn), 那么調(diào)用后編譯的分類的 +initialize的實(shí)現(xiàn);
- 如果子類調(diào)用 +initialize
- 子類/子類分類實(shí)現(xiàn)了 +initialize
- 調(diào)用父類 +initialize, 如果父類的+initialize方法同時(shí)被主類和分類實(shí)現(xiàn), 那么就只會(huì)調(diào)用父類分類的+initialize ,如果多個(gè)父類分類同時(shí)實(shí)現(xiàn), 那么調(diào)用后編譯的分類的 +initialize的實(shí)現(xiàn);
- 用子類 +initialize, 如果子類的+initialize方法同時(shí)被主類和分類實(shí)現(xiàn), 那么就只會(huì)調(diào)用子類分類的+initialize ,如果多個(gè)子類分類同時(shí)實(shí)現(xiàn), 那么調(diào)用后編譯的分類的 +initialize的實(shí)現(xiàn);
- 類/子類分類都沒有實(shí)現(xiàn)了 +initialize
第一次是因?yàn)楦割悰]有初始化, 所以會(huì)調(diào)用父類的+initialize , 第二次調(diào)用父類的+initialize是因?yàn)檎{(diào)用子類的+initialize方法, 但是子類沒有實(shí)現(xiàn), 所以調(diào)用了父類的 +initialize;- 就會(huì)調(diào)用兩次父類的 +initialize, 如果父類的+initialize方法同時(shí)被主類和分類實(shí)現(xiàn), 那么就會(huì)調(diào)用兩次父類分類的+initialize ,如果多個(gè)父類分類同時(shí)實(shí)現(xiàn), 那么調(diào)用兩次后編譯的分類的 +initialize的實(shí)現(xiàn);
- 子類/子類分類實(shí)現(xiàn)了 +initialize
extension
- 聲明私有屬性
- 聲明私有方法
- 聲明私有成員變量
- 編譯時(shí)決議(在編譯的時(shí)候就將擴(kuò)展的所有數(shù)據(jù)都合并到類中去了)
- 只能聲明, 不能實(shí)現(xiàn)
- 不能為系統(tǒng)類添加擴(kuò)展.
關(guān)聯(lián)屬性

- _object_set_associative_reference 用來添加關(guān)聯(lián)對(duì)象
- objc_destructInstance 中調(diào)用 _object_remove_assocations 移除關(guān)聯(lián)對(duì)象.