鏈?zhǔn)轿募善髦仉y點(diǎn)解析(二)

接上篇文章传蹈,里面主要講述了普通鏈?zhǔn)轿募c分類鏈?zhǔn)轿募纳蛇^程距境。今天講一下里面的幾個(gè)技術(shù)難點(diǎn)央拖。

一:為什么要構(gòu)造鏈?zhǔn)椒椒ê甓x以及如何構(gòu)造祭阀。

思考一個(gè)問題鹉戚,當(dāng)我們給類添加了鏈?zhǔn)椒椒ê螅热?code>- (MLChain4UIButton *(^)())addTarget_action_forControlEvents;其實(shí)我們就已經(jīng)可以進(jìn)行調(diào)用了专控,比如這樣

UIButton.mlc_make. addTarget_action_forEvents(self, @selector(buttonClicked:), UIControlEventTouchUpInside)

這樣寫是不會報(bào)錯的抹凳,但是因?yàn)槲覀儧]有給這個(gè)鏈?zhǔn)椒椒ㄟM(jìn)行相應(yīng)的實(shí)現(xiàn),所以它是不會產(chǎn)生任何效果的伦腐。而如果親自動手給每一個(gè)鏈?zhǔn)椒椒ǘ继砑訉?shí)現(xiàn)的話赢底,就偏離了我們的初衷。

仔細(xì)想一想柏蘑,其實(shí)它的實(shí)現(xiàn)跟原生類里面的方法addTarget:(nullable id) action:(nonnull SEL) forControlEvents:(UIControlEvents)的實(shí)現(xiàn)是一模一樣的幸冻,所以如果我們在調(diào)用鏈?zhǔn)椒椒ǖ臅r(shí)候能夠讓這個(gè)鏈?zhǔn)綄ο笏鶎?yīng)的原生對象去響應(yīng)它的原生方法的話,那么就能實(shí)現(xiàn)我們想要的效果了咳焚,而這時(shí)候我們也不需要寫任何的實(shí)現(xiàn)代碼洽损。

那么怎么實(shí)現(xiàn)這個(gè)功能呢粥脚,首先我們要知道的是班套,鏈?zhǔn)椒椒c原生方法名是可以按照固定的規(guī)則進(jìn)行相互的轉(zhuǎn)換的讹剔,而鏈?zhǔn)綄ο笈c原生對象也是可以按照固定規(guī)則進(jìn)行相互的轉(zhuǎn)換握侧。也就是說知道了一個(gè)就可以推算出另一個(gè)泊窘。這也是代碼中NSObject+ChainInfoAdaptor分類所做的事情叶骨,它像是一個(gè)適配器一樣匹层,可以根據(jù)你的需要將鏈?zhǔn)椒椒D(zhuǎn)換成原生方法名等极谊,反之亦然赏胚。而如果我們知道了原生的類访娶,原生的方法,以及方法的參數(shù)的話觉阅,那么我們就可以根據(jù)NSInvocation逐一根據(jù)數(shù)據(jù)類型設(shè)置崖疤,最后使用[Invocation invoke]來手動的觸發(fā)這個(gè)方法。

所以我們在調(diào)用鏈?zhǔn)椒椒ǖ臅r(shí)候典勇,希望可以將這個(gè)方法所對應(yīng)的原生類劫哼、原生方法、以及它的各個(gè)參數(shù)都傳遞出去割笙,讓另一個(gè)專門的類(NSObject+ChainInvocation)對這些進(jìn)行統(tǒng)一處理权烧,最后實(shí)現(xiàn)原生對象調(diào)用原生方法的效果。

這時(shí)候僅僅通過一個(gè)鏈?zhǔn)椒椒ㄊ菬o法實(shí)現(xiàn)的伤溉,因此需要一個(gè)更巧妙的設(shè)計(jì)般码。

