本文內(nèi)容由https://blog.csdn.net/intheair100/article/details/72718625修改的結(jié)果
類別的簡(jiǎn)介
在開發(fā)中有時(shí)會(huì)用到Category蝉衣,類別有三個(gè)作用:
(1)可以將類的時(shí)間分散到多個(gè)不同文件或多個(gè)不用框架中梗摇,方便代碼管理谢翎。也可以對(duì)框架提供類的擴(kuò)展(因?yàn)榭蚣茴悰]有源碼,不能修改)。
(2)創(chuàng)建對(duì)私有方法的前向引用:如果其他類的方法未實(shí)現(xiàn)匿情,在你訪問其他類的私有方法時(shí)編譯器報(bào)錯(cuò)扭倾,這時(shí)使用類別诅需,在類別中聲明這些方法漾唉,可以不必提供方法的實(shí)現(xiàn),編譯器就不會(huì)再產(chǎn)生警告了堰塌。
(3)向?qū)ο筇砑臃钦絽f(xié)議:創(chuàng)建一個(gè)NSObject的類別稱為“創(chuàng)建一個(gè)非正式協(xié)議”赵刑,因?yàn)榭梢宰鳛槿魏晤惖奈袑?duì)象使用。
有兩個(gè)方向的局限性:
(1)無法向類中添加新的實(shí)例變量场刑,類別沒有位置容納實(shí)例變量料睛。
(2)名稱沖突,即當(dāng)類別中的方法與原始類方法名稱沖突時(shí)摇邦,類別具有更高的優(yōu)先級(jí)。類別的方法將完全取代初始方法寵兒無法再使用初始方法屎勘。這個(gè)類似于方法的重載施籍,但是這里是直接覆蓋了原方法。
Category添加屬性:
@interface NSDate (CXExtension)
@property (nonatomic,assign,readonly) NSInteger day;
@property (nonatomic,assign,readonly) NSInteger month;
@property (nonatomic,assign,readonly) NSInteger year;
@property (nonatomic,assign,readonly) NSInteger firstWeek;
@property (nonatomic,assign,readonly) NSInteger totalDays;
@property (nonatomic,assign,readonly) NSDate *? lastMonth;
@property (nonatomic,assign,readonly) NSDate *? nextMonth;@end
哎呦概漱,我擦嘞丑慎,什么鬼?怎么可以添加屬性了瓤摧?這個(gè)是為什么呢竿裂?
首先解釋一下屬性和成員變量的區(qū)別
屬性和成員變量的區(qū)別
@property(nonatomic,strong)UIButton*myButton;
我們聲明了一個(gè)屬性,我們現(xiàn)在用的編譯器已經(jīng)是LLVM了照弥,所以不再需要為屬性聲明實(shí)例變量了腻异。如果LLVM發(fā)現(xiàn)一個(gè)沒有匹配實(shí)例變量的屬性,它將為你生成以下劃線開頭的實(shí)例變量_mybutton,不需要自己手動(dòng)再去寫實(shí)例變量这揣。而且也不需要再.m文件中寫
@synthesize myButton
也會(huì)自動(dòng)生成setter悔常,getter方法影斑。@synthesize的作用就是讓編譯器為你生成setter和getter方法。那么.m文件中可以直接使用_myButton實(shí)例變量机打,也可以通過self.myButton矫户,兩者都是一樣的。當(dāng)然残邀,這里的self.myButton其實(shí)調(diào)用了myButton屬性的getter/setter方法皆辽。
假如我們?cè)贠bjective-C中
@interface TodayHomeViewController : UIViewController
{??? UIButton * myButton;?? }
.m文件中,self.myButtom 這樣的表達(dá)是錯(cuò)的芥挣,Xcode會(huì)提示你使用->,改成self->myButtom就可以了驱闷。因?yàn)镺C中的點(diǎn)表達(dá)式是表示調(diào)用方法,而上面的代碼中沒有myButton這個(gè)方法九秀。
Objective-C中的點(diǎn)語法說明:
如果點(diǎn)表達(dá)式出現(xiàn)在“=”左邊遗嗽,該屬性名稱的setter方法將被調(diào)用。如果點(diǎn)表達(dá)式出現(xiàn)在“=”右邊鼓蜒,該屬性名稱getter方法將會(huì)被調(diào)用痹换。所以在OC中點(diǎn)表達(dá)式其實(shí)就是調(diào)用對(duì)象setter和getter方法的一種快捷方式。
@synthesize 還有一個(gè)作用都弹,可以指定與屬性對(duì)應(yīng)的實(shí)力變量娇豫,例如
@synthesize myButton = xxxx;
那么self.myButton其實(shí)是操作實(shí)例變量xxxx,而不是_myButton了畅厢。
在實(shí)際開發(fā)中冯痢,我們一般在.m中這樣寫:
@synthesize?? myButton;
這樣寫了之后,那么編譯器會(huì)自動(dòng)生成myButton的實(shí)例變量框杜,以及相應(yīng)的getter和setter方法浦楣。注意:_myButton這個(gè)實(shí)例變量是不存在的,因?yàn)樽詣?dòng)生成的實(shí)例變量是myButton而不是_myButton咪辱,所以現(xiàn)在@synthesize的作用就相當(dāng)于枝頂實(shí)例變量振劳。
如果在.m中寫了
@synthesize myButton;
那么生成的實(shí)例變量就是myButton,如果沒有寫
@synthesize myButton;
那么生成的實(shí)例變量就是_myButton油狂。所以跟以前的用法還是有點(diǎn)細(xì)微的區(qū)別的历恐。
Category中屬性Property
類別中只能添加方法,不能添加實(shí)例變量专筷。我們經(jīng)橙踉簦看見類別中這樣寫:
@property(nonatomic, assign) CGFloat? x;
在這種情況下一般不會(huì)生成實(shí)例變量的,這里添加的屬性磷蛹,其實(shí)是添加的setter和getter方法吮旅。
- (void)setX:(CGFloat)x{
??? CGRect rect = self.frame;
??? rect.origin.x = x;
??? self.frame = rect;
}
- (CGFloat)x{
??? return self.frame.origin.x;
}
在Objective-C提供的runtime函數(shù)中,確實(shí)有一個(gè)class_addIvar()刪除可以給類別添加成員變量味咳,但是文檔中特別說明:
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)用addIver枪眉。程序在運(yùn)行時(shí)動(dòng)態(tài)構(gòu)建的類需要在調(diào)用objc_allocateClassPair之后,ojbc_registerClassPair之前被使用再层,同樣沒有機(jī)會(huì)再添加成員變量贸铜。那么為什么可以在類別中添加方法和屬性呢?
因?yàn)榉椒ê蛯傩员炔弧皩儆凇鳖悓?shí)例聂受,而成員變量“屬于”類實(shí)例蒿秦。我們所說的“類實(shí)例”概念,指的是一塊內(nèi)存區(qū)域蛋济,包含了isa只能和所有的成員變量棍鳖。所以假如允許動(dòng)態(tài)修改類成員變量,已經(jīng)創(chuàng)建出的的類實(shí)例就不符合類定義了碗旅,變成了無效對(duì)象渡处。但方法定義是在objc_class中管理的,不管如何增刪類方法祟辟,都不影響類實(shí)例的內(nèi)存布局医瘫,已經(jīng)創(chuàng)建出的類實(shí)例仍然可以正常使用。
在類別中添加實(shí)例變量
那我們?nèi)绻砑訉?shí)例變量該怎么辦呢旧困?這個(gè)使用就該用runtime了醇份,不要忘記Objective-C是一門動(dòng)態(tài)的語言,常見的辦法是通過runtime.h中objc_getAssociateObject / objc_setAssociateObject來訪問和生成關(guān)聯(lián)對(duì)象吼具。這兩個(gè)方法可以讓一個(gè)對(duì)象和另一個(gè)對(duì)象關(guān)聯(lián)僚纷,就是說一個(gè)對(duì)象可以保持對(duì)另一個(gè)對(duì)象的引用,并獲得那個(gè)對(duì)象拗盒。
//NSObject+IndieBandName.h
@interfaceNSObject (IndieBandName)
@property(nonatomic, strong) NSString *indieBandName;
@end
上面是頭文件聲明怖竭,接下來就是見證奇跡的時(shí)刻,實(shí)現(xiàn).m文件:
// NSObject+IndieBandName.m
#import "NSObject+Extension.h"
#import <objc/runtime.h>
static const void *IndieBandNameKey = &IndieBandNameKey;
@implementation NSObject (IndieBandName)
@dynamic indieBandName;
- (NSString *)indieBandName {
return objc_getAssociatedObject(self, IndieBandNameKey);
}
- (void)setIndieBandName:(NSString *)indieBandName {
objc_setAssociatedObject(self, IndieBandNameKey, indieBandName, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
通過runtime的兩種方法就可以為列別添加一個(gè)實(shí)例變量了锣咒。
本文是有底線的
感謝各位大牛的資料
http://www.cnblogs.com/crazypebble/p/3439261.html
https://blog.csdn.net/intheair100/article/details/72718625
http://quotation.github.io/objc/2015/05/21/objc-runtime-ivar-access.html
http://www.cnblogs.com/wupher/archive/2013/01/05/2845338.html