Property是屬性y,Ivar是成員變量
其實(shí)分類(lèi)中是可以為一個(gè)類(lèi)添加屬性的并巍,但是一定做不到添加成員變量目木,不要混淆了成員變量和屬性的概念
在一個(gè)分類(lèi)中添加了一個(gè)屬性,Xcode不會(huì)自動(dòng)的為其生成一個(gè)下劃線開(kāi)頭的成員變量及set和get方法懊渡,如果你沒(méi)有手動(dòng)的實(shí)現(xiàn)這兩個(gè)方法刽射,直接在外面通過(guò)點(diǎn)語(yǔ)法調(diào)用這個(gè)屬性,肯定就直接掛了剃执,Unrecognised selector send to instance誓禁,因?yàn)樗麎焊蜎](méi)有這兩個(gè)方法,所以當(dāng)你真的在分類(lèi)中聲明了一個(gè)屬性的時(shí)候肾档,就要手動(dòng)的去實(shí)現(xiàn)這個(gè)屬性的set和get方法摹恰,這個(gè)時(shí)候就要用到運(yùn)行時(shí)機(jī)制了辫继,關(guān)聯(lián)上去這個(gè)屬性的存取過(guò)程。
那么為什么不能給分類(lèi)添加成員變量呢
成員變量是一個(gè)類(lèi)的東西俗慈,分類(lèi)本身就不是一個(gè)類(lèi)姑宽,分類(lèi)本來(lái)就是OC里面通過(guò)運(yùn)行時(shí)動(dòng)態(tài)的為一個(gè)類(lèi)添加的一些方法和屬性等,不是一個(gè)真正的類(lèi)闺阱,你怎么給他添加成員變量呢炮车?
分類(lèi)里面不能添加Ivar是因?yàn)榉诸?lèi)本身并不是一個(gè)真正的類(lèi),它并沒(méi)有自己的ISA酣溃。類(lèi)最開(kāi)始生成了很多基本屬性瘦穆,比如IvarList,MethodList赊豌,分類(lèi)只會(huì)將自己的method attach到主類(lèi)扛或,并不會(huì)影響到主類(lèi)的IvarList。這就是為什么分類(lèi)里面不能增加成員變量的原因亿絮。
為什么不能添加成員變量呢告喊?
Objective-C類(lèi)是由Class類(lèi)型來(lái)表示的麸拄,它實(shí)際上是一個(gè)指向objc_class結(jié)構(gòu)體的指針派昧。它的定義如下:
typedef struct objc_class *Class;
objc_class結(jié)構(gòu)體的定義如下:
structobjc_class{Class isa? OBJC_ISA_AVAILABILITY;#if!__OBJC2__Class super_class? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;// 父類(lèi)constchar*name? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;// 類(lèi)名longversion? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;// 類(lèi)的版本信息,默認(rèn)為0longinfo? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;// 類(lèi)信息拢切,供運(yùn)行期使用的一些位標(biāo)識(shí)longinstance_size? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;// 該類(lèi)的實(shí)例變量大小structobjc_ivar_list*ivarsOBJC2_UNAVAILABLE;// 該類(lèi)的成員變量鏈表structobjc_method_list**methodListsOBJC2_UNAVAILABLE;// 方法定義的鏈表structobjc_cache*cacheOBJC2_UNAVAILABLE;// 方法緩存structobjc_protocol_list*protocolsOBJC2_UNAVAILABLE;// 協(xié)議鏈表#endif} OBJC2_UNAVAILABLE;
在上面的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)添加方法绑雄,不能添加成員變量。
在Objective-C提供的runtime函數(shù)中奥邮,確實(shí)有一個(gè)class_addIvar()函數(shù)用于給類(lèi)添加成員變量万牺,但是文檔中特別說(shuō)明:
This function may only be called after objc_allocateClassPair and before objc_registerClassPair. Adding an instance variable to an existing class is not supported.
意思是說(shuō)罗珍,這個(gè)函數(shù)只能在“構(gòu)建一個(gè)類(lèi)的過(guò)程中”調(diào)用。一旦完成類(lèi)定義杏愤,就不能再添加成員變量了靡砌。經(jīng)過(guò)編譯的類(lèi)在程序啟動(dòng)后就runtime加載,沒(méi)有機(jī)會(huì)調(diào)用addIvar珊楼。程序在運(yùn)行時(shí)動(dòng)態(tài)構(gòu)建的類(lèi)需要在調(diào)用objc_allocateClassPair之后通殃,objc_registerClassPair之前才可以被使用,同樣沒(méi)有機(jī)會(huì)再添加成員變量厕宗。
Category不能添加成員變量(instance variables)画舌,那到底能不能添加屬性(property)呢?
這個(gè)我們要從Category的結(jié)構(gòu)體開(kāi)始分析:
typedefstructcategory_t{constchar*name;//類(lèi)的名字classref_tcls;//類(lèi)structmethod_list_t*instanceMethods;//category中所有給類(lèi)添加的實(shí)例方法的列表structmethod_list_t*classMethods;//category中所有添加的類(lèi)方法的列表structprotocol_list_t*protocols;//category實(shí)現(xiàn)的所有協(xié)議的列表structproperty_list_t*instanceProperties;//category中添加的所有屬性}category_t;
從Category的定義也可以看出Category的可為(可以添加實(shí)例方法已慢,類(lèi)方法曲聂,甚至可以實(shí)現(xiàn)協(xié)議,添加屬性)和不可為(無(wú)法添加實(shí)例變量)佑惠。
那我們?yōu)槭裁唇?jīng)常聽(tīng)說(shuō)說(shuō)類(lèi)別不能添加屬性呢朋腋?實(shí)際上,Category實(shí)際上允許添加屬性的膜楷,同樣可以使用@property旭咽,但是不會(huì)生成_變量(帶下劃線的成員變量),也不會(huì)生成添加屬性的getter和setter方法赌厅,所以穷绵,盡管添加了屬性,也無(wú)法使用點(diǎn)語(yǔ)法調(diào)用getter和setter方法特愿。那我們想要實(shí)現(xiàn)我們平時(shí)的屬性所具有的功能應(yīng)該怎么樣做呢仲墨?
其實(shí)我們可以使用runtime去做,使用runtime去實(shí)現(xiàn)Category為已有的類(lèi)添加新的屬性并生成getter和setter方法揍障。不要忘記了Objective-C是動(dòng)態(tài)語(yǔ)言目养。方法是通過(guò)runtime.h中objc_getAssociatedObject / objc_setAssociatedObject來(lái)訪問(wèn)和生成關(guān)聯(lián)對(duì)象。這兩個(gè)方法可以讓一個(gè)對(duì)象和另一個(gè)對(duì)象關(guān)聯(lián)毒嫡,就是說(shuō)一個(gè)對(duì)象可以保持對(duì)另一個(gè)對(duì)象的引用癌蚁,并獲取那個(gè)對(duì)象。
//NSObject+IndieBandName.h@interfaceNSObject(IndieBandName)@property(nonatomic,strong)NSString*indieBandName;@end
上面是頭文件聲明审胚,下面的實(shí)現(xiàn)的.m文件:
// NSObject+IndieBandName.m #import"NSObject+Extension.h"#importstaticconstvoid*IndieBandNameKey = &IndieBandNameKey;@implementationNSObject(IndieBandName)@dynamicindieBandName;- (NSString*)indieBandName {returnobjc_getAssociatedObject(self, IndieBandNameKey);}- (void)setIndieBandName:(NSString*)indieBandName { objc_setAssociatedObject(self, IndieBandNameKey, indieBandName, OBJC_ASSOCIATION_RETAIN_NONATOMIC);}@end
通過(guò)runtime的兩種方法就可以為類(lèi)別添加一個(gè)實(shí)例變量了匈勋。
形式的轉(zhuǎn)載都請(qǐng)聯(lián)系作者獲得授權(quán)并注明出處。