因?yàn)镃ategory在iOS平時(shí)的開(kāi)發(fā)中用的比較多大审,所以本文從各個(gè)方面介紹一下Category芥牌,很多地方都是自己的理解贿肩,歡迎各位看到的同學(xué)批評(píng)指正峦椰,多多交流。
一汰规、Category作用
- 為已存在的類(lèi)添加方法汤功;對(duì)類(lèi)進(jìn)行了很好的擴(kuò)展,應(yīng)對(duì)變化的需求溜哮。
- 可以把一個(gè)類(lèi)的實(shí)現(xiàn)分散到多個(gè)文件中滔金,使得每個(gè)文件不至于龐大,而且可以聚集同一邏輯的代碼在一個(gè)文件中茂嗓。同時(shí)餐茵,也可以按需加載方法。
- 同一個(gè)類(lèi)可以由多個(gè)人共同完成述吸。
二忿族、為什么category不能添加屬性或者說(shuō)是成員變量
這個(gè)問(wèn)題從runtime的角度進(jìn)行分析。
下圖是objc_class結(jié)構(gòu)體:
不詳細(xì)介紹每一個(gè)字段的含義了蝌矛,主要介紹與category相關(guān)的部分:
在上面的objc_class結(jié)構(gòu)體中道批,ivars是objc_ivar_list(成員變量列表)指針;methodLists是指向objc_method_list指針的指針入撒。在Runtime中暂幼,objc_class結(jié)構(gòu)體大小是固定的,不可能往這個(gè)結(jié)構(gòu)體中添加數(shù)據(jù)蘸鲸,只能修改榨婆。所以ivars指向的是一個(gè)固定區(qū)域,只能修改成員變量值氮唯,不能增加成員變量個(gè)數(shù)鉴吹。methodList是是一個(gè)二維數(shù)組,所以可以修改*methodLists的值來(lái)增加成員方法惩琉,雖沒(méi)辦法擴(kuò)展methodLists指向的內(nèi)存區(qū)域豆励,卻可以改變這個(gè)內(nèi)存區(qū)域的值(存儲(chǔ)的是指針)。因此瞒渠,可以動(dòng)態(tài)添加方法良蒸,不能添加成員變量。類(lèi)似的是protocol伍玖,因?yàn)樗蔷幾g器處理的嫩痰,所以可以添加變量。category是在運(yùn)行時(shí)處理的窍箍。
三串纺、怎么樣可以實(shí)現(xiàn)添加屬性或者說(shuō)添加成員變量
上面說(shuō)了category中不可能添加成員變量或?qū)傩岳雎谩R驗(yàn)橄到y(tǒng)無(wú)法合成實(shí)現(xiàn)屬性所需的實(shí)例變量,所以category中也無(wú)法添加@property纺棺。但有時(shí)候確實(shí)需要添加屬性榄笙,那有沒(méi)有其他辦法,可以不改變對(duì)象的內(nèi)存結(jié)構(gòu)祷蝌,變相的給對(duì)象增加成員變量茅撞。
我們可以Runtime的objc_getAssociatedObject和objc_setAssociatedObject方法來(lái)模擬屬性的get和set方法,用關(guān)聯(lián)對(duì)象來(lái)模擬實(shí)例變量巨朦,這樣就有了@property的三要素米丘,只是跟@property的實(shí)現(xiàn)機(jī)制是完全不一樣的。
有兩種方式罪郊,第一種代碼更簡(jiǎn)潔:
.h文件:
@property (nonatomic) NSString Id;
.m文件蠕蚜,要#import <objc/runtime.h>
- (NSString *)Id
{
return objc_getAssociatedObject(self, @selector(Id));
}
- (void)setId:(NSString *)Id
{
objc_setAssociatedObject(self, @selector(Id), Id, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
第二種:
static void *UIViewPoint =@"UIViewPoint";
@implementation UIView (Point)
@dynamic pointView;
- (void)setPointView:(UIView *)pointView {
objc_setAssociatedObject(self, UIViewPoint, pointView,OBJC_ASSOCIATION_RETAIN);
}
- (UIView *)pointView {
return objc_getAssociatedObject(self, UIViewPoint);
}
這兩個(gè)方法的原型如下:
id objc_getAssociatedObject(id object, const void *key);
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
@selector(Id) 是參數(shù)中的 key,方法二中使用了靜態(tài)指針 static void * 類(lèi)型的參數(shù)來(lái)代替悔橄,第一種方法因?yàn)槭÷粤寺暶鲄?shù)的代碼靶累,并且能很好地保證 key 的唯一性,所以更簡(jiǎn)潔癣疟。
OBJC_ASSOCIATION_RETAIN_NONATOMIC 從字面意思就能猜到它是和屬性的修飾符是對(duì)應(yīng)的挣柬。對(duì)應(yīng)關(guān)系見(jiàn)如下的定義:
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
OBJC_ASSOCIATION_ASSIGN = 0, /**< Specifies a weak reference to the associated object. */
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object.
* The association is not made atomically. */
OBJC_ASSOCIATION_COPY_NONATOMIC = 3, /**< Specifies that the associated object is copied.
* The association is not made atomically. */
OBJC_ASSOCIATION_RETAIN = 01401, /**< Specifies a strong reference to the associated object.
* The association is made atomically. */
OBJC_ASSOCIATION_COPY = 01403 /**< Specifies that the associated object is copied.
* The association is made atomically. */
};
當(dāng)屬性是基本數(shù)據(jù)類(lèi)型的時(shí)候,可能只是需要添加get和set方法睛挚,不需要實(shí)例變量的時(shí)候邪蛔,可以在category中這樣添加屬性,這種方式扎狱,同樣沒(méi)有改變對(duì)象的內(nèi)存結(jié)構(gòu)侧到。
@property (nonatomic) CGFloat left;
@property (nonatomic) CGFloat right;
- (CGFloat)left {
return self.frame.origin.x;
}
- (void)setLeft:(CGFloat)x {
CGRect frame = self.frame;
frame.origin.x = x;
self.frame = frame;
}
- (CGFloat)right {
return self.left + self.width;
}
- (void)setRight:(CGFloat)right {
if(right == self.right){
return;
}
CGRect frame = self.frame;
frame.origin.x = right - frame.size.width;
self.frame = frame;
}
我查了很多資料,很多人認(rèn)為category可以添加屬性淤击,但是不可以添加成員變量匠抗,我覺(jué)得這種說(shuō)法是不嚴(yán)謹(jǐn)?shù)模詫?xiě)了這篇博客污抬,來(lái)總結(jié)一下汞贸。如果有理解的不對(duì)的地方,希望看到的朋友指正印机,大家多多交流矢腻。