iOS分類動(dòng)態(tài)添加實(shí)例變量的思考

分類亦稱類別,開發(fā)中經(jīng)常用到,不過我所做的一般是擴(kuò)充方法,因?yàn)橹傲私獾椒诸愂菬o(wú)法添加成員變量的,所以沒有做過多考慮。后來(lái)看了些文章可以運(yùn)用runtime動(dòng)態(tài)地添加成員變量,于是自己也索性一試,看看有什么特別的。
下面做一個(gè)UILabel的分類:UILabel+Associate
.h文件中

#import <UIKit/UIKit.h>
@interface UILabel (Associate)
@property(nonatomic,strong)UIColor * instanceColor;
@end

.m文件

#import "UILabel+Associate.h"
#import <objc/runtime.h>
static void * instanceColorKey = &instanceColorKey;//申請(qǐng)一個(gè)固定地址
@implementation UILabel (Associate)
//第三個(gè)參數(shù)如果屬性為整型,那么寫法應(yīng)該是加上@(),第四個(gè)參數(shù)根據(jù)枚舉類型來(lái)選擇
- (void)setInstanceColor:(UIColor *)instanceColor{
    objc_setAssociatedObject(self, &instanceColorKey, instanceColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIColor*)instanceColor{
    return objc_getAssociatedObject(self, &instanceColorKey);
}

然后在一個(gè)VC里調(diào)用這個(gè)屬性并賦以顏色,運(yùn)行確實(shí)已經(jīng)著色

 UILabel * lab = [[UILabel alloc]initWithFrame:CGRectMake(100, 100, 100, 40)];
 lab.instanceColor = [UIColor greenColor];
 lab.backgroundColor = lab.instanceColor;
 self.view.backgroundColor = [UIColor whiteColor];
 [self.view addSubview:lab];

寫到這可能會(huì)發(fā)現(xiàn),這沒什么特別的感覺啊,這明明白白就是可以聲明屬性并調(diào)用了,而且還有下圖這種情況

圖1

?圖2

明明可以聲明屬性,還可以調(diào)用setter方法,那還大費(fèi)周章地用runtime顯擺什么?然而事實(shí)是這樣么

先解釋下屬性和成員變量的區(qū)別:
在老版本的Objective-C語(yǔ)言中,我們需要同時(shí)聲明屬性和底層實(shí)例變量,那時(shí),屬性是Objective-C語(yǔ)言的一個(gè)新的機(jī)制,并且要求你必須聲明與之對(duì)應(yīng)的實(shí)例變量,例如:

@interface ViewController :UIViewController{  
__strong UIButton *_Button;  
}  
@property (nonatomic, retain) UIButton * Button;  
@end 

后來(lái),蘋果將默認(rèn)編譯器從GCC轉(zhuǎn)換為L(zhǎng)LVM(low level virtual machine),從此不再需要為屬性聲明實(shí)例變量了。如果LLVM發(fā)現(xiàn)一個(gè)沒有匹配實(shí)例變量的屬性,它將自動(dòng)創(chuàng)建一個(gè)以下劃線開頭的實(shí)例變量。因此,在這個(gè)版本中,?不需要手動(dòng)寫實(shí)例變量,也不需要在.m文件中寫@synthesize button,同時(shí)會(huì)自動(dòng)為你生成setter,getter方法。

@interface ViewController :UIViewController
@property (nonatomic, strong) UIButton * button;  
@end

在.m文件中,編譯器自動(dòng)生成的一個(gè)實(shí)例變量_button,可以直接調(diào)用,與通過屬性self.button作用一樣,但self.button調(diào)用的是其getter和setter方法。如果點(diǎn)表達(dá)式出現(xiàn)在等號(hào) = 左邊,該屬性名稱的setter方法將被調(diào)用。如果點(diǎn)表達(dá)式出現(xiàn)在右邊,該屬性名稱的getter方法將被調(diào)用。
在一些項(xiàng)目我們經(jīng)常這樣寫:

@interface ViewController :UIViewController
{
    UIButton * button;
}  
@end

此處button即為實(shí)例變量,無(wú)法使用點(diǎn)語(yǔ)法。獲取方法應(yīng)該為self->button;
還有之前的一些比較老的寫法,在.m中如此聲明

@synthesize button;

@synthesize有兩種作用,其一是讓編譯器為你自動(dòng)生成setter與getter方法,其二可以指定與屬性對(duì)應(yīng)的實(shí)例變量。那么此處的實(shí)例變量就是button,如果沒寫這個(gè),那么就為_button;

那么如此一來(lái)就明白了,判斷是否生成實(shí)例變量,按照上文方式,看能否直接調(diào)用UILabel+Associate中的_instanceLabel,然而并木有,如果不添加runtime語(yǔ)句,直接調(diào)用屬性反而會(huì)直接報(bào)錯(cuò),無(wú)法運(yùn)行。

@property (nonatomic, assign) CGSize size;

我們?cè)诜诸愔锌吹降念愃普Z(yǔ)句,僅僅調(diào)用了其setter,getter方法,是無(wú)法生成實(shí)例變量的。

