從C#到Objective-C,循序漸進學習蘋果開發(3)--分類(category)和協議Protocal的理解


本隨筆系列主要介紹從一個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的使用場景:

1、當你在定義類的時候,在某些情況下(例如需求變更),你可能想要為其中的某個或幾個類中添加方法。
2、一個類中包含了許多不同的方法需要實現,而這些方法需要不同團隊的成員實現
3、當你在使用基礎類庫中的類時,你可能希望這些類實現一些你需要的方法。
 
遇到以上這些需求,Category可以幫助你解決問題。當然,使用Category也有些問題需要注意,
1、Category可以訪問原始類的實例變量,但不能添加變量,如果想添加變量,可以考慮通過繼承創建子類。
2、Category可以重載原始類的方法,但不推薦這么做,這么做的后果是你再也不能訪問原來的方法。如果確實要重載,正確的選擇是創建子類。
3、和普通接口有所區別的是,在分類的實現文件中可以不必實現所有聲明的方法,只要你不去調用它。

還有一種成為類擴展的功能,它是針對存在代碼的類的情況,也就是你的類代碼和你擴展的源碼是同時編譯的情況下。

類擴展的方法和上面的分類類似,他們不需要寫擴展分類的名稱,這個有點像匿名擴展分類的概念了,如下所示

@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#是多個接口)的情況。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM