探尋iOS之協議(protocol)

在iOS開發中,Protocol是一種經常用到的設計模式,蘋果的系統框架中也普遍用到了這種方式,比如UITableView中的<UITableViewDelegate>,以及<NSCopying>、<NSObject>這樣的協議。我想大家也都自定義過協議,一般都用于回調,或者數據傳遞。

Protocol是什么?

A protocol declares a programmatic interface that any class may choose to implement. Protocols make it possible for two classes distantly related by inheritance to communicate with each other to accomplish a certain goal. They thus offer an alternative to subclassing. Any class that can provide behavior useful to other classes may declare a programmatic interface for vending that behavior anonymously. Any other class may choose to adopt the protocol and implement one or more of its methods, thereby making use of the behavior. The class that declares a protocol is expected to call the methods in the protocol if they are implemented by the protocol adopter.

協議是任何類都能夠選擇實現的程序接口。協議能夠使兩個沒有繼承關系的類相互交流并完成特定的目的,因此它提供了除繼承外的另一種選擇。任何能夠為其他類提供有用行為的類都能夠聲明接口來匿名的傳達這個行為。任何其他類都能夠選擇遵守這個協議并實現其中的一個或多個方法,從而利用這個行為。如果協議遵守者實現了協議中的方法,那么聲明協議的類就能夠通過遵守者調用協議中的方法。

總結:

  • 協議能夠聲明方法,協議遵守者實現協議中的方法,聲明協議的類通過遵守者調用協議中的方法;
  • protocol不能定義成員變量,但是能夠聲明屬性,因為屬性=成員變量+setting方法+getting方法;

問題:
在定義protocol的時候后面會有<NSObject>,為什么?
e.g.

@protocol MGSwipeTableCellDelegate <NSObject>
@optional

首先要注意,NSObject是所有object-C的根類,<NSObject>是NSObject遵循的協議,協議也能繼承,既可以繼承自自定義的協議,也可以繼承自系統的協議。那自定義protocol的時候直接讓protocol繼承<NSObject>這個協議呢?因為這個協議中定義了一些基本的方法,由于我們使用的所有類都繼承NSObject這個基類,而這個基類遵守了<NSObject>這個協議,那么也就實現了其中的那些方法,這些方法當然可以由NSObject及其子類對象調用,但是在不知道遵守者類型的時候需要用到id <協議名>這樣的指針,這個指針在編譯期并不知道自己指向哪個對象,唯一能調用的便是協議中的方法,然而有時候又需要用一些基本的方法,比如要辨別id <協議名>這個指針所指的對象屬于哪個類,就要用到-isMemberOf:這個方法,而這個方法是<NSObject>這個協議中的方法之一,所以,我們自定義的協議都需要繼承<NSObject>。本段一開始便說道:<NSObject>中的方法在NSObject基類中實現了,那么無需再關心實現了,直接調用<NSObject>中的方法吧。

protocol 的分類:“正式協議” 和 “非正式協議”(類別)

對于protocol的分類,一般我們討論protocol就是談論正式協議。
對于protocol,iOS 文檔是這樣定義的:

There are two varieties of protocol, formal and informal:

A formal protocol declares a list of methods that client classes are expected to implement. Formal protocols have their own declaration, adoption, and type-checking syntax. You can designate methods whose implementation is required or optional with the @required and @optional keywords. Subclasses inherit formal protocols adopted by their ancestors. A formal protocol can also adopt other protocols. Formal protocols are an extension to the Objective-C language.

An informal protocol is a category on NSObject, which implicitly makes almost all objects adopters of the protocol. (A category is a language feature that enables you to add methods to a class without subclassing it.) Implementation of the methods in an informal protocol is optional. Before invoking a method, the calling object checks to see whether the target object implements it. Until optional protocol methods were introduced in Objective-C 2.0, informal protocols were essential to the way Foundation and AppKit classes implemented delegation.

非正式協議

非正式協議簡單理解為類別,凡是NSObject或其子類的類別,都是非正式協議。
e.g.

@interface NSString (CamelCase)  //類別  
-(NSString*) camelCaseString;    
@end    

上面就定義了一個NSString的類別。

重要協議

下面介紹幾個重要的系統定義的協議NSObject協議、NSCopying協議、NSMutableCopying協議。

<NSObject>

@protocol NSObject

- (BOOL)isEqual:(id)object;
@property (readonly) NSUInteger hash;

@property (readonly) Class superclass;
- (Class)class OBJC_SWIFT_UNAVAILABLE("use 'anObject.dynamicType' instead");
- (instancetype)self;

- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;

- (BOOL)isProxy;

- (BOOL)isKindOfClass:(Class)aClass;
- (BOOL)isMemberOfClass:(Class)aClass;
- (BOOL)conformsToProtocol:(Protocol *)aProtocol;

- (BOOL)respondsToSelector:(SEL)aSelector;

