鏈?zhǔn)轿募善髟矸治觯ㄒ?

在OC里面實(shí)現(xiàn)鏈?zhǔn)骄幊汤赐ィ梢允褂梅祷卣{(diào)用者自身來(lái)實(shí)現(xiàn)。但是類有很多穿挨,每個(gè)類也有很多方法月弛,假如要實(shí)現(xiàn)鏈?zhǔn)骄幊蹋瑒t需要每一個(gè)方法進(jìn)行命令與實(shí)現(xiàn)科盛,工作量之大可想而知帽衙。

事實(shí)是雖然工作量巨大,但是卻充滿了吸引力贞绵。

之前見(jiàn)過(guò)有人將UIKitFoundation框架中的大部分類一個(gè)個(gè)的都通過(guò)手工的方式添加上了鏈?zhǔn)骄幊痰墓δ芾髀堋Q笱鬄⒌膶懥撕芏啵坏貌慌宸@種愚公精神榨崩,然而仔細(xì)看代碼的話會(huì)發(fā)現(xiàn)其實(shí)有很多一樣的地方谴垫,而這些一樣的地方是可以提煉出來(lái)的。

懶惰是一個(gè)程序員的美德母蛛。

今天要來(lái)介紹一個(gè)非常有意思的框架翩剪,這個(gè)框架會(huì)幫助我們自動(dòng)的生成那些鏈?zhǔn)轿募恍枰覀冇卸嗲趭^彩郊,挨個(gè)的去寫實(shí)現(xiàn)前弯,只需要我們傳入我們想要進(jìn)行轉(zhuǎn)換的類名數(shù)組就可以了。

首先老規(guī)矩秫逝,框架地址博杖。

關(guān)于怎么使用可以具體看作者的readme,里面講的比較詳細(xì)了筷登,然而就像作者其他的作品一樣剃根,代碼里面注釋依然是少得可憐。筆者也是斷斷續(xù)續(xù)的看了兩個(gè)星期前方,才對(duì)這個(gè)代碼的大致流程梳理的比較清晰了狈醉。

下面是筆者整理的程序流程圖,水平有限惠险,不準(zhǔn)確之處還望指出苗傅。

part1-整體流程圖.png

流程圖中可以看出,當(dāng)我們輸入我們的類數(shù)組后班巩,首先會(huì)進(jìn)到一個(gè)循環(huán)里去渣慕,如果這個(gè)類是OC類的話那么就會(huì)被加入到新的數(shù)組中嘶炭,并且也會(huì)將它的父類加入到新數(shù)組中,總之第一步就是對(duì)要處理的類進(jìn)行處理逊桦。

然后輸出一個(gè)NSMutableSet眨猎,我們要生成的鏈?zhǔn)筋悾褪沁@里面的强经。

緊接著就是開(kāi)始根據(jù)類生成鏈?zhǔn)轿募怂悖懙搅薽ac的桌面上。并且生成的文件主要是兩類匿情,一類是MLChainObject類型的兰迫,另一類是Object+MLChain類型的。這兩類的作用不同炬称,但又是互相緊密聯(lián)系的汁果。

由于代碼量巨大,邏輯復(fù)雜玲躯,所以在一章篇幅里是很難將它解釋的明白的据德。(也可以解釋,但可能會(huì)丟掉好多有營(yíng)養(yǎng)的東西)府蔗。今天我們主要看看在mac桌面生成普通鏈?zhǔn)轿募拇笾逻^(guò)程晋控。

還是先看下筆者畫的流程圖汞窗。(強(qiáng)烈建議原作者做一點(diǎn)代碼的解釋工作姓赤。。仲吏。)

生成普通的鏈?zhǔn)轿募?png

可以看到我們將處理后的類名數(shù)組傳進(jìn)去不铆,就會(huì)以類名作為條件生成對(duì)應(yīng)的頭文件(.h)、實(shí)現(xiàn)文件(.m)裹唆、橋梁類名誓斥、以及父類名。然后根據(jù)這些條件創(chuàng)建我們專門用來(lái)生成代碼文件的模型CodeModel许帐,最后通過(guò)NSFileManager文件管理工具劳坑,將文件的代碼字符串寫到mac桌面文件中。

        [[NSFileManager defaultManager] writefileString:model.hFileResultString ToFileWithDiretory:XcodeCreateCodeDirectory fileName:chainClassName fileType:kML_CreateCodeFileType_h moveToTrashWhenFileExists:YES];
        
        [[NSFileManager defaultManager] writefileString:model.mFileResultString ToFileWithDiretory:XcodeCreateCodeDirectory fileName:chainClassName fileType:kML_CreateCodeFileType_m moveToTrashWhenFileExists:YES];

