1 #import “UIViewController.h”
2 @interface UIViewController(CustomView)
3 -(void)extMethod;
4 @end
1 #import “UIViewController+CustomView.h”
2 @implementation UIViewController(CustomView)
3 -(void)extMethod;
4 @end
objc_setAssociatedObject
objc_getAssociatedObject
//NSObject+IndieBandName.h
@interface NSObject (IndieBandName)
@property (nonatomic, strong) NSString *indieBandName;
@end
上面是頭文件聲明,下面的實現的.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
1、擴展一個其它實施者定義的類
例如,你可以爲Cocoa frameworks裏的類增加方法。增加的方法會被子類繼承、而且在運行時也不會和原始的方法有任何不同。
2、作爲子類的一個替代方式
不需要定義一個子類來擴展已有的類,通過category你可以直接爲類添加方法。
例如,你可以爲NSArray和其它的Cocoa classes添加categories.與添加子類的方式來比,你不需要你擴展的類的源代碼。
3、把實現一個新類的方法分佈在多個源文件裏
例如,你可以把一個很大的類的方法分組到幾個categories裏,然後把每個 category放在自己的文件裏。當以這種方式使用時,categories在很多方面對開發過程都是有幫助的:
1.提供一個簡單的方式來組合相關的方法。被定義在不同的類裏的相似的方法可以被保存在同一個源文件裏。
2.當一個類是由多個開發者共同定義的時候,可以簡化大類的管理。
3.爲一個非常大的類的增量編譯提供方便。
4.提高常用方法的本地參考。
5.可以根據不同版本的程序配置不同的類,而無需爲不同版本保持相同的源代碼。
4、可以用來聲明非正式協議
例如:@interface NSObject ( MyXMLSupport )
- initFromXMLRepresentation: (NSXMLElement *)XMLElement;
- ( NSXMLElement *)XMLRepresentation;
@end
以上、由於是非正式協議。所以編譯器不會檢測協議中的語法、以及方法是否實現
5、root class類別
Category 可以爲任何的類添加方法,其中也包括root class。添加到NSObject類上的方法對於所有與你的代碼相關聯的類都是可用的。有時候爲root class添加方法是非常有用的,但是它也是非常危險的。雖然從表面上看起來category所做出的修改可以被很好的理解,而且影響也是有限的,但是繼承的機制使得它有了一個廣泛的作用域。你可能會對你程序裏不可見的類做出意想不到的修改;你可能會對你正在做的事會產生的結果一無所知。甚者,當對你修改過什麼一無所知的人在你的程序上工作時,他們對於他們正在做的事也不會有一個充分的瞭解。
另外,當你爲root class實現方法時有兩點需要記住:
發送消息給super是非法的(因爲NSObject沒有超類
類的對象可以執行root class中定義的實例方法
正常來說,類對象只能執行類方法。但是root class中定義的實例方法是一個特例。它們定義了一個類,在運行時系統中的所有對象都繼承這個類。類對象是完全成熟的對象,它需要共享同一個類。這個特性意味着你爲NSObject類在category定義的實例方法不僅要能被實例對象執行,而且也要能被類對象執行。例如:在方法體中,self可能代表一個類對象,也可能是類的一個實例。
———————————————————————————————————————————————————————————————————————————————————————————————————————————————————
此外、雖然Objective-C語言目前允許使用category來通過重載繼承的類的方法或者甚至是類文件中的方法,但是這種做法是被強烈反對的。category不是子類的替代品。使用category來重載方法有很多重大的缺陷:
1.當category 重載一個從父類繼承過來的方法,通常可以通過super關鍵字來調用父類的實現方法。然而,如果category重載一個擴展類本身存在的方法,就沒有喚醒原始實現方法的辦法了。
2.同一個類的category不能聲明重載這個類的另一個category中聲明的方法。
這一點非常的重要,因爲很多Cocoa類也是通過使用categories來實現的。一個你試圖重載的框架中定義的方法可能本身就已經在一個category被實現了,如果你這樣做了,很可能使用得前面的category的方法的實現失效。
3.一些category methods的存在可能會導致整個框架的行爲發生變化。
例如,如果你在NSObject的一個category中重載windowWillClose:委託方法,在你的程序裏所有窗口的委託將會使用category方法來回應;所有NSWIndow實例的行爲都會改變。你爲一個框架類增加的Categories可能會導致行爲上很神祕
的變化和程序的崩潰。
1 @interface MyObject:NSObject
2 {
3 NSNumber* number;
4 }
5 -(NSNumber*)getNum;
6 @end
7
8 @interface MyObject(Setter)
9 -(void)setNum:(NSNumber*)num;
10 @end
11
12 @implementation MyObject
13 -(NSNumber*)getNum
14 {
15 return number;
16 }
1 @interface MyObject:NSObject
2 {
3 NSNumber* number;
4 }
5 -(NSNumber*)getNum;
6 @end
7
8 @interface MyObject() //注意這裏的括號裏面是沒有名字的
9 -(void)setNum:(NSNumber*)num;
10 @end
11
12 @implementation MyObject
13 -(NSNumber*)getNum
14 {
15 return number;
16 }
17
18 -(void)setNum:(NSNumber*)num
19 {
20 number = num;
21 }
22 @end
關於Extensions的延伸用法
Extensions 被設計出來的目的是爲了解決二個問題。
第一就是便利編譯器能更好的驗證類的私有接口,第二個目的就是解決一個微秒而粗糙的properties(另一個objective-c 2.0的特性)問題.
1、有關更好的驗證類的私有接口:
當實現一個類,通常在類的@implementation塊會有一個方法集。它們作用於整個@implementation塊,在其它所有方法的之前實現,這樣當有其它方法用到這些私有方法
時 ,就不會有警告出現(如果它們被實現在最下面,那麼編譯器會發出警告)。
但這樣的實現方式是很粗笨的,我們可以把所有私有方法的聲明放在一個category裏面,然後把這個category放在.m實現文件頂部。
就像下面這樣:
- @interface MyClass (SuperSecretInternalSauce)
- - (void) doMyPrivateThing;
- - (BOOL) canMyPrivateThingEatThis: (OtherClass *) aThing;
- @end
- @implementation MyClass
- ...
- @end
這些方法將不會在相應的@implementation MycClass (SuperSecretInternalSauce) 塊裏被實現,當然它們也不是一定要被實現的。
但這樣做的結果是編譯器將不會做確保你實現了所有在category裏聲明的方法的檢查,換句話說,編譯器也不會捕獲方法聲明中的拼寫錯誤。
這是因爲category如果沒有相應@implementation MycClass (SuperSecretInternalSauce)實現塊,那麼在objective-c裏它就是一個非正式協議。
它就是一個方法聲明集,裏面的方法可以有選擇的去實現,通常這種category會被聲明在這個類的了類裏。
由於 class extension 有效的擴展類的主接口,那麼把上面的一段聲明代碼改成下面這樣,也可以達到同樣的效果。
- @interface MyClass ()
- - (void) doMyPrivateThing;
- - (BOOL) canMyPrivateThingEatThis: (OtherClass *) aThing;
- @end
- @implementation MyClass
- ...
- @end
2、設計一個對外只讀、對內可讀可寫的properties:
當我們設計屬性時,通常不會設計得太強大。爲實現這個目的,可以把它聲明在一個categories裏,通過特別的synthesis這個屬性,可以對它達到功能性的限制或者完全禁止。
注意:synthesis在categories裏是被禁止的,因爲synthesis需要存儲,而在category裏不能有效的聲明一個存儲區,允許category合成訪問類實例變量的方法是不可接受的,
這樣太脆弱也太醜陋了。
然而,爲了達到內部類和框架的目的,聲明一個對公共來說是隻讀,而對私有來說可以讀寫的property也是可取的。
一個額外的需求是synthesis 這樣的properties必須總是能原生而精確的synthesize setter和getter方法。特別是當聲明一個atomic的property,
開發者沒有辦法正確的手動編寫1/2的getter setter方法對;也沒法保證鎖定的資源不外露,這就是說,在這種情況下沒法保證原子性。
class extensions很優雅的解決了這個問題。
具體來說,你可以像下面這樣來聲明一個property:
- @interface MyClass : NSObject
- @property(readonly) NSView *targetView;
- @end
然後是實現文件:
- @interface MyClass()
- @property(readwrite) NSView *targetView;
- @end
- @implementation MyClass
- @synthesize targetView;
- @end
這樣一個publicly readonly、privately readwrite 的property就完成了。