(轉(zhuǎn)載)鏈式文件生成器原理分析(一)

此文章由熱心網(wǎng)友 ccSundayChina授權(quán)轉(zhuǎn)載

在OC里面實現(xiàn)鏈式編程,可以使用返回調(diào)用者自身來實現(xiàn)配椭。但是類有很多虫溜,每個類也有很多方法,假如要實現(xiàn)鏈式編程股缸,則需要每一個方法進行命令與實現(xiàn)衡楞,工作量之大可想而知。

事實是雖然工作量巨大,但是卻充滿了吸引力瘾境。

之前見過有人將UIKitFoundation框架中的大部分類一個個的都通過手工的方式添加上了鏈式編程的功能歧杏。洋洋灑灑的寫了很多,不得不佩服這種愚公精神迷守,然而仔細看代碼的話會發(fā)現(xiàn)其實有很多一樣的地方犬绒,而這些一樣的地方是可以提煉出來的。

懶惰是一個程序員的美德兑凿。

今天要來介紹一個非常有意思的框架凯力,這個框架會幫助我們自動的生成那些鏈式文件,不需要我們有多勤奮礼华,挨個的去寫實現(xiàn)咐鹤,只需要我們傳入我們想要進行轉(zhuǎn)換的類名數(shù)組就可以了。

首先老規(guī)矩圣絮,框架地址祈惶。

關(guān)于怎么使用可以具體看作者的readme,里面講的比較詳細了扮匠,然而就像作者其他的作品一樣捧请,代碼里面注釋依然是少得可憐。筆者也是斷斷續(xù)續(xù)的看了兩個星期棒搜,才對這個代碼的大致流程梳理的比較清晰了疹蛉。

下面是筆者整理的程序流程圖,水平有限帮非,不準確之處還望指出氧吐。

part1-整體流程圖.png

流程圖中可以看出,當我們輸入我們的類數(shù)組后末盔,首先會進到一個循環(huán)里去,如果這個類是OC類的話那么就會被加入到新的數(shù)組中座慰,并且也會將它的父類加入到新數(shù)組中陨舱,總之第一步就是對要處理的類進行處理。

然后輸出一個NSMutableSet版仔,我們要生成的鏈式類游盲,就是這里面的。

緊接著就是開始根據(jù)類生成鏈式文件了蛮粮,并寫到了mac的桌面上益缎。并且生成的文件主要是兩類,一類是MLChainObject類型的然想,另一類是Object+MLChain類型的莺奔。這兩類的作用不同,但又是互相緊密聯(lián)系的变泄。

由于代碼量巨大令哟,邏輯復雜恼琼,所以在一章篇幅里是很難將它解釋的明白的。(也可以解釋屏富,但可能會丟掉好多有營養(yǎng)的東西)晴竞。今天我們主要看看在mac桌面生成普通鏈式文件的大致過程。

還是先看下筆者畫的流程圖狠半。(強烈建議原作者做一點代碼的解釋工作噩死。。神年。)

生成普通的鏈式文件.png

可以看到我們將處理后的類名數(shù)組傳進去已维,就會以類名作為條件生成對應的頭文件(.h)、實現(xiàn)文件(.m)瘤袖、橋梁類名衣摩、以及父類名。然后根據(jù)這些條件創(chuàng)建我們專門用來生成代碼文件的模型CodeModel捂敌,最后通過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];

下面來看一下普通鏈式頭文件(.h文件)是如何生成的占婉,還是看流程圖:

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

首先判斷該類是不是NSObject基類泡嘴,然后對應生成不同的屬性,屬性的目的是為了獲取當前鏈式對象所綁定的的原生對象逆济。同時運用runtime獲取傳入類的所有方法酌予,并對方法進行過濾,主要就是去掉私有方法奖慌、轉(zhuǎn)換set抛虫、get方法、過濾鏈式方法等简僧,然后開始循環(huán)遍歷方法數(shù)組中的每一個方法建椰,運用適配器將方法都轉(zhuǎn)換成鏈式方法,其實主要就是加了一個前綴岛马,用以區(qū)別棉姐。其中比較難理解的就是生成鏈式宏定義(ChainMacroDefines)的過程,生成了鏈式的宏定義啦逆,然后再將該宏定義與諸如(MLChainClass*(^)())chainSelName的鏈式方法進行拼接伞矩。比如這樣:

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

按照此種方式對該類所有的方法都進行處理,并加入到methodAndMacro數(shù)組中夏志,等遍歷完該類中的所有的方法時乃坤,就將該數(shù)組中的所有元素進行拼接,返回該類所有符合條件方法的宏定義與對應的鏈式方法名的拼接字符串。

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

