iOS底層原理 - Category實(shí)現(xiàn)原理(一)

通過探索Category底層原理回答以下問題

  1. Category是否可以添加方法诵原、屬性英妓、成員變量?Category是否可以遵守Protocol绍赛?
  2. Category的本質(zhì)是什么蔓纠,在底層是怎么存儲(chǔ)的?
  3. Category的實(shí)現(xiàn)原理是什么吗蚌,Catagory中的方法是如何調(diào)用到的腿倚?
  4. Category中是否有Load方法,load方法是什么時(shí)候調(diào)用的蚯妇?
  5. load敷燎、initialize的區(qū)別

Category可以直接添加 屬性、成員變量嗎侮措?

首先我們創(chuàng)建一個(gè)類MGCPerson懈叹,為該類添加一個(gè)分類MGCPerson (Sport),并嘗試為該分類添加實(shí)例方法分扎、類方法澄成、屬性、成員變量畏吓,觀察是否報(bào)錯(cuò)
創(chuàng)建MGCPerson

// MGCPerson.h
@interface MGCPerson : NSObject

@property (nonatomic, assign) NSInteger height;
@property (nonatomic, assign) NSInteger weight;
@property (nonatomic, copy) NSString    *name;
@property (nonatomic, strong) NSDate    *brithday;

- (void)life;
+ (void)life;

@end

// MGCPerson.m
@implementation MGCPerson

- (void)life
{
    NSLog(@"MGCPerson : - (void)life");
}

+ (void)life
{
    NSLog(@"MGCPerson : + (void)life");
}

@end

添加分類MGCPerson (Sport)墨状,并嘗試為該分類添加實(shí)例方法、類方法菲饼、屬性肾砂、成員變量

Category-IVarError.png

通過操作我們發(fā)現(xiàn),可以在Category中直接添加實(shí)例方法宏悦、類方法镐确、屬性以及準(zhǔn)守協(xié)議包吝,但是當(dāng)嘗試在Category中添加成員變量時(shí)會(huì)報(bào)錯(cuò)Instance variables may not be placed in categories,即不能直接在分類中添加成員變量源葫。接下來我們繼續(xù)探討Category的本質(zhì)诗越,分析為什么不能在Category中直接添加成員變量

Category的底層數(shù)據(jù)結(jié)構(gòu)

首先我們?yōu)镸GCPerson創(chuàng)建兩個(gè)分類MGCPerson (Sport)MGCPerson (Eat)
MGCPerson (Sport)

// MGCPerson+Sport.h
@interface MGCPerson (Sport) <NSCopying, NSCoding>

@property (nonatomic, assign) NSInteger stepCount;
@property (nonatomic, copy) NSDate      *sportStartTime;
@property (nonatomic, copy) NSDate      *sportEndTime;

- (void)sport;
+ (void)sport;
- (void)run;
+ (void)run;

@end

// MGCPerson+Sport.m
@implementation MGCPerson (Sport)

- (void)sport
{
    NSLog(@"MGCPerson (Sport) : - (void)sport");
}

+ (void)sport
{
    NSLog(@"MGCPerson (Sport) : + (void)sport");
}

- (void)run
{
    NSLog(@"MGCPerson (Sport) : - (void)run");
}

+ (void)run
{
    NSLog(@"MGCPerson (Sport) : + (void)run");
}

- (void)life
{
    NSLog(@"MGCPerson (Sport) : - (void)life");
}

+ (void)life
{
    NSLog(@"MGCPerson (Sport) : + (void)life");
}

@end

MGCPerson (Eat)

// MGCPerson+Eat.h
@interface MGCPerson (Eat)

+ (void)eat;
- (void)eat;

@end

// MGCPerson+Eat.m
@implementation MGCPerson (Eat)

- (void)eat
{
    NSLog(@"MGCPerson (Eat) : - (void)eat");
}

+ (void)eat
{
    NSLog(@"MGCPerson (Eat) : + (void)eat");
}

- (void)life
{
    NSLog(@"MGCPerson (Eat) : - (void)life");
}

+ (void)life
{
    NSLog(@"MGCPerson (Eat) : + (void)life");
}

@end

