Swift 中,定義協議 protocol 時,可以使用 extension 給它的某些方法提供默認實現:
protocol Printable {
func print()
func print1()
}
extension Printable {
func print() {
print("default print")
}
}
有了上面的代碼,當創建一個遵從 Printable 協議的類或者是結構體時,就能獲得 print() 方法??梢栽谛枰臅r候重新定義這個方法,如果不重新定義,就會使用這個默認方法。
遺憾的是 Objective-C 并沒有提供這樣的便利。當我們想為一個協議提供默認實現時,通常會提供一個基類,基類遵從協議并且提供默認實現。如果不想使用默認實現,需繼承基類,在子類中重寫方法。有沒有辦法可以像 Swift 那樣,在協議聲明的地方而不需要繼承鏈,就能簡便的提供默認實現呢?
為協議提供默認實現,可以理解為所有遵從協議的類都擁有提前定義的協議方法。Objective-C 強大的運行時可以獲得程序中所有的類,可以獲得類遵從的協議,還可以為每一個類添加方法。我們可以用一個臨時類來實現協議的方法,在所有的類自己的方法全部加載完后,為遵從協議的類添加臨時類的默認方法。之所以要在類自己的方法加載完成后,是為了防止類自己的實現被覆蓋,當方法已經存在時,class_addMethod 會添加失敗,這正是我們希望的。
__attribute__((constructor)) 特性修飾的函數,會在 runtime 加載完所有類之后,main 函數執行之前運行。通過這樣一個函數,將滿足條件的類加上提供的默認實現。
現在需要一個臨時類,來為協議實現默認方法。假設有協議:
@protocol Printable
@optional
- (NSString *)desc;
@end
為協議提供臨時類,來提供默認實現:
@interface PrintableTemporaryClass : NSObject <Printable>
@end
@implementation PrintableTemporaryClass
- (NSString *)desc {
return @"Default desc";
}
@end
上面代碼中,print 方法前的部分對所有協議來說都是類似的的,可以定義個宏來簡化代碼:
#define extensionProtocol(NAME) \
interface NAME ## TemporaryClass : NSObject <NAME> \
@end \
@implementation NAME ## TemporaryClass \
此時,臨時類的寫法會變成下面這樣:
@extensionProtocol(Printable)
- (NSString *)desc {
return @"Default desc";
}
@end
然后通過遍歷所有的類,進而遍歷類所有的協議,并查找運行時中是否有協議對應的臨時類(通過名字關聯),如果有,遍歷該臨時類和元類的所有方法進行添加。關鍵的 __attribute__((constructor)) 函數如下:
__attribute__((constructor)) static void _append_default_implement_method_to_class() {
unsigned classCount;
Class *classes = objc_copyClassList(&classCount);
//第一層遍歷所有的類
for (int i = 0; i < classCount; i ++) {
Class class = classes[i];
Class metaClass = object_getClass(class);
unsigned protocolCount;
Protocol * __unsafe_unretained *protocols = class_copyProtocolList(class, &protocolCount);
//第二層遍歷類中所有的協議
for (int j = 0; j < protocolCount; j ++) {
Protocol *protocol = protocols[j];
NSString *tempClassName = [NSString stringWithFormat:@"%sTemporaryClass", protocol_getName(protocol)];
Class tempClass = objc_getClass(tempClassName.UTF8String);
if (!tempClass) continue;
unsigned methodCount;
Method *methods = class_copyMethodList(tempClass, &methodCount);
//第三層遍歷臨時類的所有方法并添加
for (int k = 0; k < methodCount; k ++) {
Method method = methods[k];
class_addMethod(class, method_getName(method), method_getImplementation(method), method_getTypeEncoding(method));
}
free(methods);
Class metaTempClass = object_getClass(tempClass);
unsigned metaMethodCount;
Method *metaMethods = class_copyMethodList(metaTempClass, &metaMethodCount);
//第三層遍歷臨時類元類的所有方法并添加
for (int k = 0; k < metaMethodCount; k ++) {
Method method = metaMethods[k];
class_addMethod(metaClass, method_getName(method), method_getImplementation(method), method_getTypeEncoding(method));
}
free(metaMethods);
}
free(protocols);
}
free(classes);
}
這樣,任何一個遵從 Printable 協議的類,即使沒有實現 desc 方法,也具有了 desc 的默認實現。
以上包含了所有為 Objective-C 提供協議默認實現功能的完整代碼,核心是 __attribute__((constructor)) 修飾的函數,利用運行時來為類和元類添加方法,還定義了一個簡單的宏方便代碼書寫。如果有別的意見,歡迎與我交流。