- (instancetype)retain OBJC_ARC_UNAVAILABLE;
- (oneway void)release OBJC_ARC_UNAVAILABLE;
- (instancetype)autorelease OBJC_ARC_UNAVAILABLE;
- (NSUInteger)retainCount OBJC_ARC_UNAVAILABLE;

- (struct _NSZone *)zone OBJC_ARC_UNAVAILABLE;

@property (readonly, copy) NSString *description;
@optional
@property (readonly, copy) NSString *debugDescription;

@end

注意到沒,NSObject定義了兩個readonly關鍵字的屬性hash和superclass。因為上文提到過,protocol不能定義成員變量,但是能夠聲明屬性,因為屬性=成員變量+setting方法+getting方法

<NSCopying>

@protocol NSCoding

- (void)encodeWithCoder:(NSCoder *)aCoder;
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder; // NS_DESIGNATED_INITIALIZER

@end

NSCopying是一個與對象拷貝有關的協議。如果想讓一個類的對象支持拷貝,就需要讓該類實現NSCopying協議。NSCopying協議中的聲明的方法只有一個- (id)copyWithZone:(NSZone *)zone。當我們的類實現了NSCopying協議,通過類的對象調用copy方法時,copy方法就會去調用我們實現的- (id)copyWithZone:(NSZone *)zone方法,實現拷貝功能。實現代碼如下所示:

- (id)copyWithZone:(NSZone *)zone{    
        PersonModel *model = [[[self class] allocWithZone:zone] init];
        model.firstName = self.firstName;
        model.lastName  = self.lastName;
        //未公開的成員
     model->_nickName = _nickName;
     return model;
}

tips:

  • 在- (id)copyWithZone:(NSZone *)zone方法中,一定要通過[self class]方法返回的對象調用allocWithZone:方法。因為指針可能實際指向的是PersonModel的子類。這種情況下,通過調用[self class],就可以返回正確的類的類型對象。

<NSMutableCopying>

@protocol NSMutableCopying

- (id)mutableCopyWithZone:(nullable NSZone *)zone;

@end

NSCopying協議與NSMutableCopying的區別主要是在于,返回的對象是否是可變類型的。
NSCopying協議與NSMutableCopying的區別主要是在于,返回的對象是否是可變類型的。以Foundation框架的NSArray為例

NSArray *nameArray = @[@"Jim", @"Tom", @"David"];
NSArray *copyArray = [nameArray copy];
NSMutableArray *mutableCopyArray = [nameArray mutableCopy];
[mutableCopyArray addObject:@"Sam"];

NSArray對象調用copy方法時,copy方法會調用- (id)copyWithZone:(NSZone *)zone,得到對象的一份拷貝,但得到的對象還是不可變的對象。而NSArray對象調用mutableCopy方法時,mutableCopy方法會調用- (id)mutableCopyWithZone:(NSZone *)zone,得到可變的對象。

所以,如果自定義類具有可變和不可變的區別,想讓它支持拷貝時,就需要同時實現NSCopying和NSMutableCopying,在- (id)copyWithZone:(NSZone *)zone返回的是不可變對象,在- (id)mutableCopyWithZone:(NSZone *)zone返回的是可變對象。例如NSArray,在定義的時候需要申明支持NSCopying和NSMutableCopying協議,在NSArray中實現- (id)copyWithZone:(NSZone *)zone,在NSMutableArray中實現- (id)mutableCopyWithZone:(NSZone *)zone。

NSSecureCoding

// Objects which are safe to be encoded and decoded across privilege boundaries should adopt NSSecureCoding instead of NSCoding. Secure coders (those that respond YES to requiresSecureCoding) will only encode objects that adopt the NSSecureCoding protocol.
// NOTE: NSSecureCoding guarantees only that an archive contains the classes it claims. It makes no guarantees about the suitability for consumption by the receiver of the decoded content of the archive. Archived objects which  may trigger code evaluation should be validated independently by the consumer of the objects to verify that no malicious code is executed (i.e. by checking key paths, selectors etc. specified in the archive).

@protocol NSSecureCoding <NSCoding>
@required
// This property must return YES on all classes that allow secure coding. Subclasses of classes that adopt NSSecureCoding and override initWithCoder: must also override this method and return YES.
// The Secure Coding Guide should be consulted when writing methods that decode data.
#if FOUNDATION_SWIFT_SDK_EPOCH_AT_LEAST(8)
@property (class, readonly) BOOL supportsSecureCoding;
#else
+ (BOOL)supportsSecureCoding;
#endif
@end
  • 蘋果在iOS6引入的基于NSCoding的一個新的協議,該協議能夠保證有關歸檔代碼的安全性。
  • 大部分支持NSCoding的系統對象都已經升級到支持NSSecureCoding;
  • 常用于對象編解碼;

致謝

感謝雨雪傳奇作品
感謝黃龍輝作品

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,702評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,143評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,553評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,620評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,416評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,940評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,024評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,170評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,709評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,597評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,291評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,029評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,407評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,663評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,403評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,746評論 2 370

推薦閱讀更多精彩內容