我們知道OC是基于C/C++實(shí)現(xiàn)的息堂,接下來我們將通過命令xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc 文件名將分類MGCPerson (Sport)嚷狞、 MGCPerson (Eat)轉(zhuǎn)化成.cpp文件,窺探Category的底層實(shí)現(xiàn)
分析.cpp文件思路
第一步: 因?yàn)槲覀兎治龅氖?code>.m轉(zhuǎn)化稱的.cpp文件荣堰,所以我們首先嘗試在.cpp文件中查找關(guān)鍵字@implementation MGCPerson (Sport)床未,通過檢索,我們找到了對(duì)應(yīng)方法的實(shí)現(xiàn)

// @implementation MGCPerson (Sport)

static void _I_MGCPerson_Sport_sport(MGCPerson * self, SEL _cmd) {
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_gp_d5d9dd_x6kzfb5mn22zbqsg40000gn_T_MGCPerson_Sport_bea66e_mi_0);
}


static void _C_MGCPerson_Sport_sport(Class self, SEL _cmd) {
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_gp_d5d9dd_x6kzfb5mn22zbqsg40000gn_T_MGCPerson_Sport_bea66e_mi_1);
}


static void _I_MGCPerson_Sport_run(MGCPerson * self, SEL _cmd) {
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_gp_d5d9dd_x6kzfb5mn22zbqsg40000gn_T_MGCPerson_Sport_bea66e_mi_2);
}


static void _C_MGCPerson_Sport_run(Class self, SEL _cmd) {
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_gp_d5d9dd_x6kzfb5mn22zbqsg40000gn_T_MGCPerson_Sport_bea66e_mi_3);
}


static void _I_MGCPerson_Sport_life(MGCPerson * self, SEL _cmd) {
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_gp_d5d9dd_x6kzfb5mn22zbqsg40000gn_T_MGCPerson_Sport_bea66e_mi_4);
}


static void _C_MGCPerson_Sport_life(Class self, SEL _cmd) {
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_gp_d5d9dd_x6kzfb5mn22zbqsg40000gn_T_MGCPerson_Sport_bea66e_mi_5);
}

// @end

對(duì)函數(shù)名進(jìn)行簡(jiǎn)單分析振坚,得出命名規(guī)則


Category-MethodNameRule.png

第二步: 檢索函數(shù)名_I_MGCPerson_Sport_sport薇搁、_C_MGCPerson_Sport_sport進(jìn)一步分析

static struct /*_method_list_t*/ {
    unsigned int entsize;  // sizeof(struct _objc_method)
    unsigned int method_count;
    struct _objc_method method_list[3];
} _OBJC_$_CATEGORY_INSTANCE_METHODS_MGCPerson_$_Sport __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    3,
    {{(struct objc_selector *)"sport", "v16@0:8", (void *)_I_MGCPerson_Sport_sport},
    {(struct objc_selector *)"run", "v16@0:8", (void *)_I_MGCPerson_Sport_run},
    {(struct objc_selector *)"life", "v16@0:8", (void *)_I_MGCPerson_Sport_life}}
};

static struct /*_method_list_t*/ {
    unsigned int entsize;  // sizeof(struct _objc_method)
    unsigned int method_count;
    struct _objc_method method_list[3];
} _OBJC_$_CATEGORY_CLASS_METHODS_MGCPerson_$_Sport __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    3,
    {{(struct objc_selector *)"sport", "v16@0:8", (void *)_C_MGCPerson_Sport_sport},
    {(struct objc_selector *)"run", "v16@0:8", (void *)_C_MGCPerson_Sport_run},
    {(struct objc_selector *)"life", "v16@0:8", (void *)_C_MGCPerson_Sport_life}}
};

分析可知:上述代碼創(chuàng)建了_method_list_t類型的結(jié)構(gòu)體變量_OBJC_$_CATEGORY_INSTANCE_METHODS_MGCPerson_$_Sport_OBJC_$_CATEGORY_CLASS_METHODS_MGCPerson_$_Sport分別用于存儲(chǔ)實(shí)例方法列表和類方法列表。

struct _method_list_t {
    unsigned int entsize;  // sizeof(struct _objc_method) 方法所占內(nèi)存大小
    unsigned int method_count; // 方法個(gè)數(shù)
    struct _objc_method method_list[3]; // 方法列表
};
struct _objc_method {
    struct objc_selector * _cmd; // SEL屡拨,可以理解為函數(shù)名字符串
    const char *method_type; // 函數(shù)類型(包括返回值類型只酥、參數(shù)類型)
    void  *_imp; // 指向函數(shù)實(shí)現(xiàn)
};

