1、分類(category)概念和使用 如果我們使用過C#,我們都知道,C#裡面有一個叫做擴展函數的東西,可以在不繼承已有類的情況下,給存在的類增加一些原本沒有的接口函數,Object 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#裡面,擴展方法是命名空間相關的,一旦跳出了命名空間的范圍,這個擴展函數就不在起作用,而Object 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,雖然Object C裡面已經有一個關鍵字 @interface,不過這個和Protocal還是有不同的。 和C#的接口一樣,這種協議也可以繼承自另外一個Protocal,也就是他們可以有繼承關系。 @protocol NewProtocal <Protocal> @end 由於Object C開發的很多應用,如IOS的應用,他們在MVC的開發模型裡面,都大量使用了代理模式,這種Protocal很好的處理了這種關系。在iOS和OS X開發中,Apple采用了大量的代理模式來實現MVC中View和Controller的解耦。 例如UIView產生的所有事件,都是通過委托的方式交給Controller完成。根據約定,框架中後綴為Delegate的都是Protocol,例如UIApplicationDelegate,UIWebViewDelegate等。 在C#裡面有很多如IClonable, IEnumerable這樣的接口,只要實現了,就能實現克隆和枚舉,在Object裡面,這個就是可以使用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#的接口定義類似,Object C的一個類對象它可以實現多的協議,如下例子是一個類的接口定義實現幾個協議的情況。 @interface MyClass : NSObject <MyProtocol, AnotherProtocol, YetAnotherProtocol> ... @end 這樣就實現了MyClass對象只有一個基類對象,但是可以實現多個協議(C#是多個接口)的情況。