下面來(lái)看一下普通鏈?zhǔn)筋^文件(.h文件)是如何生成的成畦,還是看流程圖:

生成普通.h文件流程圖.png

首先判斷該類是不是NSObject基類距芬,然后對(duì)應(yīng)生成不同的屬性,屬性的目的是為了獲取當(dāng)前鏈?zhǔn)綄?duì)象所綁定的的原生對(duì)象循帐。同時(shí)運(yùn)用runtime獲取傳入類的所有方法框仔,并對(duì)方法進(jìn)行過(guò)濾,主要就是去掉私有方法拄养、轉(zhuǎn)換set离斩、get方法、過(guò)濾鏈?zhǔn)椒椒ǖ龋缓箝_(kāi)始循環(huán)遍歷方法數(shù)組中的每一個(gè)方法跛梗,運(yùn)用適配器將方法都轉(zhuǎn)換成鏈?zhǔn)椒椒ㄑ傲螅鋵?shí)主要就是加了一個(gè)前綴,用以區(qū)別茄袖。其中比較難理解的就是生成鏈?zhǔn)胶甓x(ChainMacroDefines)的過(guò)程操软,生成了鏈?zhǔn)降暮甓x,然后再將該宏定義與諸如(MLChainClass*(^)())chainSelName的鏈?zhǔn)椒椒ㄟM(jìn)行拼接宪祥。比如這樣:

#ifndef numberOfLines                                  
#define numberOfLines(...)  numberOfLines(@"setNumberOfLines:", (long long)metamacro_at(0, __VA_ARGS__))                                  
#endif
/**     ClassName-> UILabel                                
SEL:   setNumberOfLines: 'q'
     */
- (MLChain4UILabel *(^)())numberOfLines;

按照此種方式對(duì)該類所有的方法都進(jìn)行處理聂薪,并加入到methodAndMacro數(shù)組中,等遍歷完該類中的所有的方法時(shí)蝗羊,就將該數(shù)組中的所有元素進(jìn)行拼接藏澳,返回該類所有符合條件方法的宏定義與對(duì)應(yīng)的鏈?zhǔn)椒椒钠唇幼址?/p>

到這里.h文件的主要內(nèi)容基本就完成了。然后就是再讓它拼接屬性字符串耀找,以構(gòu)造完整的content翔悠。

總之.h文件里生成了該類及其父類方法所對(duì)應(yīng)的鏈?zhǔn)椒椒?并定義了用來(lái)接收可變參數(shù)的宏定義。宏定義的名字跟鏈?zhǔn)椒椒粯右懊ⅲ@樣我們?cè)谑褂命c(diǎn)語(yǔ)法調(diào)用鏈?zhǔn)椒椒ǖ臅r(shí)候蓄愁,實(shí)際上就會(huì)進(jìn)入到宏中,而在這個(gè)宏中會(huì)接收一個(gè)可變的參數(shù)列表狞悲。如下面代碼所指示的:

#ifndef addTarget_action_forControlEvents                                  
#define addTarget_action_forControlEvents(...)  addTarget_action_forControlEvents(@"addTarget:action:forControlEvents:", metamacro_at(0, __VA_ARGS__), metamacro_at(1, __VA_ARGS__), (long long)metamacro_at(2, __VA_ARGS__))                                  
#endif
/**     ClassName-> UIButton                                
SEL:   addTarget: '@'
  action: ':'
  forControlEvents: 'Q'
     */
- (MLChain4UIButton *(^)())addTarget_action_forControlEvents;

當(dāng)調(diào)用obj. mlc_make.addTarget_action_forControlEvents(self,SEL action, UIControlEventTouchUpInside)的時(shí)候撮抓,其實(shí)最終會(huì)進(jìn)入NSObject+ChainInvocation.h分類中的方法- (instancetype (^)(NSString *selName, ...))mlc_rootChainMethod,在這里我們會(huì)獲取到鏈?zhǔn)綄?duì)象所綁定的原生對(duì)象與傳入的可變參數(shù)摇锋,最后轉(zhuǎn)換為原生對(duì)象調(diào)用原生的方法丹拯。(更加詳細(xì)的解析會(huì)在下一篇文章中)

