OC協(xié)議的默認(rèn)實現(xiàn)

Swift 中梅桩,定義協(xié)議 protocol 時士嚎,可以使用 extension 給它的某些方法提供默認(rèn)實現(xiàn):

protocol Printable {
    func print()
    func print1()
}
extension Printable {
    func print() {
        print("default print")
    }
}

有了上面的代碼,當(dāng)創(chuàng)建一個遵從 Printable 協(xié)議的類或者是結(jié)構(gòu)體時豺型,就能獲得 print() 方法徒像。可以在需要的時候重新定義這個方法笨枯,如果不重新定義,就會使用這個默認(rèn)方法遇西。

遺憾的是 Objective-C 并沒有提供這樣的便利馅精。當(dāng)我們想為一個協(xié)議提供默認(rèn)實現(xiàn)時,通常會提供一個基類粱檀,基類遵從協(xié)議并且提供默認(rèn)實現(xiàn)洲敢。如果不想使用默認(rèn)實現(xiàn),需繼承基類茄蚯,在子類中重寫方法压彭。有沒有辦法可以像 Swift 那樣睦优,在協(xié)議聲明的地方而不需要繼承鏈,就能簡便的提供默認(rèn)實現(xiàn)呢壮不?

為協(xié)議提供默認(rèn)實現(xiàn)汗盘,可以理解為所有遵從協(xié)議的類都擁有提前定義的協(xié)議方法。Objective-C 強(qiáng)大的運行時可以獲得程序中所有的類询一,可以獲得類遵從的協(xié)議隐孽,還可以為每一個類添加方法。我們可以用一個臨時類來實現(xiàn)協(xié)議的方法健蕊,在所有的類自己的方法全部加載完后菱阵,為遵從協(xié)議的類添加臨時類的默認(rèn)方法。之所以要在類自己的方法加載完成后缩功,是為了防止類自己的實現(xiàn)被覆蓋晴及,當(dāng)方法已經(jīng)存在時,class_addMethod 會添加失敗嫡锌,這正是我們希望的虑稼。

__attribute__((constructor)) 特性修飾的函數(shù),會在 runtime 加載完所有類之后世舰,main 函數(shù)執(zhí)行之前運行动雹。通過這樣一個函數(shù),將滿足條件的類加上提供的默認(rèn)實現(xiàn)跟压。

現(xiàn)在需要一個臨時類胰蝠,來為協(xié)議實現(xiàn)默認(rèn)方法。假設(shè)有協(xié)議:

@protocol Printable
@optional
- (NSString *)desc;
@end

為協(xié)議提供臨時類震蒋,來提供默認(rèn)實現(xiàn):

@interface PrintableTemporaryClass : NSObject <Printable>
@end 
@implementation PrintableTemporaryClass
- (NSString *)desc {
            return @"Default desc";
}
@end

上面代碼中茸塞,print 方法前的部分對所有協(xié)議來說都是類似的的,可以定義個宏來簡化代碼:

#define extensionProtocol(NAME) \
interface NAME ## TemporaryClass : NSObject <NAME> \
@end \
@implementation NAME ## TemporaryClass \

此時查剖,臨時類的寫法會變成下面這樣:

@extensionProtocol(Printable)
- (NSString *)desc {
            return @"Default desc";
}
@end

然后通過遍歷所有的類钾虐,進(jìn)而遍歷類所有的協(xié)議,并查找運行時中是否有協(xié)議對應(yīng)的臨時類(通過名字關(guān)聯(lián))笋庄,如果有效扫,遍歷該臨時類和元類的所有方法進(jìn)行添加。關(guān)鍵的 __attribute__((constructor)) 函數(shù)如下:

__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);
        //第二層遍歷類中所有的協(xié)議
        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 協(xié)議的類菌仁,即使沒有實現(xiàn) desc 方法,也具有了 desc 的默認(rèn)實現(xiàn)静暂。

