本隨筆系列主要介紹從一個Windows平台從事C#開發到Mac平台蘋果開發的一系列感想和體驗歷程,本系列文章是在起步階段逐步積累的,希望帶給大家更好,更真實的轉換歷程體驗。本文繼續上一篇隨筆《從C#到Object C,循序漸進學習蘋果開發(2)--Objective-C和C#的差異》,繼續對比介紹它們兩者之間的差異,以便我們從C#陣營過來的人員加深印象,深入了解Objective-C語言的特性。本篇隨筆主要針對Objective-C里面的分類(category)和協議Protocal概念的理解進行介紹。
1、分類(category)概念和使用
如果我們使用過C#,我們都知道,C#里面有一個叫做擴展函數的東西,可以在不繼承已有類的情況下,給存在的類增加一些原本沒有的接口函數,Objective-C的分類概念和這個很相似,甚至可以說是同一類型的東西,雖然不知道他們誰先誰后出現,這個東西的引入,能使得編程方面更加豐富高效。
Objective-C提供了一種與眾不同的方式——Category,可以動態的為已經存在的類添加新的行為。這樣可以保證類的原始設計規模較小,功能增加時再逐步擴展。使用Category對類進行擴展時,不需要訪問其源代碼,也不需要創建子類。Category使用簡單的方式,實現了類的相關方法的模塊化,把不同的類方法分配到不同的分類文件中。不過Category並不能給類擴展出屬性,這點要注意,因為Object C不支持這樣的屬性擴展。
分類(Category)的定義語法如下所示。
@interface ClassName (CategoryName) @end
這里好像它們還有一個約定俗成的習慣,將聲明文件和實現文件名稱統一采用“原類名+Category”的方式命名。所以OC的這種功能雖然和C#功能差不多,但是這點約定和C#不一樣,C#不管你放到哪里都行,但是我們還是會應該尊重它的規則。
例如,我們給XYZPerson類增加一個擴展方法的定義如下所示,這個定義的函數約定是放到文件"XYZPerson+XYZPersonNameDisplayAdditions.h"里面。
#import "XYZPerson.h" @interface XYZPerson (XYZPersonNameDisplayAdditions) - (NSString *)testMethod; @end
那么它的實現代碼如下所示,它的代碼約定是放到 "XYZPerson+XYZPersonNameDisplayAdditions.m"里面。
#import "XYZPerson+XYZPersonNameDisplayAdditions.h" @implementation XYZPerson (XYZPersonNameDisplayAdditions) - (NSString *)testMethod { return [NSString stringWithFormat:@"%@, %@", self.lastName, self.firstName]; } @end
在C#里面,擴展方法是命名空間相關的,一旦跳出了命名空間的范圍,這個擴展函數就不在起作用,而Objective-C的這個和類一樣,沒有命名空間的概念,因此在擴展的時候,需要小心謹慎一點,否則容易導致分類的接口和類本身發生沖突。基於這個原因,所以蘋果建議也是給分類的接口增加一個前綴,命名則采用接口的一貫規則,如下面代碼所示。
@interface NSSortDescriptor (XYZAdditions) + (id)xyz_sortDescriptorWithKey:(NSString *)key ascending:(BOOL)ascending; @end
這樣擴展方法名稱雖然長了一點,但是基本上確保和普通的接口方法不會發生沖突了。
Category的使用場景:
還有一種成為類擴展的功能,它是針對存在代碼的類的情況,也就是你的類代碼和你擴展的源碼是同時編譯的情況下。
類擴展的方法和上面的分類類似,他們不需要寫擴展分類的名稱,這個有點像匿名擴展分類的概念了,如下所示
@interface ClassName () @end
這個匿名的擴展分類,和普通的Category不同,它除了可以方法外,還可以添加屬性或者變量的。
2、協議Protocal
這個概念有很大程度上和C#的接口類似,但是它有所不同,它可以可選的實現接口@optional,也有必選的實現接口@required,雖然Objective-C里面已經有一個關鍵字 @interface,不過這個和Protocal還是有不同的。
和C#的接口一樣,這種協議也可以繼承自另外一個Protocal,也就是他們可以有繼承關系。
@protocol NewProtocal <Protocal> @end
由於Objective-C開發的很多應用,如IOS的應用,他們在MVC的開發模型里面,都大量使用了代理模式,這種Protocal很好的處理了這種關系。在iOS和OS X開發中,Apple采用了大量的代理模式來實現MVC中View和Controller的解耦。
例如UIView產生的所有事件,都是通過委托的方式交給Controller完成。根據約定,框架中后綴為Delegate的都是Protocol,例如UIApplicationDelegate,UIWebViewDelegate等。
在C#里面有很多如IClonable, IEnumerable這樣的接口,只要實現了,就能實現克隆和枚舉,在Objective-C里面,這個就是可以使用Protocal來替代了,如果某個協議繼承了NSObject,那么這是代表在此聲明的協議,是NSObject協議的衍生協議(不是NSObject類),也就是說,這里的語境理解NSObject是一個協議,如果是在@Interface里面的繼承關系,那么那個就是NSObject對象。有點意思哦。
下面是一個可選和必選的協議定義例子。
@protocol XYZPieChartViewDataSource - (NSUInteger)numberOfSegments; - (CGFloat)sizeOfSegmentAtIndex:(NSUInteger)segmentIndex; @optional - (NSString *)titleForSegmentAtIndex:(NSUInteger)segmentIndex; - (BOOL)shouldExplodeSegmentAtIndex:(NSUInteger)segmentIndex; @required - (UIColor *)colorForSegmentAtIndex:(NSUInteger)segmentIndex; @end
由於協議有可選和必選,如果我們想知道某個動態的對象是否具有某個接口函數,就是通過@selector操作符來進行判斷的。
NSString *thisSegmentTitle; if ([self.dataSource respondsToSelector:@selector(titleForSegmentAtIndex:)]) { thisSegmentTitle = [self.dataSource titleForSegmentAtIndex:index]; }
和C#的接口定義類似,Objective-C的一個類對象它可以實現多的協議,如下例子是一個類的接口定義實現幾個協議的情況。
@interface MyClass : NSObject <MyProtocol, AnotherProtocol, YetAnotherProtocol> ... @end
這樣就實現了MyClass對象只有一個基類對象,但是可以實現多個協議(C#是多個接口)的情況。