本隨筆系列主要介紹從一個Windows平臺從事C#開發(fā)到Mac平臺蘋果開發(fā)的一系列感想和體驗歷程伞访,本系列文章是在起步階段逐步積累的,希望帶給大家更好式廷,更真實的轉(zhuǎn)換歷程體驗咐扭。本文繼續(xù)上一篇隨筆《從C#到Object C,循序漸進學(xué)習(xí)蘋果開發(fā)(2)--Objective-C和C#的差異》滑废,繼續(xù)對比介紹它們兩者之間的差異蝗肪,以便我們從C#陣營過來的人員加深印象,深入了解Objective-C語言的特性蠕趁。本篇隨筆主要針對Objective-C里面的分類(category)和協(xié)議Protocal概念的理解進行介紹薛闪。
1、分類(category)概念和使用
如果我們使用過C#俺陋,我們都知道豁延,C#里面有一個叫做擴展函數(shù)的東西,可以在不繼承已有類的情況下腊状,給存在的類增加一些原本沒有的接口函數(shù)诱咏,Objective-C的分類概念和這個很相似,甚至可以說是同一類型的東西缴挖,雖然不知道他們誰先誰后出現(xiàn)袋狞,這個東西的引入,能使得編程方面更加豐富高效映屋。
Objective-C提供了一種與眾不同的方式——Category苟鸯,可以動態(tài)的為已經(jīng)存在的類添加新的行為。這樣可以保證類的原始設(shè)計規(guī)模較小棚点,功能增加時再逐步擴展早处。使用Category對類進行擴展時,不需要訪問其源代碼瘫析,也不需要創(chuàng)建子類砌梆。Category使用簡單的方式默责,實現(xiàn)了類的相關(guān)方法的模塊化,把不同的類方法分配到不同的分類文件中么库。不過Category并不能給類擴展出屬性傻丝,這點要注意,因為Object C不支持這樣的屬性擴展诉儒。
分類(Category)的定義語法如下所示葡缰。
@interface ClassName (CategoryName)
@end
這里好像它們還有一個約定俗成的習(xí)慣,將聲明文件和實現(xiàn)文件名稱統(tǒng)一采用“原類名+Category”的方式命名忱反。所以O(shè)C的這種功能雖然和C#功能差不多泛释,但是這點約定和C#不一樣,C#不管你放到哪里都行温算,但是我們還是會應(yīng)該尊重它的規(guī)則怜校。
例如,我們給XYZPerson類增加一個擴展方法的定義如下所示注竿,這個定義的函數(shù)約定是放到文件"XYZPerson+XYZPersonNameDisplayAdditions.h"里面茄茁。
#import "XYZPerson.h"
@interface XYZPerson (XYZPersonNameDisplayAdditions)
- (NSString *)testMethod;
@end
那么它的實現(xiàn)代碼如下所示,它的代碼約定是放到 "XYZPerson+XYZPersonNameDisplayAdditions.m"里面巩割。
#import "XYZPerson+XYZPersonNameDisplayAdditions.h"
@implementation XYZPerson (XYZPersonNameDisplayAdditions)
- (NSString *)testMethod {
return [NSString stringWithFormat:@"%@, %@", self.lastName, self.firstName];
}
@end
在C#里面裙顽,擴展方法是命名空間相關(guān)的,一旦跳出了命名空間的范圍宣谈,這個擴展函數(shù)就不在起作用愈犹,而Objective-C的這個和類一樣,沒有命名空間的概念闻丑,因此在擴展的時候漩怎,需要小心謹(jǐn)慎一點,否則容易導(dǎo)致分類的接口和類本身發(fā)生沖突嗦嗡⊙福基于這個原因,所以蘋果建議也是給分類的接口增加一個前綴侥祭,命名則采用接口的一貫規(guī)則叁执,如下面代碼所示。
@interface NSSortDescriptor (XYZAdditions)
+ (id)xyz_sortDescriptorWithKey:(NSString *)key ascending:(BOOL)ascending;
@end
這樣擴展方法名稱雖然長了一點卑硫,但是基本上確保和普通的接口方法不會發(fā)生沖突了。
Category的使用場景:
1蚕断、當(dāng)你在定義類的時候欢伏,在某些情況下(例如需求變更),你可能想要為其中的某個或幾個類中添加方法亿乳。
2硝拧、一個類中包含了許多不同的方法需要實現(xiàn)径筏,而這些方法需要不同團隊的成員實現(xiàn)
3、當(dāng)你在使用基礎(chǔ)類庫中的類時障陶,你可能希望這些類實現(xiàn)一些你需要的方法滋恬。
遇到以上這些需求,Category可以幫助你解決問題抱究。當(dāng)然恢氯,使用Category也有些問題需要注意,
1鼓寺、Category可以訪問原始類的實例變量勋拟,但不能添加變量,如果想添加變量妈候,可以考慮通過繼承創(chuàng)建子類敢靡。
2、Category可以重載原始類的方法苦银,但不推薦這么做啸胧,這么做的后果是你再也不能訪問原來的方法。如果確實要重載幔虏,正確的選擇是創(chuàng)建子類纺念。
3、和普通接口有所區(qū)別的是所计,在分類的實現(xiàn)文件中可以不必實現(xiàn)所有聲明的方法柠辞,只要你不去調(diào)用它。
還有一種成為類擴展的功能主胧,它是針對存在代碼的類的情況叭首,也就是你的類代碼和你擴展的源碼是同時編譯的情況下。
類擴展的方法和上面的分類類似踪栋,他們不需要寫擴展分類的名稱焙格,這個有點像匿名擴展分類的概念了,如下所示
@interface ClassName ()
@end
這個匿名的擴展分類夷都,和普通的Category不同眷唉,它除了可以方法外,還可以添加屬性或者變量的囤官。
2冬阳、協(xié)議Protocal
這個概念有很大程度上和C#的接口類似,但是它有所不同党饮,它可以可選的實現(xiàn)接口@optional肝陪,也有必選的實現(xiàn)接口@required,雖然Objective-C里面已經(jīng)有一個關(guān)鍵字 @interface刑顺,不過這個和Protocal還是有不同的氯窍。
和C#的接口一樣饲常,這種協(xié)議也可以繼承自另外一個Protocal,也就是他們可以有繼承關(guān)系狼讨。
@protocol NewProtocal <Protocal>
@end
由于Objective-C開發(fā)的很多應(yīng)用贝淤,如IOS的應(yīng)用,他們在MVC的開發(fā)模型里面政供,都大量使用了代理模式播聪,這種Protocal很好的處理了這種關(guān)系。在iOS和OS X開發(fā)中鲫骗,Apple采用了大量的代理模式來實現(xiàn)MVC中View和Controller的解耦犬耻。
例如UIView產(chǎn)生的所有事件,都是通過委托的方式交給Controller完成执泰。根據(jù)約定枕磁,框架中后綴為Delegate的都是Protocol,例如UIApplicationDelegate术吝,UIWebViewDelegate等计济。
在C#里面有很多如IClonable, IEnumerable這樣的接口,只要實現(xiàn)了排苍,就能實現(xiàn)克隆和枚舉沦寂,在Objective-C里面,這個就是可以使用Protocal來替代了淘衙,如果某個協(xié)議繼承了NSObject传藏,那么這是代表在此聲明的協(xié)議,是NSObject協(xié)議的衍生協(xié)議(不是NSObject類)彤守,也就是說毯侦,這里的語境理解NSObject是一個協(xié)議,如果是在@Interface里面的繼承關(guān)系具垫,那么那個就是NSObject對象侈离。有點意思哦。
下面是一個可選和必選的協(xié)議定義例子筝蚕。
@protocol XYZPieChartViewDataSource
- (NSUInteger)numberOfSegments;
- (CGFloat)sizeOfSegmentAtIndex:(NSUInteger)segmentIndex;
@optional
- (NSString *)titleForSegmentAtIndex:(NSUInteger)segmentIndex;
- (BOOL)shouldExplodeSegmentAtIndex:(NSUInteger)segmentIndex;
@required
- (UIColor *)colorForSegmentAtIndex:(NSUInteger)segmentIndex;
@end
由于協(xié)議有可選和必選卦碾,如果我們想知道某個動態(tài)的對象是否具有某個接口函數(shù),就是通過@selector操作符來進行判斷的起宽。
NSString *thisSegmentTitle;
if ([self.dataSource respondsToSelector:@selector(titleForSegmentAtIndex:)]) {
thisSegmentTitle = [self.dataSource titleForSegmentAtIndex:index];
}
和C#的接口定義類似洲胖,Objective-C的一個類對象它可以實現(xiàn)多的協(xié)議,如下例子是一個類的接口定義實現(xiàn)幾個協(xié)議的情況坯沪。
@interface MyClass : NSObject <MyProtocol, AnotherProtocol, YetAnotherProtocol>
...
@end
這樣就實現(xiàn)了MyClass對象只有一個基類對象绿映,但是可以實現(xiàn)多個協(xié)議(C#是多個接口)的情況。