以上包含了所有為 Objective-C 提供協(xié)議默認(rèn)實現(xiàn)功能的完整代碼济丘,核心是 __attribute__((constructor)) 修飾的函數(shù),利用運行時來為類和元類添加方法,還定義了一個簡單的宏方便代碼書寫摹迷。如果有別的意見疟赊,歡迎與我交流。

Github 地址: https://github.com/Jeffery91/ExtensionProtocol

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末峡碉,一起剝皮案震驚了整個濱河市近哟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌异赫,老刑警劉巖椅挣,帶你破解...
    沈念sama閱讀 222,729評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異塔拳,居然都是意外死亡鼠证,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評論 3 399
  • 文/潘曉璐 我一進(jìn)店門靠抑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來量九,“玉大人,你說我怎么就攤上這事颂碧≤校” “怎么了?”我有些...
    開封第一講書人閱讀 169,461評論 0 362
  • 文/不壞的土叔 我叫張陵载城,是天一觀的道長肌似。 經(jīng)常有香客問我,道長诉瓦,這世上最難降的妖魔是什么川队? 我笑而不...
    開封第一講書人閱讀 60,135評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮睬澡,結(jié)果婚禮上固额,老公的妹妹穿的比我還像新娘。我一直安慰自己煞聪,他們只是感情好斗躏,可當(dāng)我...
    茶點故事閱讀 69,130評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著昔脯,像睡著了一般啄糙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上云稚,一...
    開封第一講書人閱讀 52,736評論 1 312
  • 那天迈套,我揣著相機(jī)與錄音,去河邊找鬼碱鳞。 笑死,一個胖子當(dāng)著我的面吹牛踱蛀,可吹牛的內(nèi)容都是我干的窿给。 我是一名探鬼主播贵白,決...
    沈念sama閱讀 41,179評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼崩泡!你這毒婦竟也來了禁荒?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,124評論 0 277
  • 序言:老撾萬榮一對情侶失蹤角撞,失蹤者是張志新(化名)和其女友劉穎呛伴,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體谒所,經(jīng)...
    沈念sama閱讀 46,657評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡热康,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,723評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了劣领。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片姐军。...
    茶點故事閱讀 40,872評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖尖淘,靈堂內(nèi)的尸體忽然破棺而出奕锌,到底是詐尸還是另有隱情,我是刑警寧澤村生,帶...
    沈念sama閱讀 36,533評論 5 351
  • 正文 年R本政府宣布惊暴,位于F島的核電站,受9級特大地震影響趁桃,放射性物質(zhì)發(fā)生泄漏辽话。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,213評論 3 336
  • 文/蒙蒙 一镇辉、第九天 我趴在偏房一處隱蔽的房頂上張望屡穗。 院中可真熱鬧,春花似錦忽肛、人聲如沸村砂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽础废。三九已至,卻和暖如春罕模,著一層夾襖步出監(jiān)牢的瞬間评腺,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評論 1 274
  • 我被黑心中介騙來泰國打工淑掌, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留蒿讥,地道東北人。 一個月前我還...
    沈念sama閱讀 49,304評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像芋绸,于是被迫代替她去往敵國和親媒殉。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,876評論 2 361

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,332評論 25 707
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理摔敛,服務(wù)發(fā)現(xiàn)廷蓉,斷路器,智...
    卡卡羅2017閱讀 134,716評論 18 139
  • importUIKit classViewController:UITabBarController{ enumD...
    明哥_Young閱讀 3,823評論 1 10
  • *面試心聲:其實這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個offer,總結(jié)起來就是把...
    Dove_iOS閱讀 27,168評論 30 470
  • 當(dāng)傷心時只有我一個人,沒有朋友安慰行楞,這樣的次數(shù)增多了以后攒暇,我開始羨慕那些可以輕易面對面看到朋友,可以當(dāng)天傾訴的學(xué)生...
    飛魚kiwi閱讀 592評論 6 4