這里簡(jiǎn)單說下_objc_method中的method_type褥实,詳細(xì)介紹后續(xù)在探究runtime消息機(jī)制時(shí)再繼續(xù)扒呀狼。method_type其實(shí)可以看做用字符縮寫來表達(dá)的函數(shù)類型字符串,比如v@:i就是返回類型為void损离,第一個(gè)參數(shù)為id類型哥艇,第二個(gè)參數(shù)為指針類型,第三個(gè)參數(shù)為int類型的函數(shù)僻澎,如- (void)addStepCount:(int)count(我們知道iOS中方法調(diào)用會(huì)默認(rèn)傳入隱式參數(shù) 方法調(diào)用者:self 和 方法名:_cmd貌踏。這也是為什么我們可以在方法內(nèi)部訪問selfcmd的原因)

第三步:繼續(xù)檢索_OBJC_$_CATEGORY_INSTANCE_METHODS_MGCPerson_$_Sport窟勃,繼續(xù)分析

static struct _category_t _OBJC_$_CATEGORY_MGCPerson_$_Sport __attribute__ ((used, section ("__DATA,__objc_const"))) = 
{
    "MGCPerson",
    0, // &OBJC_CLASS_$_MGCPerson,
    (const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_MGCPerson_$_Sport,
    (const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_MGCPerson_$_Sport,
    (const struct _protocol_list_t *)&_OBJC_CATEGORY_PROTOCOLS_$_MGCPerson_$_Sport,
    (const struct _prop_list_t *)&_OBJC_$_PROP_LIST_MGCPerson_$_Sport,
};

分析可知:上述代碼創(chuàng)建了一個(gè)_category_t類型的結(jié)構(gòu)體變量 _OBJC_$_CATEGORY_MGCPerson_$_Sport祖乳,并且傳入了類方法列表、實(shí)例方法列表秉氧、協(xié)議類別眷昆、屬性列表,具備了Category的所有信息汁咏,沒錯(cuò)Category在底層就是_category_t類型亚斋,下邊我們檢索結(jié)構(gòu)體類型_category_t,看下_category_t的定義

struct _category_t {
    const char *name; // 類名(MGCPerson)
    struct _class_t *cls;  // 類對(duì)象指針(指向MGCPerson類對(duì)象)
    const struct _method_list_t *instance_methods; // 實(shí)例方法列表
    const struct _method_list_t *class_methods; // 類方法列表
    const struct _protocol_list_t *protocols; // 協(xié)議列表
    const struct _prop_list_t *properties; // 屬性列表
};

到此我們已經(jīng)清楚的看到了Category在底層的存儲(chǔ)結(jié)構(gòu)攘滩,并且可以看到底層并沒有存儲(chǔ)成員變量帅刊,這也就是為什么直接添加成員變量會(huì)報(bào)錯(cuò)的原因。
上邊我們已經(jīng)分析了_category_t_method_list_t漂问,下邊我們?cè)倏聪?code>_protocol_list_t和_prop_list_t
先看_protocol_list_t赖瞒,同樣的在.cpp文件中檢索_protocol_list_t

static struct /*_protocol_list_t*/ {
    long protocol_count;  // Note, this is 32/64 bit 協(xié)議個(gè)數(shù)
    struct _protocol_t *super_protocols[2]; // 協(xié)議數(shù)組(2是數(shù)組長(zhǎng)度)
} _OBJC_CATEGORY_PROTOCOLS_$_MGCPerson_$_Sport __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    2,
    &_OBJC_PROTOCOL_NSCopying, // 協(xié)議變量地址
    &_OBJC_PROTOCOL_NSCoding  // 協(xié)議變量地址
};

分析可知:上述代碼定義了_protocol_list_t類型的結(jié)構(gòu)體變量_OBJC_CATEGORY_PROTOCOLS_$_MGCPerson_$_Sport用于存儲(chǔ)協(xié)議列表

struct _protocol_t _OBJC_PROTOCOL_NSCopying __attribute__ ((used)) = {
    0,
    "NSCopying",
    0,
    (const struct method_list_t *)&_OBJC_PROTOCOL_INSTANCE_METHODS_NSCopying,
    0,
    0,
    0,
    0,
    sizeof(_protocol_t),
    0,
    (const char **)&_OBJC_PROTOCOL_METHOD_TYPES_NSCopying
};

