在Objective-C語言中, 類別、類擴展(也稱爲匿名類別)以及協議是Objective-C 語言級別支持的模式,用來實現對類進行功能擴展。
一、類別--用來增加方法到已存在類
聲明一個類別的語法如下:
@interface ClassName (CategoryName)
@end
Objective-C 的標準的類聲明,也使用@interface 關鍵字。但類別與類聲明區別的是在括號中聲明瞭一個類別名字。
類別能爲任何類添加類別,包括不知道源代碼的類(例如標準的Cocoa Touch 類等)。
已聲明類別的類,類及其子類的所有實例都可以使用聲明在類別中的方法,在運行時,通過類別爲類添加的方法與類本身的方法沒有任何區別。
類別也通常在頭文件中進行聲明,在分離的源文件中進行類別方法的實現。
爲了使用爲類添加的類別,需要在使用的地方輸入聲明類別的頭文件。
類別可以作爲一種設計模式使用,用來使用類別把一個實現複雜的類分離爲幾個實現文件。也可以爲不同的平臺提供不同的類別實現方法。
類別用來聲明實例方法或類方法,但不適合聲明額外的屬性。
在一個類別接口中聲明一個屬性是有效的,但在類別中聲明一個額外的實例變量是不可能的,這意味者編譯器不能夠生成任何實例變量,也不能夠生成任何屬性存取方法。 但你能夠在類別實現中實現自己的屬性存取方法,但你不能夠保持和跟蹤一個屬性值,除非它已經在原先的類中存儲。
另外需要注意的是類別中定義的方法名字不能與該類已有的方法或爲該類(或其超類)定義的其它類別中的方法衝突。
爲已存在類添加帶有實例變量的屬性的僅有的方法是使用類擴展。
二、類擴展--用來擴展一個類的內部實現
類擴展功能上與類別類似,用來擴展一個類的功能,與類別不同的是,類擴展中能爲一個類增加屬性和實例變量。另外類擴展僅能在一個有源碼的類中使用,用來爲其添加功能。使用類擴展通常用來實現和擴展類的私有信息(包括私有屬性和方法)。
聲明一個類擴展的語法與類別的語法相似,但括號中名字爲空,因此類擴展也被稱爲匿名類別。如下所示:
@interface ClassName ()
@end
類擴展中聲明的方法要在原先類的@implementation塊中加以實現。
如果在一個類擴展中聲明一個屬性,編譯器自動爲其生成相對應的存取方法和一個實例變量。如果在一個類擴展中增加任意方法,它們也必須在類的實現中加以實現。
在一個類擴展中添加定製的實例變量也是可能的。
三、協議
在Objective-C中,每個類都都對外提供自己的接口,類包括接口聲明和類實現。而沒有像其它語言(如java)那樣,接口與類定義是獨立的,一個類可以實現多個接口。
但在Objective-C中協議可以起到類似的作用,協議用來聲明與任何特定的類獨立的方法,如果一個類聲明符合某種協議,則其實現中必須實現協議中聲明的方法,但與其它語言接口定義不同的是在協議中不僅能聲明對象方法,還能聲明類方法以及屬性。
定義一個協議的語法如下:
@protocol ProtocolName
// list of methods and properties
@end
Objective-C 使用三角括號指示一個屬性或類符合一種協議。
默認情況下在一個協議中聲明的方法都是必須方法,任何符合該協議的類必須實現這些方法。
在協議中也可以使用@optional關鍵字規定可選方法。遵從包括可選方法的協議的類可以根據需要實現可選方法或不實現。
@protocol XYZPieChartViewDataSource
- (NSUInteger)numberOfSegments;
- (CGFloat)sizeOfSegmentAtIndex:(NSUInteger)segmentIndex;
@optional
- (NSString *)titleForSegmentAtIndex:(NSUInteger)segmentIndex;
- (BOOL)shouldExplodeSegmentAtIndex:(NSUInteger)segmentIndex;
@required
- (UIColor *)colorForSegmentAtIndex:(NSUInteger)segmentIndex;
@end
在以上協議聲明中的titleForSegmentAtIndex:和shouldExplodeSegmentAtIndex:方法標記爲是可選方法。
@optional關鍵字指示其下面聲明的方法都是可選方法,直到協議定義結束或碰到另外的@required指令。
如果在一個協議中標記一個方法爲可選,你必須在試圖調用它之前檢查是否一個對象實現了該方法。
使用respondsToSelector: 方法使用一個@selector指令引用一個方法的標識(方法名)來檢查該方法是否實現。如下所示:
NSString *thisSegmentTitle;
if ([self.dataSource respondsToSelector:@selector(titleForSegmentAtIndex:)]) {
thisSegmentTitle = [self.dataSource titleForSegmentAtIndex:index];
}
也可以規定一個協議符合另外的協議,語法如下:
@protocol MyProtocol <NSObject>
...
@end
聲明一個類符合一個協議的語法如下:
@interface MyClass : NSObject <MyProtocol>
...
@end
與其它語言一個類可以實現多個接口相同,在Objective-C也可以聲明一個類符合多個協議,語法如下:
@interface MyClass : NSObject <MyProtocol, AnotherProtocol, YetAnotherProtocol>
...
@end
但如果出現這種情況,意味着你的類過於複雜,需要重構,分離相關的行爲到多個較小的類,每一個類定義清楚的責任。