objective-c的categories(分類)
一,當你想要為某個class 新增methods,你通常會擴充(extend,即繼承)它。然而這不一定是個完美解法,特別是你想要重寫一個class 的
某個功能,但你卻沒有原始碼時。Categories 允許你在現有的class 加入新功能,但不需要擴充它。Ruby 語言也有類似的功能。
例子一:
1,MyObjectCateName.h:
#import <Foundation/NSObject.h>#import "MyObject.h" //該標頭檔已經定義了MyObject類//重點是這行:MyObject是已經存在的類,()裡面是分類的名字,該分類擴充了之前MyObject的方法add和sub@interface MyObject(MyObjectCateName)-(MyObject*) add: (MyObject*) m;-(MyObject*) sub: (MyObject*) m;@end
2,MyObjectCateName.m:
#import "MyObjectCateName.h" //重點是這行:MyObject是已經存在的類,()裡面是分類的名字,該分類擴充了之前MyObject的方法add和sub@implementation MyObject(MyObjectCateName)-(MyObject*) add: (MyObject*) m { return [[MyObject alloc] initWithNumerator: numerator + [m numerator]];}-(MyObject*) sub: (MyObject*) m { return [[MyObject alloc] initWithNumerator: numerator - [m numerator]];}@end
二,(同一個class)只能有一個同名的category,其他的categories 得加上不同的、獨一無二的名字,Categories 在建立private methods 時十分有用。因為Objective-C
並沒有像Java 這種private/protected/public methods 的概念,所以必須要使用categories 來達成這種功能。作法是把private method 從你的class header (.h) 檔案移到implementation (.m) 檔案。
例子2:
1,MyClass.h
#import <Foundation/NSObject.h>@interface MyClass: NSObject-(void) publicMethod;@end
2,MyClass.m
#import <stdio.h>#import "MyClass.h"@implementation MyClass-(void) publicMethod {printf( "public method\n" );}@end// private methods@interface MyClass (Private)-(void) privateMethod;@end@implementation MyClass (Private)-(void) privateMethod {printf( "private method\n" );}@end
三,如何使用Categories
對於在Objective-C中Categories的使用,有以下幾種情況:
1)擴充已有的類
舉個例子,你可以向Cocoa架構中定義好的類中增加新的方法。新增的方法同時也會被子類繼承,在運行時將無法區分這些方法是新增的還是原來類已經定義好的。
2)作為子類的替代方案
除了通過定一個子類來擴充已有的類,你也可以通過Category直接向已有的類增加新的方法來實現。舉例來說,你可以向NSArray和其它Cocoa類增加新的Categories來擴充已有的類。如同定義子類,你同樣也不需要知道被擴充類的原始碼。
3)通過使用多個代碼檔案來定義一個類
舉個例子,你可以將一個規模比較大的類所有方法進行分組,然後將每組方法放到一個Category中,每個Category則用一個檔案來定義。在開發過程中,這樣的好處是顯而易見的。
提供一個簡單的方法對相關的方法進行分組。不同的類中相似的方法可以統一放到相同的檔案中,如果有多個開發人員需要同時實現一個類,通過將類的方法分成不同的Categories可以簡化代碼管理 ,可以對一個規模非常大的類進行增量編譯,減少重複編譯的時間 ,可以將常用方法集中在一起,便於尋找和參考 ,如果同一個類針對不同的應用有不同的實現,可以將不同的部分分別放入一個單獨的檔案中,以便代碼的維護 。
4)可以申明非正式的協議(protocols)
儘管Objective-C能夠允許使用Category覆蓋重寫類方法,甚至是定義在類介面中的方法,但強烈不建議這種用法。
Category不能完全代替子類,有以下幾個最大的缺點:
當在Category中覆蓋一個繼承的方法,在Category中的方法可以通過向super類發送一個訊息來調用被繼承的方法。但是,如果Category中覆蓋的那個方法已經在這個類的其它Category定義過了,則之前定義的方法將沒有機會被程式調用 ,在Category中無法確定其能夠可靠的覆蓋某個方法,而這個方法已經在其它的Category中定義過。這個問題在使用Cocoa架構時尤其突出。當你想覆蓋某個架構已經定義好的方法時,該方法已經在其它Category中實現,這樣就無法確定哪個定義和實現會被最先使用,帶來很大的不確定性。
如果你重新覆蓋定義了一些方法,往往會導致這個方法在整個架構中實現發生了變化。舉例來說,如果你增加了NSObject中windowWillClose:的實現,
這會導致所有的視窗調用那個新實現的方法,從而改變所有NSWindows執行個體的行為。這會帶來很多不確定性,並很有可能導致程式的崩潰。