在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;
- 常用于對象編解碼;