我們知道如果定義一個(gè)形如addTarget_action_forControlEvents(...)跟鏈?zhǔn)椒椒ㄍ暮甓x的話,那么我們再寫上面的UIButton.mlc_make. addTarget_action_forEvents(self, @selector(buttonClicked:), UIControlEventTouchUpInside)的時(shí)候乱顾,程序就會進(jìn)到那個(gè)宏里面去板祝,這是我們必須清楚地一件事情。

知道了這個(gè)前提走净,我們就可以在這上面的宏中做文章了券时。思考一下如何使用這個(gè)宏傳出去原生方法孤里、方法的參數(shù)(個(gè)數(shù)不確定)和原生的類(這個(gè)可以在外面再獲取)橘洞,由于原生方法在生成鏈?zhǔn)椒椒ǖ臅r(shí)候是已經(jīng)知道了的捌袜,因?yàn)槲覀兪峭ㄟ^原生方法映射出鏈?zhǔn)椒椒ǖ模晕覀兛梢栽谧詣由晌募臅r(shí)候就將原生方法名作為宏中已知的默認(rèn)第一項(xiàng)炸枣,而外界輸入的可變參數(shù)作為第二個(gè)大項(xiàng)(可以通過RAC中的宏定義metamacro_at(N, ...)來依次獲取參數(shù)的值)對外進(jìn)行傳遞虏等。所以我們需要的宏定義如下:

#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__))                                  

因此我們在自動生成宏定義的時(shí)候圍繞上面的原則進(jìn)行生成就可以了,需要在注意一下參數(shù)的類型适肠。關(guān)鍵代碼如下:

macroDefineString = [NSString stringWithFormat: @"#ifndef %@\
 \n#define %@(...)  %@(@\"%@\", %@)\
 \n#endif", chainSelName, chainSelName, chainSelName, selName, [mulArgStrs componentsJoinedByString:@", "]];
//chainSelName:鏈?zhǔn)椒椒┢洌瑂elName:原生方法名,mulArgStrs:該方法的參數(shù)類型數(shù)組迂猴。

二:如何在調(diào)用鏈?zhǔn)椒椒ǖ臅r(shí)候,轉(zhuǎn)為讓原生的對象去響應(yīng)原生的方法背伴。

定義好了統(tǒng)一的鏈?zhǔn)胶甓x后沸毁,我們希望能夠?qū)⒑曛械哪切l件都傳到一個(gè)固定的方法中進(jìn)行處理。也就是說在調(diào)用鏈?zhǔn)椒椒ǖ臅r(shí)候傻寂,無論是什么息尺,它的實(shí)現(xiàn)都能夠進(jìn)到另一個(gè)固定的方法中。
為此我們在每一個(gè)類中的+(void) load;方法(這個(gè)方法的執(zhí)行在main函數(shù)前)中給每一個(gè)鏈?zhǔn)椒椒ǘ歼M(jìn)行了動態(tài)的綁定疾掰,并將它們方法的實(shí)現(xiàn)轉(zhuǎn)到一個(gè)固定的方法中搂誉。

+ (void)mlc_setUpMethodDynamically{
    Class originalClass = MLCOriginalClass(self);//獲取鏈?zhǔn)筋愃鶎?yīng)的原生類
    Class chainBridgeClass = self;                            //當(dāng)前鏈?zhǔn)筋?    Method desMethod =  class_getInstanceMethod([originalClass class], @selector(mlc_rootChainMethod));      //mlc_rootChainMethod是我們用來統(tǒng)一處理所有鏈?zhǔn)椒椒ǖ模墟準(zhǔn)椒椒ǘ紩哌@個(gè)方法静檬。
    IMP imp = method_getImplementation(desMethod);//獲取mlc_rootChainMethod的實(shí)現(xiàn)
    for (NSString *methodName in [originalClass mlc_noReturnValueSelNames]) {
        NSString *chainMethodName = [originalClass mlc_chainSelNameWithOringalSelName:methodName];//將原生方法名轉(zhuǎn)換為鏈?zhǔn)椒椒?        class_addMethod([chainBridgeClass class], sel_registerName(chainMethodName.UTF8String), imp, method_getTypeEncoding(desMethod));//給鏈?zhǔn)筋悇討B(tài)添加鏈?zhǔn)椒椒ㄌ堪茫⑶覍lc_rootChainMethod方法的實(shí)現(xiàn)作為它們的統(tǒng)一實(shí)現(xiàn)。
    }
}