如何生成文件,那就是事先你需要將該文件的具體內(nèi)容荸恕,按照規(guī)則乖酬,拼接成一個(gè)具體的字符串,然后在通過(guò)NSFileManager寫到桌面就可以了融求。

既然鏈?zhǔn)筋愔姓{(diào)用所有的方法最終都會(huì)轉(zhuǎn)換為原生類調(diào)用原生的方法咬像,所以在鏈?zhǔn)筋惖膶?shí)現(xiàn)文件中,就沒(méi)有必要寫具體的實(shí)現(xiàn)了生宛。這就是這個(gè)框架設(shè)計(jì)的巧妙之處县昂。

看了普通鏈?zhǔn)筋^文件中的內(nèi)容,下面簡(jiǎn)單的看一下普通鏈?zhǔn)綄?shí)現(xiàn)文件中的代碼:

/**
 m文件內(nèi)容
 
 @param className <#className description#>
 @return <#return value description#>
 */
+ (NSString *)mlc_mFileContentStrWithClassName:(NSString *)className{
    NSMutableArray *resultStrs = [[NSMutableArray alloc] init];
 
    if ([className isEqualToString:@"NSObject"]) {
        NSString *  mfileContentString = @"+ (void)load{\n\
        \n  [self mlc_setUpMethodDynamically];\
        \n}";
         [resultStrs addObject:mfileContentString];
    }else{
      NSString *  mfileContentString = @"+ (void)load{\n\
        \n  [self mlc_setUpMethodDynamically];\
        \n}";
        NSString *chainObjectMethod =
        [NSString stringWithFormat:
         @"- (%@ *)chainObject{\
         \n    return (id)[super chainObject];\
         \n}", className];
        [resultStrs addObject:mfileContentString];
        [resultStrs addObject:chainObjectMethod];
    }
    return [resultStrs componentsJoinedByString:@"\n"];
    
}

實(shí)現(xiàn)文件的代碼比較簡(jiǎn)單茅糜,簡(jiǎn)單講就是添加了兩個(gè)方法七芭,一個(gè)是+ (void)load方法,另一個(gè)是獲取該鏈?zhǔn)筋愃壎ǖ脑惖膅et方法蔑赘。
load方法會(huì)在main函數(shù)前就調(diào)用狸驳,給鏈?zhǔn)筋愔械乃蟹椒ㄗ鲆粋€(gè)動(dòng)態(tài)添加方法的操作预明,并且以- (instancetype (^)(NSString *selName, ...))mlc_rootChainMethod作為它們的方法實(shí)現(xiàn),也就是說(shuō)當(dāng)調(diào)用鏈?zhǔn)椒椒ǖ臅r(shí)候耙箍,就會(huì)進(jìn)到- (instancetype (^)(NSString *selName, ...))mlc_rootChainMethod中撰糠,而在這個(gè)方法里,我們會(huì)讓鏈?zhǔn)綄?duì)象所綁定的原生對(duì)象調(diào)用鏈?zhǔn)椒椒ㄋ鶎?duì)應(yīng)的原生方法辩昆。

關(guān)于OBJ+MLChain分類文件的生成

分類里面的代碼是比較少的阅酪,也是比較容易理解的。主要就是為了當(dāng)調(diào)用obj.mlc_make的時(shí)候能夠返回一個(gè)橋梁對(duì)象汁针,同時(shí)也是為了模仿Masonry的設(shè)置方式术辐,作者在這里添加了類方法與實(shí)例方法。(這里就不做過(guò)多的解釋了施无。)
頭文件代碼如下:

+ (NSString *)mlc_methodStringInCategory
{
    NSMutableString *resultString = [[NSMutableString alloc] init];
    NSString *methodString1 = [NSString stringWithFormat:@"+ (MLChain4%@ *)mlc_make;\n\n", NSStringFromClass(self)];
    NSString *methodString2 = [NSString stringWithFormat:@"- (MLChain4%@ *)mlc_make;\n\n", NSStringFromClass(self)];
    NSString *methodString3 = [NSString stringWithFormat:@"+ (MLChain4%@ *)mlc_makeConfigs:(void(^)(MLChain4%@ *maker))block;\n\n", NSStringFromClass(self), NSStringFromClass(self)];
    NSString *methodString4 = [NSString stringWithFormat:@"- (MLChain4%@ *)mlc_makeConfigs:(void(^)(MLChain4%@ *maker))block;\n\n", NSStringFromClass(self), NSStringFromClass(self)];
    [resultString appendString:methodString1];
    [resultString appendString:methodString2];
    [resultString appendString:methodString3];
    [resultString appendString:methodString4];
    return resultString;
    
}