總之.h文件里生成了該類及其父類方法所對應的鏈式方法,并定義了用來接收可變參數(shù)的宏定義枫吧。宏定義的名字跟鏈式方法名一樣浦旱,這樣我們在使用點語法調(diào)用鏈式方法的時候,實際上就會進入到宏中九杂,而在這個宏中會接收一個可變的參數(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;

當調(diào)用obj. mlc_make.addTarget_action_forControlEvents(self,SEL action, UIControlEventTouchUpInside)的時候,其實最終會進入NSObject+ChainInvocation.h分類中的方法- (instancetype (^)(NSString *selName, ...))mlc_rootChainMethod例隆,在這里我們會獲取到鏈式對象所綁定的原生對象與傳入的可變參數(shù)甥捺,最后轉(zhuǎn)換為原生對象調(diào)用原生的方法。(更加詳細的解析會在下一篇文章中)

如何生成文件镀层,那就是事先你需要將該文件的具體內(nèi)容镰禾,按照規(guī)則,拼接成一個具體的字符串唱逢,然后在通過NSFileManager寫到桌面就可以了吴侦。

既然鏈式類中調(diào)用所有的方法最終都會轉(zhuǎn)換為原生類調(diào)用原生的方法,所以在鏈式類的實現(xiàn)文件中坞古,就沒有必要寫具體的實現(xiàn)了备韧。這就是這個框架設計的巧妙之處。

看了普通鏈式頭文件中的內(nèi)容痪枫,下面簡單的看一下普通鏈式實現(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"];
    
}

實現(xiàn)文件的代碼比較簡單织堂,簡單講就是添加了兩個方法,一個是+ (void)load方法奶陈,另一個是獲取該鏈式類所綁定的原生類的get方法易阳。
load方法會在main函數(shù)前就調(diào)用,給鏈式類中的所有方法做一個動態(tài)添加方法的操作吃粒,并且以- (instancetype (^)(NSString *selName, ...))mlc_rootChainMethod作為它們的方法實現(xiàn)闽烙,也就是說當調(diào)用鏈式方法的時候,就會進到- (instancetype (^)(NSString *selName, ...))mlc_rootChainMethod中声搁,而在這個方法里,我們會讓鏈式對象所綁定的原生對象調(diào)用鏈式方法所對應的原生方法捕发。

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

分類里面的代碼是比較少的疏旨,也是比較容易理解的。主要就是為了當調(diào)用obj.mlc_make的時候能夠返回一個橋梁對象扎酷,同時也是為了模仿Masonry的設置方式檐涝,作者在這里添加了類方法與實例方法。(這里就不做過多的解釋了。)
頭文件代碼如下:

+ (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;
    
}

以上所講的就是普通鏈式文件與分類文件的大致生成過程谁榜,并簡要的講了下代碼是如何進行調(diào)用的幅聘。下篇文章會對這個框架的幾個難點進行分析,一個是方法宏定義的生成窃植、另一個是鏈式方法的調(diào)用過程帝蒿,其中關(guān)于調(diào)用過程里作者用到了YYKit中的一個重要的方法,下篇文章也會進行闡述巷怜。

最后建議大伙自己去demo中查看葛超。可以說這是一個設計非常巧妙的架構(gòu)延塑,可以學到很多的東西绣张,筆者也會持續(xù)的對它進行分析并繪制相關(guān)的程序流程圖,希望能幫助小伙伴理解关带,當然有錯誤的話還望指正出來侥涵。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市宋雏,隨后出現(xiàn)的幾起案子芜飘,更是在濱河造成了極大的恐慌,老刑警劉巖好芭,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件燃箭,死亡現(xiàn)場離奇詭異,居然都是意外死亡舍败,警方通過查閱死者的電腦和手機招狸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來邻薯,“玉大人裙戏,你說我怎么就攤上這事〔薰睿” “怎么了累榜?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長灵嫌。 經(jīng)常有香客問我壹罚,道長,這世上最難降的妖魔是什么寿羞? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任猖凛,我火速辦了婚禮,結(jié)果婚禮上绪穆,老公的妹妹穿的比我還像新娘辨泳。我一直安慰自己虱岂,他們只是感情好,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布菠红。 她就那樣靜靜地躺著第岖,像睡著了一般。 火紅的嫁衣襯著肌膚如雪试溯。 梳的紋絲不亂的頭發(fā)上蔑滓,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天,我揣著相機與錄音耍共,去河邊找鬼烫饼。 笑死,一個胖子當著我的面吹牛试读,可吹牛的內(nèi)容都是我干的杠纵。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼钩骇,長吁一口氣:“原來是場噩夢啊……” “哼比藻!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起倘屹,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤银亲,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后纽匙,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體务蝠,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年烛缔,在試婚紗的時候發(fā)現(xiàn)自己被綠了馏段。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡践瓷,死狀恐怖院喜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情晕翠,我是刑警寧澤喷舀,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站淋肾,受9級特大地震影響硫麻,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜樊卓,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一庶香、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧简识,春花似錦赶掖、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至颈走,卻和暖如春膳灶,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背立由。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工轧钓, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人锐膜。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓毕箍,卻偏偏與公主長得像,于是被迫代替她去往敵國和親道盏。 傳聞我的和親對象是個殘疾皇子而柑,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

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