下面看一下所有鏈?zhǔn)椒椒ǖ淖罱K實(shí)現(xiàn):mlc_rootChainMethod方法拂檩。這是在NSObject+ChainInvocation分類中定義的方法侮腹。

- (instancetype (^)(NSString *selName, ...))mlc_rootChainMethod{
    return ^ id (NSString *selName, ...){
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
        id chainObject = [self performSelector:@selector(chainObject)];
#pragma clang diagnostic pop
        NSMethodSignature *sig = [chainObject methodSignatureForSelector:sel_registerName(selName.UTF8String)];
        NSInvocation *inv = [NSInvocation invocationWithMethodSignature:sig];
        [inv setTarget:chainObject];
        [inv setSelector:sel_registerName(selName.UTF8String)];
        va_list args;
        va_start(args, selName);
        [NSObject mlc_setInv:inv withSig:sig andArgs:args];
         va_end(args);
        [inv invoke];
        return self;
    };
    
}

在這里這個(gè)方法的block參數(shù)第一項(xiàng)就是原生類的原始方法,而后面接收一個(gè)可變參數(shù)的列表稻励,也就是我們從宏定義中獲取到的參數(shù)父阻。
根據(jù)這些條件,我們就可以讓原生對象調(diào)用原生的方法了望抽。

三:再梳理一下程序的流程

1加矛、在生成文件的時(shí)候,讓鏈?zhǔn)椒椒c鏈?zhǔn)胶甓x的名字一樣煤篙。并在宏定義中將原生方法名作為第一項(xiàng)默認(rèn)參數(shù)斟览,輸入的方法變量作為可變參數(shù)列表這個(gè)第二個(gè)大項(xiàng)。鏈?zhǔn)胶昙胺椒ㄈ缦拢?/p>

#define chainSelName(...)  chainSelName(@"originalSelName", [mulArgStrs componentsJoinedByString:@", "]])
//chainSelName:鏈?zhǔn)椒椒Ⅲ riginalSelName:原生方法趣惠、[mulArgStrs componentsJoinedByString:@", "]]方法參數(shù)類型宏定義狸棍。

- (MLChain4Object *(^)()) chainSelName;

2、程序啟動時(shí)味悄,給所有的鏈?zhǔn)筋惖逆準(zhǔn)椒椒ㄗ鲆粋€(gè)動態(tài)的綁定草戈,并且將NSObject+ChainInvocation分類中的mlc_rootChainMethod方法作為他們的統(tǒng)一實(shí)現(xiàn)。