struct _protocol_t _OBJC_PROTOCOL_NSCoding __attribute__ ((used)) = {
    0,
    "NSCoding",
    0,
    (const struct method_list_t *)&_OBJC_PROTOCOL_INSTANCE_METHODS_NSCoding,
    0,
    0,
    0,
    0,
    sizeof(_protocol_t),
    0,
    (const char **)&_OBJC_PROTOCOL_METHOD_TYPES_NSCoding
};

分析可知:上述代碼定義了兩個(gè)_protocol_t類型的協(xié)議變量_OBJC_PROTOCOL_NSCopying_OBJC_PROTOCOL_NSCoding

struct _protocol_t {
    void * isa;  // NULL
    const char *protocol_name; // 協(xié)議名稱
    const struct _protocol_list_t * protocol_list; // super protocols 父協(xié)議
    const struct method_list_t *instance_methods; // 協(xié)議中實(shí)例方法列表
    const struct method_list_t *class_methods; // 協(xié)議中類方法列表
    const struct method_list_t *optionalInstanceMethods;// 協(xié)議中可選實(shí)現(xiàn)的實(shí)例方法列表
    const struct method_list_t *optionalClassMethods;// 協(xié)議中可選實(shí)現(xiàn)的類方法列表
    const struct _prop_list_t * properties; // 協(xié)議中的屬性列表
    const unsigned int size;  // sizeof(struct _protocol_t) // 協(xié)議所占內(nèi)存大小
    const unsigned int flags;  // = 0
    const char ** extendedMethodTypes;
};

再來看下_prop_list_t

static struct /*_prop_list_t*/ {
    unsigned int entsize;  // sizeof(struct _prop_t) 屬性數(shù)組所占內(nèi)存大小
    unsigned int count_of_properties; // 屬性個(gè)數(shù)
    struct _prop_t prop_list[3]; // 屬性數(shù)組
} _OBJC_$_PROP_LIST_MGCPerson_$_Sport __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_prop_t),
    3,
    {{"stepCount","Tq,N"},
    {"sportStartTime","T@\"NSDate\",C,N"}, // 屬性名女揭、 類型名、copy栏饮、nonatomic
    {"sportEndTime","T@\"NSDate\",C,N"}} // 屬性名田绑、 類型名、copy抡爹、nonatomic
};

分析可知:上述代碼創(chuàng)建了一個(gè)_prop_t類型的結(jié)構(gòu)體變量_OBJC_$_PROP_LIST_MGCPerson_$_Sport用于存放屬性列表

struct _prop_t {
    const char *name; // 屬性名
    const char *attributes; // 屬性描述掩驱,就是@property后邊的一些關(guān)鍵字
};

到此我們已經(jīng)了解了Category的底層數(shù)據(jù)結(jié)構(gòu),以及可以在Category中添方法冬竟、協(xié)議欧穴、屬性,但是不能添加成員變量泵殴,那么Category中的屬性和類中的屬性是否一樣的呢涮帘?如果不一樣,怎么實(shí)現(xiàn)和類中添加屬性一樣的效果呢笑诅?接下來我們將探討這個(gè)問題

Category中的屬性

我們已經(jīng)為MGCPerson添加了height调缨、weightname吆你、brithday屬性弦叶,為MGCPerson+Sport添加了stepCountsportStartTime妇多、sportEndTime屬性

并且我們知道在類中添加一個(gè)屬性伤哺,系統(tǒng)為我們做了三件事
@property (nonatomic, copy) NSString *name;

  • 創(chuàng)建了一個(gè)成員變量_name
  • 生成了settergetter方法的聲明
  • 生成了setter者祖、getter方法的實(shí)現(xiàn)

那么在Category添加屬性系統(tǒng)是否也會(huì)為我們做這三件事呢立莉?

  • Category添加屬性系統(tǒng)不會(huì)自動(dòng)添加成員變量
    根據(jù)上邊的學(xué)習(xí)我們知道,_category_t 中并沒有存儲(chǔ)成員變量
  • Category添加屬性系統(tǒng)會(huì)生成setter七问、getter方法的聲明
    嘗試在MGCPerson+Sport.h中直接敲對(duì)應(yīng)的setter蜓耻、getter,發(fā)現(xiàn)是有提示的械巡,說明會(huì)生成setter刹淌、getter方法的聲明
  • Category添加屬性系統(tǒng)不會(huì)生成settergetter方法的實(shí)現(xiàn)
    下邊我們比下MGCPerson.cppMGCPerson+Sport.cppp 文件
// @implementation MGCPerson

