Category的實現(xiàn)原理是什么纤控?
Category編譯之后,底層結(jié)構(gòu)是 struct_category_t 船万,里面存儲著分類的方法刻撒、屬性、協(xié)議信息耿导,在程序運行的時候声怔,runtime會將Category的數(shù)據(jù)合并到類信息中(類對象、元類對象中)
Category的加載處理過程:
1.通過runtime加載某個類的所有Category數(shù)據(jù)
2.把所有Category的方法舱呻、屬性醋火、協(xié)議數(shù)據(jù)合并到一個大數(shù)組中。(后編譯的Category數(shù)據(jù)會放到數(shù)組的前面)
3.將合并后的分類數(shù)據(jù)(方法箱吕、屬性芥驳、協(xié)議),插入到類原來的數(shù)據(jù)前面
Category與Class Extension的區(qū)別是什么茬高?
- Category是在運行時才會將數(shù)據(jù)合并到類信息中兆旬, Extension是在編譯的時候,他的數(shù)據(jù)就包含在類信息中
- Category可以為系統(tǒng)類添加分類怎栽,Extension不能
- Category是有聲明和實現(xiàn)丽猬,Extension直接寫在宿主.m文件宿饱,只有聲明
- 如果Category聲明了聲明了一個屬性,那么Category只會生成這個屬性的set,get方法的聲明,也就不是會實現(xiàn)
Category中有l(wèi)oad方法嗎?load方法是什么時候調(diào)用的宝鼓?load方法能繼承嗎刑棵?
有l(wèi)oad方法。
load方法會在runtime加載類愚铡、分類的時候調(diào)用
每個類蛉签、分類的+load,在程序運行過程中只調(diào)用一次
調(diào)用順序 :
1.先調(diào)用類的+load方法
- 按照編譯先后順序調(diào)用(先編譯沥寥,先調(diào)用)
- 調(diào)用子類的+load之前會先調(diào)用父類的+load
2.在調(diào)用分類的+load
- 按照編譯先后順序調(diào)用(先編譯碍舍,先調(diào)用)
load方法是可以繼承的,但是一般情況下不會主動調(diào)用load方法邑雅,都是讓系統(tǒng)自動調(diào)用片橡。
注意:+load方法是根據(jù)地址直接調(diào)用,不是經(jīng)過objc_msgSend函數(shù)調(diào)用
load淮野、initialize方法的區(qū)別捧书,它們在Category中調(diào)用的順序,以及出現(xiàn)繼承時骤星,它們之間的調(diào)用過程经瓷?
load、initialize方法的區(qū)別是:
1.調(diào)用方式區(qū)別:
+load是根據(jù)函數(shù)地址直接調(diào)用
+initialize是通過objc_msgSend進行調(diào)用
2.調(diào)用時刻:
+load方法會在runtime加載類洞难、分類的時候調(diào)用(只會調(diào)用一次)
+initialize方法會在類第一次接收到消息時調(diào)用舆吮,每一個類只會initialize一次,如果子類沒有實現(xiàn)+initialize队贱,會調(diào)用父類的+initialize(所以父類的+initialize可能會被調(diào)用多次)
3.調(diào)用順序:
1)+load:
先調(diào)用類的load
- 按照編譯先后順序調(diào)用(先編譯色冀,先調(diào)用)
- 調(diào)用子類的+load之前,會先調(diào)用父類的+load
2)+initialize” - 先初始化父類
- 在初始化子類(可能最終調(diào)用的是父類的initialize方法)
調(diào)用順序:
先調(diào)用父類的initialize柱嫌,在調(diào)用子類的initialize
(先初始化父類锋恬,再初始化子類,每個類只會初始化一次)
+initialize方法會在類第一次接收到消息時調(diào)用编丘。
+initialize是通過objc_msgSend進行調(diào)用的与学,所以有以下特點:
- 如果子類沒有實現(xiàn)+initialize,會調(diào)用父類的+initialize(所以父類的+initialize可能會被調(diào)用多次)
- 如果分類實現(xiàn)了+initialize瘪吏,就覆蓋類本身的+initialize調(diào)用
Category能否添加成員變量,如果能蜗巧,如何給Category添加成員變量掌眠?
不能直接給category添加成員變量,但是可以間接實現(xiàn)category有成員變量的效果幕屹。
方法一:給分類添加全局字典
@implementation Person (Test)
NSMutableDictionary *weights_;
+(void)load {
weights_ = [NSMutableDictionary dictionary];
}
- (void)setWeight:(int)weight {
NSString *key = [NSString stringWithFormat:@"%p",self];
weights_[key] = @(weight);
}
- (int)weight {
NSString *key = [NSString stringWithFormat:@"%p",self];
return [weights_[key] intValue];
}
@end
缺點:
1.全局字典一直存放在內(nèi)存中不會釋放蓝丙,存在內(nèi)存泄漏問題级遭。
2.每一個person對象的set方法都會同時訪問這個字典,存在線程安全問題渺尘。
3.每次添加一個屬性都需要添加一個字典挫鸽,比較麻煩。
方法二:關(guān)聯(lián)對象
#import "Person+Test.h"
#import <objc/runtime.h>
@implementation Person (Test)
// static const void *nameKey = &nameKey;
//可以使用char類型只占一個字節(jié) 指針類型需要占8個字節(jié)
static const char NameKey;
-(void)setName:(NSString *)name {
// 關(guān)聯(lián)策略
// objc_AssociationPolicy 對應(yīng)的修飾符
// OBJC_ASSOCIATION_ASSIGN assign
// OBJC_ASSOCIATION_RETAIN_NONATOMIC strong, nonatomic
// OBJC_ASSOCIATION_COPY_NONATOMIC copy, nonatomic
// OBJC_ASSOCIATION_RETAIN strong, atomic
// OBJC_ASSOCIATION_COPY copy, atomic
//設(shè)置關(guān)聯(lián)對象
objc_setAssociatedObject(self, &NameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name {
//獲得關(guān)聯(lián)對象
return objc_getAssociatedObject(self, &NameKey);
}
關(guān)聯(lián)對象的實現(xiàn)原理:
關(guān)聯(lián)對象并不是存儲在被關(guān)聯(lián)對象本身內(nèi)存中
關(guān)聯(lián)對象存儲在全局的統(tǒng)一的一個AssociationManager中
如果關(guān)聯(lián)對象為nil鸥跟,就相當于移除關(guān)聯(lián)對象