這樣在書寫形如UIButton.mlc_make. addTarget_action_forEvents(self, @selector(buttonClicked:), UIControlEventTouchUpInside)的時(shí)候侍瑟,就會進(jìn)到- (instancetype (^)(NSString *selName, ...))mlc_rootChainMethod方法中唐片,在這里我們會獲取到原生類、原生方法涨颜、參數(shù)列表费韭,獲取到這些我們就可以手動的觸發(fā)方法了。主要就是使用NSInvocation逐一根據(jù)數(shù)據(jù)類型進(jìn)行設(shè)置庭瑰,設(shè)置的方法setInv:withSig:andArgs:從YYKit中得來星持。篇幅有限,這里就先不做過多的對這個(gè)方法的闡述了弹灭,之后會加入的督暂。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市穷吮,隨后出現(xiàn)的幾起案子逻翁,更是在濱河造成了極大的恐慌,老刑警劉巖捡鱼,帶你破解...
    沈念sama閱讀 222,590評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件八回,死亡現(xiàn)場離奇詭異,居然都是意外死亡驾诈,警方通過查閱死者的電腦和手機(jī)缠诅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來翘鸭,“玉大人滴铅,你說我怎么就攤上這事【团遥” “怎么了汉匙?”我有些...
    開封第一講書人閱讀 169,301評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長生蚁。 經(jīng)常有香客問我噩翠,道長,這世上最難降的妖魔是什么邦投? 我笑而不...
    開封第一講書人閱讀 60,078評論 1 300
  • 正文 為了忘掉前任伤锚,我火速辦了婚禮,結(jié)果婚禮上志衣,老公的妹妹穿的比我還像新娘屯援。我一直安慰自己猛们,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,082評論 6 398
  • 文/花漫 我一把揭開白布狞洋。 她就那樣靜靜地躺著弯淘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪吉懊。 梳的紋絲不亂的頭發(fā)上庐橙,一...
    開封第一講書人閱讀 52,682評論 1 312
  • 那天,我揣著相機(jī)與錄音借嗽,去河邊找鬼态鳖。 笑死,一個(gè)胖子當(dāng)著我的面吹牛恶导,可吹牛的內(nèi)容都是我干的浆竭。 我是一名探鬼主播,決...
    沈念sama閱讀 41,155評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼惨寿,長吁一口氣:“原來是場噩夢啊……” “哼兆蕉!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起缤沦,我...
    開封第一講書人閱讀 40,098評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎易稠,沒想到半個(gè)月后缸废,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,638評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡驶社,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,701評論 3 342
  • 正文 我和宋清朗相戀三年企量,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片亡电。...
    茶點(diǎn)故事閱讀 40,852評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡届巩,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出份乒,到底是詐尸還是另有隱情恕汇,我是刑警寧澤,帶...
    沈念sama閱讀 36,520評論 5 351
  • 正文 年R本政府宣布或辖,位于F島的核電站瘾英,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏颂暇。R本人自食惡果不足惜缺谴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,181評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望耳鸯。 院中可真熱鬧湿蛔,春花似錦膀曾、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至苫纤,卻和暖如春碉钠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背卷拘。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評論 1 274
  • 我被黑心中介騙來泰國打工喊废, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人栗弟。 一個(gè)月前我還...
    沈念sama閱讀 49,279評論 3 379
  • 正文 我出身青樓污筷,卻偏偏與公主長得像,于是被迫代替她去往敵國和親乍赫。 傳聞我的和親對象是個(gè)殘疾皇子瓣蛀,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,851評論 2 361

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

  • 此文章由熱心網(wǎng)友 ccSundayChina授權(quán)轉(zhuǎn)載 接上篇文章,里面主要講述了普通鏈?zhǔn)轿募c分類鏈?zhǔn)轿募纳蛇^...
    luomagaoshou閱讀 422評論 0 0
  • 在OC里面實(shí)現(xiàn)鏈?zhǔn)骄幊汤壮В梢允褂梅祷卣{(diào)用者自身來實(shí)現(xiàn)惋增。但是類有很多,每個(gè)類也有很多方法改鲫,假如要實(shí)現(xiàn)鏈?zhǔn)骄幊陶┟螅瑒t需要...
    ccSundayChina閱讀 1,392評論 7 7
  • 此文章由熱心網(wǎng)友 ccSundayChina授權(quán)轉(zhuǎn)載 在OC里面實(shí)現(xiàn)鏈?zhǔn)骄幊蹋梢允褂梅祷卣{(diào)用者自身來實(shí)現(xiàn)像棘。但是類...
    luomagaoshou閱讀 382評論 0 3
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,321評論 25 707
  • 最近讀到華姿的《萬物有靈皆可師》稽亏,說的是有關(guān)動物的故事,難得可貴的一本說說動物的書缕题,雖是小篇章截歉,但述說的道理淺顯、...
    七月風(fēng)閱讀 651評論 1 4