static void _I_MGCPerson_life(MGCPerson * self, SEL _cmd) {
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_gp_d5d9dd_x6kzfb5mn22zbqsg40000gn_T_MGCPerson_d3d3d6_mi_0);
}

static void _C_MGCPerson_life(Class self, SEL _cmd) {
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_gp_d5d9dd_x6kzfb5mn22zbqsg40000gn_T_MGCPerson_d3d3d6_mi_1);
}

static NSInteger _I_MGCPerson_height(MGCPerson * self, SEL _cmd) { return (*(NSInteger *)((char *)self + OBJC_IVAR_$_MGCPerson$_height)); }
static void _I_MGCPerson_setHeight_(MGCPerson * self, SEL _cmd, NSInteger height) { (*(NSInteger *)((char *)self + OBJC_IVAR_$_MGCPerson$_height)) = height; }

static NSInteger _I_MGCPerson_weight(MGCPerson * self, SEL _cmd) { return (*(NSInteger *)((char *)self + OBJC_IVAR_$_MGCPerson$_weight)); }
static void _I_MGCPerson_setWeight_(MGCPerson * self, SEL _cmd, NSInteger weight) { (*(NSInteger *)((char *)self + OBJC_IVAR_$_MGCPerson$_weight)) = weight; }

static NSString * _Nonnull _I_MGCPerson_name(MGCPerson * self, SEL _cmd) { return (*(NSString * _Nonnull *)((char *)self + OBJC_IVAR_$_MGCPerson$_name)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);

static void _I_MGCPerson_setName_(MGCPerson * self, SEL _cmd, NSString * _Nonnull name) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct MGCPerson, _name), (id)name, 0, 1); }

static NSDate * _Nonnull _I_MGCPerson_brithday(MGCPerson * self, SEL _cmd) { return (*(NSDate * _Nonnull *)((char *)self + OBJC_IVAR_$_MGCPerson$_brithday)); }
static void _I_MGCPerson_setBrithday_(MGCPerson * self, SEL _cmd, NSDate * _Nonnull brithday) { (*(NSDate * _Nonnull *)((char *)self + OBJC_IVAR_$_MGCPerson$_brithday)) = brithday; }
// @end
// @implementation MGCPerson (Sport)

static void _I_MGCPerson_Sport_sport(MGCPerson * self, SEL _cmd) {
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_gp_d5d9dd_x6kzfb5mn22zbqsg40000gn_T_MGCPerson_Sport_bea66e_mi_0);
}


static void _C_MGCPerson_Sport_sport(Class self, SEL _cmd) {
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_gp_d5d9dd_x6kzfb5mn22zbqsg40000gn_T_MGCPerson_Sport_bea66e_mi_1);
}


static void _I_MGCPerson_Sport_run(MGCPerson * self, SEL _cmd) {
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_gp_d5d9dd_x6kzfb5mn22zbqsg40000gn_T_MGCPerson_Sport_bea66e_mi_2);
}


static void _C_MGCPerson_Sport_run(Class self, SEL _cmd) {
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_gp_d5d9dd_x6kzfb5mn22zbqsg40000gn_T_MGCPerson_Sport_bea66e_mi_3);
}


static void _I_MGCPerson_Sport_life(MGCPerson * self, SEL _cmd) {
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_gp_d5d9dd_x6kzfb5mn22zbqsg40000gn_T_MGCPerson_Sport_bea66e_mi_4);
}


static void _C_MGCPerson_Sport_life(Class self, SEL _cmd) {
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_gp_d5d9dd_x6kzfb5mn22zbqsg40000gn_T_MGCPerson_Sport_bea66e_mi_5);
}

// @end

可以看到坟比,MGCPerson自動(dòng)生成了對(duì)應(yīng)屬性的setter芦鳍、getter的實(shí)現(xiàn)方法,但是MGCPerson+Sport卻沒有生成對(duì)應(yīng)屬性的setter葛账、getter的實(shí)現(xiàn)方法柠衅,綜上所述在Category添加屬性系統(tǒng)僅僅只生成了settergetter方法的聲明

如何使Category中的屬性與類中的屬性具備同樣的效果(關(guān)聯(lián)對(duì)象)

通過上邊的學(xué)習(xí)籍琳,我們知道想達(dá)到與類添加屬性一樣的效果菲宴,我們需要做兩件事

  • 添加一個(gè)存儲(chǔ)屬性值的變量
  • 實(shí)現(xiàn)setter贷祈、getter方法
    可以通過runtime中的關(guān)聯(lián)對(duì)象方法來實(shí)現(xiàn)
