一曹锨、分類(Category)
想必大家都知道我們可以在分類中為某個類擴展方法舵盈,并且通過分類添加的方法的優(yōu)先級還高于原始類中的方法,即如果分類中和原始類中有相同的方法驮宴,那我們調(diào)用時是優(yōu)先調(diào)用通過分類添加的方法逮刨。
既然通過分類能給類添加方法,那能不能給類添加屬性呢堵泽?屬性中綁定了一個成員變量修己,那分類又能不能給類添加成員變量呢?今天元宵節(jié)迎罗,閑著沒事睬愤,就此調(diào)研了一番。
論點
(1) 分類可以給類添加屬性
(2) 分類不可以給一個類擴展成員變量的`
先嘮嘮嗑
強調(diào)一下纹安,分類不是類程癌,它只是一個類似匿名擴展(下面會討論擴展)的模塊赢笨,用于擴展給類添加方法泵肄,便于協(xié)作楚堤、分模塊的開發(fā),你看看蘋果提供的很多API都是這樣塔粒,蘋果會寫很多分類來給某個類添加不同的功能结借,一來結(jié)構(gòu)清晰,二來也便于協(xié)同開發(fā)卒茬,可以每個人負(fù)責(zé)一個分類船老。
我們都知道咖熟,OC的世界里,任何集成自NSObject的對象都有isa指針努隙,他本質(zhì)是一個結(jié)構(gòu)體指針,但是對于Category來說辜昵,他是沒有isa指針的荸镊,好了,下面進行論證一番堪置。
論據(jù)
先說說屬性躬存,屬性是對成員變量的一個封裝,當(dāng)我們聲明一個屬性的時候舀锨,Xcode會給我們默認(rèn)創(chuàng)建一個 _屬性名 命名的成員變量岭洲,也會給我們自動創(chuàng)建getter和setter方法。當(dāng)然我們也可以用@synthesize指定其關(guān)聯(lián)的變量
【例如】給屬性name指定其關(guān)聯(lián)的變量@synthesize name = xxx坎匿;
執(zhí)行self.name的時候盾剩,其實是操作的是實例變量xxx,而不是_name了替蔬。
窩草告私,扯遠(yuǎn)了,回歸正傳承桥。驻粟。
所以我們要添加一個屬性得有三樣?xùn)|西,setter凶异、getter以及關(guān)聯(lián)的實例變量蜀撑。
在分類中Xcode不會為我們自動創(chuàng)建setter、getter方法剩彬,但是我們可以手動實現(xiàn)酷麦,但是如何把一個變量關(guān)聯(lián)到屬性上呢,直接聲明一個全局變量不行嗎喉恋,事實證明不太行贴铜,對于getter方法還好說,直接返回一個變量就行瀑晒,可是setter方法卻不行绍坝,因為你要找到該屬性關(guān)聯(lián)的變量你才能給人家賦值啊,怎么辦苔悦?怎么辦轩褐??經(jīng)過調(diào)研發(fā)現(xiàn)這只能用runtime的對象關(guān)聯(lián)來實現(xiàn)了
.m文件中
// 定義關(guān)聯(lián)的key
static const char *key = "name";
@implementation NSObject (Property)
- (NSString *)name {
// 根據(jù)關(guān)聯(lián)的key玖详,獲取關(guān)聯(lián)的值把介。
return objc_getAssociatedObject(self, key);
}
- (void)setName:(NSString *)name {
// 參數(shù)一:目標(biāo)對象
// 參數(shù)二:關(guān)聯(lián)的key勤讽,可以通過這個key獲取
// 參數(shù)三:關(guān)聯(lián)的value
// 參數(shù)四:關(guān)聯(lián)的策略
objc_setAssociatedObject(self, key, name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
看到這里你也許就會說,這不是把變量給添加上去了嗎拗踢?我表示默默一笑脚牍,如果你在原始類中打印IVarList你就會發(fā)現(xiàn)并沒有這個變量,只能打印出添加的屬性巢墅。
其實對象關(guān)聯(lián)只是把屬性關(guān)聯(lián)上去了诸狭,并沒有把變量添加進去,說了這么多這回你該信了吧君纫。
原因就是分類不是類驯遇,他沒有isa指針,下面是isa指針的代碼蓄髓,可以看出他本質(zhì)上是一個objc_class結(jié)構(gòu)體叉庐,通過isa指針才能找指向裝有變量的ivars容器,也就是說你都不知道裝有變量的容器ivars会喝,你怎么去給它生猴子啊陡叠,但是奇怪了,ISA指針里沒有指向?qū)傩詳?shù)組的指針肢执,沒有是對的匾竿,要不然這一段的解釋就廢了,可是講真蔚万,屬性指針在哪傲胙?反璃?昵慌?有知道的小伙伴請告訴我一下 >_< !!!
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE;
const char *name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;
struct objc_cache *cache OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */
結(jié)論
分類中可以給一個對象添加屬性,但是不能添加實例變量淮蜈,只能通過運行時關(guān)聯(lián)上去斋攀。
注意:如果不信,你可以在原始類中打印一遍吧梧田,會發(fā)現(xiàn)只能打印出添加的屬性淳蔼,打印不出變量。
二裁眯、擴展(Extension)
在分類中鹉梨,我們可以添加屬性,聲明方法穿稳,但是這些都是私有的存皂,因為你無法把擴展給導(dǎo)出去啊,這就是和category的一個區(qū)別逢艘。