以上所講的就是普通鏈?zhǔn)轿募c分類文件的大致生成過(guò)程辉词,并簡(jiǎn)要的講了下代碼是如何進(jìn)行調(diào)用的。下篇文章會(huì)對(duì)這個(gè)框架的幾個(gè)難點(diǎn)進(jìn)行分析猾骡,一個(gè)是方法宏定義的生成瑞躺、另一個(gè)是鏈?zhǔn)椒椒ǖ恼{(diào)用過(guò)程,其中關(guān)于調(diào)用過(guò)程里作者用到了YYKit中的一個(gè)重要的方法兴想,下篇文章也會(huì)進(jìn)行闡述幢哨。

最后建議大伙自己去demo中查看,可以說(shuō)這是一個(gè)設(shè)計(jì)非常巧妙的架構(gòu)嫂便,可以學(xué)到很多的東西(喜歡的記得給作者star哈)捞镰。筆者也會(huì)持續(xù)的對(duì)它進(jìn)行分析并繪制相關(guān)的程序流程圖,希望能幫助小伙伴理解顽悼,當(dāng)然有錯(cuò)誤的話還望指正出來(lái)曼振。

另外一篇關(guān)于這個(gè)框架的原理分析已經(jīng)寫好了几迄,這里是傳送門蔚龙。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市映胁,隨后出現(xiàn)的幾起案子木羹,更是在濱河造成了極大的恐慌,老刑警劉巖解孙,帶你破解...
    沈念sama閱讀 222,464評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件坑填,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡弛姜,警方通過(guò)查閱死者的電腦和手機(jī)脐瑰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,033評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)廷臼,“玉大人苍在,你說(shuō)我怎么就攤上這事绝页。” “怎么了寂恬?”我有些...
    開(kāi)封第一講書人閱讀 169,078評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵续誉,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我初肉,道長(zhǎng)酷鸦,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 59,979評(píng)論 1 299
  • 正文 為了忘掉前任牙咏,我火速辦了婚禮臼隔,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘妄壶。我一直安慰自己躬翁,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,001評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布盯拱。 她就那樣靜靜地躺著盒发,像睡著了一般。 火紅的嫁衣襯著肌膚如雪狡逢。 梳的紋絲不亂的頭發(fā)上宁舰,一...
    開(kāi)封第一講書人閱讀 52,584評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音奢浑,去河邊找鬼蛮艰。 笑死,一個(gè)胖子當(dāng)著我的面吹牛雀彼,可吹牛的內(nèi)容都是我干的壤蚜。 我是一名探鬼主播,決...
    沈念sama閱讀 41,085評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼徊哑,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼袜刷!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起莺丑,我...
    開(kāi)封第一講書人閱讀 40,023評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤著蟹,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后梢莽,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體萧豆,經(jīng)...
    沈念sama閱讀 46,555評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,626評(píng)論 3 342
  • 正文 我和宋清朗相戀三年昏名,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了涮雷。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,769評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡轻局,死狀恐怖洪鸭,靈堂內(nèi)的尸體忽然破棺而出膜钓,到底是詐尸還是另有隱情,我是刑警寧澤卿嘲,帶...
    沈念sama閱讀 36,439評(píng)論 5 351
  • 正文 年R本政府宣布颂斜,位于F島的核電站,受9級(jí)特大地震影響拾枣,放射性物質(zhì)發(fā)生泄漏沃疮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,115評(píng)論 3 335
  • 文/蒙蒙 一梅肤、第九天 我趴在偏房一處隱蔽的房頂上張望司蔬。 院中可真熱鬧,春花似錦姨蝴、人聲如沸俊啼。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,601評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)授帕。三九已至,卻和暖如春浮梢,著一層夾襖步出監(jiān)牢的瞬間跛十,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,702評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工秕硝, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留芥映,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,191評(píng)論 3 378
  • 正文 我出身青樓远豺,卻偏偏與公主長(zhǎng)得像奈偏,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子躯护,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,781評(píng)論 2 361

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