其實在分類中可以給壹個類添加屬性,但是不能添加成員變量。不要混淆成員變量和屬性的概念。
Xcode不會通過添加屬性來自動生成帶下劃線的成員變量以及類別的set和get方法。如果不手動實現這兩個方法,通過點語法調用這個屬性肯定會直接掛掉,無法識別的選擇器send to instance,因為他根本沒有這兩個方法。所以當妳真正在分類中聲明壹個屬性的時候,必須手動實現這個屬性的set和get方法。這時,妳需要使用運行時機制來關聯這個屬性的訪問過程。
那麽為什麽不在分類中加入成員變量呢?
成員變量是壹種東西,分類本身不是壹個類。分類本來就是通過運行時在OC中動態添加到壹個類中的壹些方法和屬性,並不是真正的類。如何向它添加成員變量呢?
不能將Ivar添加到分類中,因為分類本身不是壹個真正的類,它沒有自己的ISA。開始時,該類生成許多基本屬性,如IvarList和MethodList。分類只會把自己的方法附加到主類上,不會影響主類的IvarList。這就是不能將成員變量添加到分類中的原因。
為什麽不能添加成員變量?
Objective-C類由類類型表示,它實際上是壹個指向objc_class結構的指針。其定義如下:
typedef struct objc _ class * Class
objc_class結構的定義如下:
structobjc_class{Class isa?OBJC _ ISA _ AVAILABILITY#如果!__OBJC2__Class super_class?OBJC2 _不可用;//父類const char * name objc 2 _ unavailable;//類名longversion OBJC2 _ UNAVAILABLE//類的版本信息,默認為0longinfo?OBJC2 _不可用;//類信息,運行時標識longinstance_size的壹些位?OBJC2 _不可用;//該類的實例變量大小為struct objc _ ivar _ list * ivarsobjc 2 _ unavailable;//該類的成員變量鏈表struct objc _ method _ list * * methodlistsobjc 2 _ unavailable;//方法定義的鏈表struct objc _ cache * cache objc 2 _ unavailable;//方法緩存structo bjc _ protocol _ list * protocol bjc 2 _ unavailable;//協議鏈表# endif } OBJC2 _ UNAVAILABLE
在上面的objc_class結構中,ivars是壹個objc_ivar_list(成員變量列表)指針;MethodLists是指向objc_method_list指針的指針。在運行時,objc_class結構的大小是固定的,所以無法向該結構添加數據,只能修改。所以ivars指的是固定區域,只能修改成員變量值,不能增加成員變量的數量。MethodList是壹個二維數組,因此可以修改*methodLists的值來添加成員方法。雖然不能擴展methodLists所指向的內存區域,但是可以改變這個內存區域(存儲指針)的值。因此,可以動態添加方法,但不能添加成員變量。
在Objective-C提供的運行時函數中,確實有壹個class_addIvar()函數用於向類中添加成員變量,但是文檔中特別說明:
此函數只能在objc_allocateClassPair之後和objc_registerClassPair之前調用。不支持向現有類添加實例變量。
這意味著這個函數只能在“構建類的過程中”被調用。壹旦完成了類定義,就不能再添加成員變量了。編譯好的類在程序啟動後由運行庫加載,沒有機會調用addIvar。程序在運行時動態構建的類只能在調用objc_allocateClassPair之後,objc_registerClassPair之前使用,沒有機會添加成員變量。
Category不能添加實例變量,那麽可以添加屬性嗎?
我們必須從類別的結構開始:
typedefstructcategory _ t { const char * name;classref _ tcls類的名稱;//class struct method _ list _ t * instance methods;//在//category struct method _ List _ t * class methods中添加到該類的所有實例方法的列表;//category struct protocol _ list _ t * protocols中所有添加的類方法的列表;//category struct property _ list _ t * instance properties實現的所有協議的列表;category } category _ t中添加的所有屬性;
從Category的定義也可以看出,Category可以是(可以添加實例方法,類方法,甚至實現協議,添加屬性)和不可以是(不能添加實例變量)。
那為什麽我們經常聽到類別不能加屬性?其實Category其實是允許添加屬性的,妳也可以使用@property,但是它不會生成_ variables(帶下劃線的成員變量),也不會生成添加屬性的getter和setter方法。因此,雖然添加了屬性,但不可能使用點語法調用getter和setter方法。想實現自己平時屬性的功能,應該怎麽做?
事實上,我們可以使用運行時來實現它,使用運行時來實現Category,向現有的類添加新的屬性並生成getter和setter方法。不要忘記Objective-C是壹種動態語言。方法是在運行時通過objc _ get associated object/objc _ set associated object來訪問和生成相關對象. h這兩種方法可以將壹個對象與另壹個對象關聯起來,即壹個對象可以保持對另壹個對象的引用,並獲取那個對象。
//ns object+indie bandname . h @ interfaceNSObject(indie bandname)@ property(nonatomic,strong)ns string * indie bandname;@end
上面是頭文件聲明,下面是。m文件:
//ns object+indie bandname . m # import " ns object+extension . h " # importstaticconstvoid * indie bandname key = & amp;IndieBandNameKey@ implementationNSObject(indie bandname)@ dynamicindieBandName;-(ns string *)indie bandname { return objc _ getAssociatedObject(self,indie bandname key);}-(void)setIndieBandName:(ns string *)IndieBandName { OBJC _ setassociated object(self,IndieBandNameKey,indie bandname,OBJC _ ASSOCIATION _ RETAIN _ NONATOMIC);} @結束
可以通過運行時的兩種方法將實例變量添加到類別中。