/ **
  *使用給定的鍵和關(guān)聯(lián)策略為給定的對(duì)象設(shè)置關(guān)聯(lián)值。
  *
  * @param object 關(guān)聯(lián)的源對(duì)象喝峦。
  * @param key 關(guān)聯(lián)的鍵势誊。
  * @param value 與對(duì)象的鍵相關(guān)聯(lián)的值。 傳遞nil清除現(xiàn)有關(guān)聯(lián)谣蠢。
  * @param policy 關(guān)聯(lián)的策略
  *
  * /

objc_setAssociatedObject(<#id  _Nonnull object#>, <#const void * _Nonnull key#>, <#id  _Nullable value#>, <#objc_AssociationPolicy policy#>)

// 關(guān)聯(lián)策略粟耻,和@property后的關(guān)鍵字對(duì)應(yīng)
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
    OBJC_ASSOCIATION_ASSIGN = 0,       
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3, 
    OBJC_ASSOCIATION_RETAIN = 01401, 
    OBJC_ASSOCIATION_COPY = 01403
};
- (void)setSportEndTime:(NSDate *)sportEndTime
{
    objc_setAssociatedObject(self, @selector(sportEndTime), sportEndTime, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSDate *)sportEndTime
{
    return objc_getAssociatedObject(self, _cmd);
}

系統(tǒng)是如何管理關(guān)聯(lián)對(duì)象的

去官網(wǎng)下載runtime源碼,搜索objc_setAssociatedObject方法眉踱,這里不做過多分析挤忙,簡(jiǎn)單說下結(jié)論,后續(xù)會(huì)單開一篇扒關(guān)聯(lián)對(duì)象的實(shí)現(xiàn)原理谈喳。
runtime用四個(gè)類來管理關(guān)聯(lián)對(duì)象册烈,AssociationsManagerAssociationsHashMap婿禽、AssociationsMap赏僧、ObjectAssociation,四個(gè)類之間的關(guān)系如下圖

管理關(guān)聯(lián)對(duì)象的類之間的關(guān)系.png

  • 對(duì)象并不是存儲(chǔ)在被關(guān)聯(lián)對(duì)象本身內(nèi)存中
  • 關(guān)聯(lián)對(duì)象存儲(chǔ)在全局的統(tǒng)一的一個(gè)AssociationsManager中
  • 設(shè)置關(guān)聯(lián)對(duì)象為nil扭倾,就相當(dāng)于是移除關(guān)聯(lián)對(duì)象

小尾巴

到此我們已經(jīng)解決了開篇中的第1淀零、2個(gè)問題,后續(xù)問題見iOS底層原理 - Category實(shí)現(xiàn)原理(二)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末吆录,一起剝皮案震驚了整個(gè)濱河市窑滞,隨后出現(xiàn)的幾起案子琼牧,更是在濱河造成了極大的恐慌恢筝,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件巨坊,死亡現(xiàn)場(chǎng)離奇詭異撬槽,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)趾撵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門侄柔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人占调,你說我怎么就攤上這事暂题。” “怎么了究珊?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵薪者,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我剿涮,道長(zhǎng)言津,這世上最難降的妖魔是什么攻人? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮悬槽,結(jié)果婚禮上怀吻,老公的妹妹穿的比我還像新娘。我一直安慰自己初婆,他們只是感情好蓬坡,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著磅叛,像睡著了一般渣窜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上宪躯,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天乔宿,我揣著相機(jī)與錄音,去河邊找鬼访雪。 笑死详瑞,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的臣缀。 我是一名探鬼主播坝橡,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼精置!你這毒婦竟也來了计寇?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤脂倦,失蹤者是張志新(化名)和其女友劉穎番宁,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體赖阻,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蝶押,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了火欧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片棋电。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖苇侵,靈堂內(nèi)的尸體忽然破棺而出赶盔,到底是詐尸還是另有隱情,我是刑警寧澤榆浓,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布于未,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏沉眶。R本人自食惡果不足惜打却,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望谎倔。 院中可真熱鬧柳击,春花似錦、人聲如沸片习。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽藕咏。三九已至状知,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間孽查,已是汗流浹背饥悴。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留盲再,地道東北人西设。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像答朋,于是被迫代替她去往敵國(guó)和親贷揽。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容