很多人在學(xué)到Category時(shí)都會(huì)有疑問,既然允許用Category給類增加方法和屬性,那為什么不允許增加成員變量?

在Objective-C提供的runtime函數(shù)中,確實(shí)有一個(gè)class_addIvar()函數(shù)用于給類添加成員變量,但是文檔中特別說明:

This function may only be called after objc_allocateClassPair and before objc_registerClassPair. Adding an instance variable to an existing class is not supported.

這個(gè)函數(shù)只能在“構(gòu)建一個(gè)類的過程中”調(diào)用。一旦完成類定義,就不能再添加成員變量了。經(jīng)過編譯的類在程序啟動(dòng)后就被runtime加載,沒有機(jī)會(huì)調(diào)用addIvar。程序在運(yùn)行時(shí)動(dòng)態(tài)構(gòu)建的類需要在調(diào)用objc_registerClassPair之后才可以被使用,同樣沒有機(jī)會(huì)再添加成員變量。

那為什么runtime允許動(dòng)態(tài)添加方法和屬性,而不會(huì)引發(fā)問題呢?

因?yàn)榉椒ê蛯傩圆⒉弧皩儆凇鳖悓?shí)例,而成員變量“屬于”類實(shí)例。我們所說的“類實(shí)例”概念,指的是一塊內(nèi)存區(qū)域,包含了isa指針和所有的成員變量。所以假如允許動(dòng)態(tài)修改類成員變量布局,已經(jīng)創(chuàng)建出的類實(shí)例就不符合類定義了,變成了無(wú)效對(duì)象。但方法定義是在objc_class中管理的,不管如何增刪類方法,都不影響類實(shí)例的內(nèi)存布局,已經(jīng)創(chuàng)建出的類實(shí)例仍然可正常使用。

那么由此分類的作用與局限便清晰了,以下做個(gè)總結(jié)。
作用:
1.分類可以在不獲悉,不改變?cè)瓉?lái)代碼的情況下往里面添加新的方法,但不能添加實(shí)例變量
2.如果分類和原來(lái)類中的方法產(chǎn)生名稱沖突,則分類將覆蓋原來(lái)的方法,因?yàn)榉诸惥哂懈叩膬?yōu)先級(jí)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 如何進(jìn)步?某種程度上來(lái)說,進(jìn)步需要犧牲。 這周在忙于做實(shí)驗(yàn),給小老鼠做手術(shù),之前做手術(shù)死亡率1/3,為了減少死亡率...
    郭云閱讀 336評(píng)論 0 0
  • 在某一件事情中,我們都會(huì)偏向弱者,因?yàn)橄啾容^他們屬于弱勢(shì)的一方。在潛意識(shí)里,我們都會(huì)感覺弱者都是需要支持的。 前幾...
    徐徐徐什么的閱讀 649評(píng)論 4 6
  • 無(wú)論做什么,都會(huì)有人看不慣,這一點(diǎn)我很小的時(shí)候就知道了,隨著年歲增長(zhǎng),不免發(fā)掘到一些浮躁的支撐,比如偶爾被別人贊美...
    嘰哩咕嚕AMY貓閱讀 362評(píng)論 6 3
  • 在那些想不通的日夜中 我也是摸著黑 走出來(lái) 今天主要分享一些我在抗病期間寫的一些心情 路途 我看...
    陰陽(yáng)先生閱讀 529評(píng)論 0 1

友情鏈接更多精彩內(nèi)容