下面我們來說說多態和繼承。
與Java一樣,Objective-C一樣不支持多重繼承,但是通過類別(Category)和協議(Protocol)可以很好的實現代碼復用和擴展。
—Category—
首先我們來談談Category。
Objective-C提供了一種與眾不同的方式——Catagory,可以動態的為已經存在的類添加新的行為。這樣可以保證類的原始設計規模較小,功能增加時再逐步擴展。使用Category 對類進行擴展時,不需要訪問其源代碼,也不需要創建子類。Category使用簡單的方式,實現了類的相關方法的模塊化,把不同的類方法分配到不同的分類文件中。
實現起來很簡單,我們舉例說明。
SomeClass.h
@interface SomeClass : NSObject{
}
-(void) print;
@end
這是類SomeClass的聲明文件,其中包含一個實例方法print。如果我們想在不修改原始類、不增加子類的情況下,為該類增加一個hello的方法,只需要簡單的定義兩個文件 SomeClass+Hello.h和SomeClass+Hello.m,在聲明文件和實現文件中用“()”把Category的名稱括起來即可。聲明文件代碼如下:
#import "SomeClass.h"
@interface SomeClass (Hello)
-(void)hello;
@end
實現文件代碼如下
#import "SomeClass+Hello.h"
@implementationSomeClass (Hello)
-(void)hello{
NSLog (@"name:%@ ", @"Jacky");
}
@end
其中Hello是Category的名稱,如果你用XCode創建Category,那么需要填寫的內容包括名稱和要擴展的類的名稱。這里還有一個約定成俗的習慣,將聲明文件和實現文件名稱統一采用“原類名+Category”的方式命名。
調用也非常簡單,毫無壓力,如下:
首先引入Category的聲明文件,然后正常調用即可。
#import "SomeClass+Hello.h"
SomeClass * sc =[[SomeClass alloc] init];
[sc hello]
執行結果是:
name:Jacky
Category的使用場景:
1、當你在定義類的時候,在某些情況下(例如需求變更),你可能想要為其中的某個或幾個類中添加方法。
2、一個類中包含了許多不同的方法需要實現,而這些方法需要不同團隊的成員實現
3、當你在使用基礎類庫中的類時,你可能希望這些類實現一些你需要的方法。
遇到以上這些需求,Category可以幫助你解決問題。當然,使用Category也有些問題需要注意,
1、Category可以訪問原始類的實例變量,但不能添加變量,如果想添加變量,可以考慮通過繼承創建子類。
2、Category可以重載原始類的方法,但不推薦這么做,這么做的后果是你再也不能訪問原來的方法。如果確實要重載,正確的選擇是創建子類。
3、和普通接口有所區別的是,在分類的實現文件中可以不必實現所有聲明的方法,只要你不去調用它。
用好Category可以充分利用Objective-C的動態特性,編寫出靈活簡潔的代碼。
—Protocol—
下面我們再來看Protocol。
Protocol,簡單來說就是一系列不屬于任何類的方法列表,其中聲明的方法可以被任何類實現。這種模式一般稱為代理(delegation)模式。你通過Protocol定義各種行為,在不同的場景采用不同的實現方式。在iOS和OS X開發中,Apple采用了大量的代理模式來實現MVC中View和Controller的解耦。
定義Protocol很簡單,在聲明文件(h文件)中通過關鍵字@protocol定義,然后給出Protocol的名稱,方法列表,然后用@end表示Protocol結束。在@end指令結束之前定義的方法,都屬于這個Protocol。例如:
@protocol ProcessDataDelegate <NSObject>
@required
- (void) processSuccessful: (BOOL)success;
@optional
- (id) submitOrder: (NSNumber *) orderid;
@end
以上代碼可以單獨放在一個h文件中,也可以寫在相關類的h文件中,可以視具體情況而定。該Protocol包含兩個方法,processSuccessful和submitOrder。這里還有兩個關鍵字,@required和@optional,表示如果要實現這個協議,那么processSuccessful方法是必須要實現的,submitOrder則是可選的,這兩個注解關鍵字是在Objective-C 2.0之后加入的語法特性。如果不注明,那么方法默認是@required的,必須實現。
那么如何實現這個Protocol呢,很簡單,創建一個普通的Objective-C類,取名為TestAppDelegate,這時會生成一個h文件和m文件。在h文件中引入包含Protocol的h文件,之后聲明采用這個Protocol即可,如下:
@interface TestAppDelegate : NSObject<ProcessDataDelegate>;
@end
用尖括號(<...>)括起來的ProcessDataDelegate就是我們創建的Protocol。如果要采用多個Protocol,可以在尖括號內引入多個Protocol 名稱,并用逗號隔開即可。例如<ProcessDataDelegate,xxxDelegate>
m文件如下:
@implementation TestAppDelegate
- (void) processSuccessful: (BOOL)success{
if (success) {
NSLog(@"成功");
}else {
NSLog(@"失敗");
}
}
@end
由于submitOrder方法是可選的,所以我們可以只實現processSuccessful。
Protocol一般使用在哪些場景呢?Objective-C里的Protocol和Java語言中的接口很類似,如果一些類之間沒有繼承關系,但是又具備某些相同的行為,則可以使用 Protocol來描述它們的關系。不同的類,可以遵守同一個Protocol,在不同的場景下注入不同的實例,實現不同的功能。其中最常用的就是委托代理模式,Cocoa框架中大量采用了這種模式實現數據和UI的分離。例如UIView產生的所有事件,都是通過委托的方式交給Controller完成。根據約定,框架中后綴為Delegate的都是Protocol,例如UIApplicationDelegate,UIWebViewDelegate 等,使用時大家可以留意一下,體會其用法。
使用Protocol時還需要注意的是:
1、Protocol本身是可以繼承的,比如:
@protocol A
-(void)methodA;
@end
@protocol B <A>
-(void)methodB;
@end
如果你要實現B,那么methodA和methodB都需要實現。
2、Protocol是類無關的,任何類都可以實現定義好的Protocol。如果我們想知道某個類是否實現了某個Protocol,還可以使用conformsToProtocol進行判斷,如下:
[obj conformsToProtocol:@protocol(ProcessDataDelegate)]