組件化開發(fā)之組件通信原理(一)

組件化架構(gòu)缀棍、通信方案想必大家都看到很多朴上,蘑菇街李忠casatwy 這兩個(gè)博客收益頗多懂鸵,我先說下開始我的組件通信思路偏螺。


前期我講述了,如何用cocoapods模塊化一個(gè)工程匆光,那么就面臨一個(gè)問題套像。開發(fā)工程師除了能看到基礎(chǔ)模塊和自己寫的模塊之外,看不到別人寫的模塊终息,別人的模塊叫什么名字也不知道夺巩,如何通信呢

我們需要解決三個(gè)問題

  • 視圖展示:比如普通的跳轉(zhuǎn)
  • 參數(shù)傳遞:比如我們到訂單頁面,至少要給這個(gè)頁面一個(gè)訂單號(hào)
  • 回調(diào):一說到回調(diào)周崭,那就是block柳譬、delegate占大多數(shù)了

首先我們解決視圖展示問題

Class class = NSClassFromString(@"CouponViewController");
UIViewController* couponVC = [[class alloc] init];
[self.navigationController pushViewController:couponVC animated:YES];

上面的代碼很容易看懂是我要跳轉(zhuǎn)到CouponViewController頁面

其次我們解決參數(shù)傳遞問題

一提起delegate,原理就簡(jiǎn)單的理解為:比如A跳轉(zhuǎn)到B了续镇,B頁面讓A頁面就調(diào)用一個(gè)方法并傳遞參數(shù)美澳,這里我們正向去考慮 A頁面直接讓B頁面主動(dòng)調(diào)用一個(gè)方法并傳遞參數(shù)這里就以類似delegate的方式實(shí)現(xiàn)了參數(shù)傳遞

回到代碼里

performSelector: withObject:

大家對(duì)上面的方法可能看到的比較多,只要調(diào)用perform開頭的方法摸航,這個(gè)方法就擺在第一位了制跟,今天終于排上用場(chǎng)了。類方法忙厌、實(shí)例方法 都可以調(diào)用的凫岖,滿足各種需求。

比如在A頁面調(diào)整到B頁面逢净,第一步我們想辦法已經(jīng)創(chuàng)建了這個(gè)B對(duì)象了哥放,上面的方法的作用就是讓B去調(diào)用他自己實(shí)現(xiàn)的方法,同時(shí)給傳遞一個(gè)參數(shù)爹土,原理跟delegate一樣

下面是A頁面的代碼甥雕,我們假定有一個(gè)按鈕,點(diǎn)擊后執(zhí)行如下的方法跳轉(zhuǎn)到B頁面并且傳遞一個(gè)參數(shù)

//傳遞參數(shù)
    Class class = NSClassFromString(@"AViewController");
    UIViewController* BVC = [[class alloc] init];
    
    SEL selector = NSSelectorFromString(@"setCouponFilter:");
    if ([BVC respondsToSelector:selector]) {
       [BVC performSelector:selector withObject:@{@"name": @"This is a super order"}];
    }

    [self.navigationController pushViewController: AVC animated:YES];

那么我們?cè)贐頁面實(shí)現(xiàn)一下這個(gè)方法

-(void)setCouponFilter:(NSDictionary *)couponFilter{
    NSLog(@"上個(gè)頁面帶來的參數(shù):%@", couponFilter);
}

那么在B頁面就會(huì)看到輸出這個(gè)參數(shù)了胀茵,參數(shù)你拿到了社露,隨便怎么處理了

最后我們解決回調(diào)問題

block的方式詳見代碼,也類似于傳遞一個(gè)參數(shù)琼娘,參數(shù)為block峭弟。

delegate方式如下

可以定義一個(gè)協(xié)議,同時(shí)也要設(shè)置一個(gè)delegate脱拼,我們假定要A頁面稱為B頁面的delegate瞒瘸,那么以前我們會(huì)在B頁面的接口文件.h中聲明一個(gè)代理,現(xiàn)在聲明也沒有用了熄浓,因?yàn)榭床坏角槌簦∧敲慈绾卧O(shè)置呢?delegate也可以當(dāng)做一個(gè)普通的參數(shù)傳遞,比如在A頁面將self,作為參數(shù)傳遞過去

SEL delegateSEL = NSSelectorFromString(@"setDelegate:");
if ([BVC respondsToSelector:delegateSEL]) {
    [BVC performSelector:delegateSEL withObject:self]; 
    }

B頁面,我們就聲明一個(gè)delegate屬性牙丽,把set方法當(dāng)做我們的傳遞參數(shù)方法

-(void)setDelegate:(id<ModuleDelegate>)delegate{
    _delegate = delegate;
    
    NSLog(@"delegate::%@", delegate);
}

在跳轉(zhuǎn)到B頁面的時(shí)候,會(huì)看到有delegate輸出了肥败,這樣delegate也有了,我們就可以像普通的delegate方式去使用了

  • 定義一個(gè)協(xié)議劈猿,大家都能看到的協(xié)議
  • 為B頁面設(shè)置一個(gè)delegate拙吉,目前就A頁面
  • 在A頁面中實(shí)現(xiàn)協(xié)議的方法潮孽,其實(shí)就是B讓A調(diào)用的方法

公共協(xié)議部分

@protocol ModuleDelegate <NSObject>

@optional
-(void)module:(id)obj info:(id)info;

B頁面某個(gè)操作后調(diào)用了這個(gè)方法

if (self.delegate && [self.delegate respondsToSelector:@selector(module:info:)]) {
        [self.delegate module:self.class.description info:@{@"type":@"super coupon", @"desc": @"可狠了", @"fee":@50}];
    }
    
    [self.navigationController popViewControllerAnimated:YES];

在A頁面中

-(void)module:(id)obj info:(id)info{
    NSLog(@"module: %@      info: %@", obj, info);
}

這樣通信方式的基本原理就完事了揪荣。


這里的類名、方法名都屬于硬編碼往史,參數(shù)用的字典仗颈,為了去model化,就必須要求去維護(hù)一個(gè)Protocol椎例,這里我定義了一個(gè)公共的Protocol

//
//  ModuleProtocol.h
//  ModuleCommuication
//
//  Created by L's on 2017/1/23.
//  Copyright ? 2017年 zuiye. All rights reserved.
//

#ifndef ModuleProtocol_h
#define ModuleProtocol_h


/**
 
 通信方式示例
 
 Class class = NSClassFromString(@"CouponViewController");
 
 UIViewController* couponVC = [[class alloc] init];
 
 //設(shè)置delegate
 SEL delegateSEL = NSSelectorFromString(@"setDelegate:");
 if ([couponVC respondsToSelector:delegateSEL]) {
    SuppressPerformSelectorLeakWarning(
        [couponVC performSelector:delegateSEL withObject:self];
    );
 }

//    設(shè)置block
    void (^block)(id) = ^(id couponObj){
        NSLog(@"通過block回調(diào):%@", couponObj);
    };
    SEL blockSEL = NSSelectorFromString(@"setBlock:");
    if ([couponVC respondsToSelector:blockSEL]) {
        SuppressPerformSelectorLeakWarning(
                                           [couponVC performSelector:blockSEL withObject:block];
                                           );
    }
 
 //傳遞參數(shù)
 SEL selector = NSSelectorFromString(@"setCouponFilter:");
 if ([couponVC respondsToSelector:selector]) {
    SuppressPerformSelectorLeakWarning(
        [couponVC performSelector:selector withObject:@{@"name": @"This is a super order"}];
    );
 }
 
 [self.navigationController pushViewController:couponVC animated:YES];
 
 */

/** 解決編譯的時(shí)候出錯(cuò)的問題 */
#define SuppressPerformSelectorLeakWarning(Stuff) \
do { \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"") \
Stuff; \
_Pragma("clang diagnostic pop") \
} while (0)


/**
    通信協(xié)議挨决,所有頁面的接口都在這個(gè)公共的協(xié)議方法里定義
 */
@protocol ModuleDelegate <NSObject>

@optional

/**
 對(duì)外提供接口的模塊必須實(shí)現(xiàn)delegate設(shè)置的方法,當(dāng)然方法叫什么名字無所謂 內(nèi)部如何寫無所謂(你問我為啥叫setDelegate订歪,就寫方法的時(shí)候少敲幾個(gè)字母脖祈,復(fù)用屬性的set方法了),主要是要拿到delegate

 @param delegate 稱為其delegate的對(duì)象
 */
-(void)setDelegate:(id<ModuleDelegate>)delegate;

/*******************優(yōu)惠券頁面通信協(xié)議***********************/

/**
 設(shè)置接口參數(shù)
 
 給優(yōu)惠券頁面?zhèn)鬟f參數(shù)刷晋,方法名字無所謂(你問我為啥叫setCouponFilter盖高,就寫方法的時(shí)候少敲幾個(gè)字母,復(fù)用屬性的set方法了)眼虱,重點(diǎn)在obj這個(gè)參數(shù)上了喻奥,模塊內(nèi)部如何處理這里不去管,這里直接實(shí)現(xiàn)了屬性的set方法
 接口里的屬性建議放到.h接口文件中捏悬,便于模塊內(nèi)部自己維護(hù)

 @param obj 提供給模塊的參數(shù)
 */
-(void)setCouponFilter:(id)obj;

/**
 優(yōu)惠券頁面回調(diào)

 @param obj 預(yù)留撞蚕,傳遞什么無所謂,目前傳遞了本身过牙,便于外部查看
 @param info 回調(diào)的參數(shù)
 */
-(void)module:(id)obj info:(id)info;
/*******************優(yōu)惠券頁面通信協(xié)議***********************/


@end



#endif /* ModuleProtocol_h */


還沒完事呢甥厦,大家會(huì)發(fā)現(xiàn)一個(gè)問題,就是如果執(zhí)行下面的方法會(huì)有警告啊

performSelector: withObject:

可以這樣解決

#pragma clang diagnostic push 
#pragma clang diagnostic ignored "-Warc-performSelector-leaks" 
   [someController performSelector: NSSelectorFromString(@"someMethod")]
#pragma clang diagnostic pop

為了解決這個(gè)問題我們定義一個(gè)宏

/** 解決編譯的時(shí)候出錯(cuò)的問題 */
#define SuppressPerformSelectorLeakWarning(Stuff) \
do { \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"") \
Stuff; \
_Pragma("clang diagnostic pop") \
} while (0)

在調(diào)用的時(shí)候

//傳遞參數(shù)
    SEL selector = NSSelectorFromString(@"setCouponFilter:");
    if ([BVC respondsToSelector:selector]) {
        SuppressPerformSelectorLeakWarning(
          [BVC performSelector:selector withObject:@{@"name": @"This is a super order"}];
                                           );
    }

其他處理

這里我們集中在控制器處理上了寇钉,現(xiàn)實(shí)中夸組件調(diào)用各種各樣了刀疙,比如我要從 UserModel 獲取用戶信息,在UserModel 實(shí)現(xiàn)中有如下方法

-(NSDictionary*)getUserInfo{
    NSDictionary* info = @{@"userId": @"8888", @"name": @"張三", @"age": @18};
    
    return info;
}

那么我們?cè)谕獠空{(diào)用就可以獲取到信息

    Class class = NSClassFromString(@"UserModel");
    SEL selector = NSSelectorFromString(@"getUserInfo");
    
    id target = [[class alloc] init];
    
    if (target && [target respondsToSelector:selector]) {
        id result;
        SuppressPerformSelectorLeakWarning(
            result = [target performSelector:selector]
                                           );
        NSLog(@"用戶信息:%@", result);
    }

如果需要調(diào)用工具類Utils里的方法處理數(shù)據(jù)摧莽,比如工具類Utils有個(gè)方法是給字典里的字符串追加前綴

+(NSDictionary *)formatInfo:(NSDictionary *)info{
    if (![info isKindOfClass:[NSDictionary class]]) {
        return nil;
    }
    
    NSMutableDictionary* dic = [NSMutableDictionary dictionary];
    for (id key in info) {
        dic[key] = [NSString stringWithFormat:@"zuiye_%@", info[key]];
    }
    
    return dic;
}

那么實(shí)際調(diào)用的時(shí)候

    NSDictionary* info = @{@"userId": @"8888", @"name": @"張三", @"age": @18};
    Class class = NSClassFromString(@"Utils");
    SEL selector = NSSelectorFromString(@"formatInfo:");
    
    if ([class respondsToSelector:selector]) {
        id result;
        SuppressPerformSelectorLeakWarning(
                                           result = [class performSelector:selector withObject:info]
                                           );
        NSLog(@"用戶信息追加前綴:%@", result);
    }
    

總結(jié)了下我的方法有點(diǎn)類似于 Target-Action + 協(xié)議 的方式庙洼,當(dāng)然這個(gè)只是簡(jiǎn)單的實(shí)現(xiàn),如果復(fù)雜的功能,那么還有去封裝一下油够,比如原生蚁袭、H5切換,我的思路不一定是最好的石咬,如果想看看大神們的通信方案揩悄,開篇提供了鏈接,大家可以去看看鬼悠。

感謝您閱讀完畢删性,如有疑問,歡迎添加QQ:714387953(蝸牛上高速)焕窝。
github:https://github.com/yhl714387953/ModuleCommuication
如果有錯(cuò)誤蹬挺,歡迎指正,一起切磋它掂,共同進(jìn)步
如果喜歡可以Follow巴帮、Star、Fork虐秋,都是給我最大的鼓勵(lì)榕茧。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市客给,隨后出現(xiàn)的幾起案子用押,更是在濱河造成了極大的恐慌,老刑警劉巖靶剑,帶你破解...
    沈念sama閱讀 212,080評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蜻拨,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡抬虽,警方通過查閱死者的電腦和手機(jī)官觅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,422評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來阐污,“玉大人休涤,你說我怎么就攤上這事〉驯伲” “怎么了功氨?”我有些...
    開封第一講書人閱讀 157,630評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)手幢。 經(jīng)常有香客問我捷凄,道長(zhǎng),這世上最難降的妖魔是什么围来? 我笑而不...
    開封第一講書人閱讀 56,554評(píng)論 1 284
  • 正文 為了忘掉前任跺涤,我火速辦了婚禮匈睁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘桶错。我一直安慰自己航唆,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,662評(píng)論 6 386
  • 文/花漫 我一把揭開白布院刁。 她就那樣靜靜地躺著糯钙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪退腥。 梳的紋絲不亂的頭發(fā)上任岸,一...
    開封第一講書人閱讀 49,856評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音狡刘,去河邊找鬼享潜。 笑死,一個(gè)胖子當(dāng)著我的面吹牛颓帝,可吹牛的內(nèi)容都是我干的米碰。 我是一名探鬼主播,決...
    沈念sama閱讀 39,014評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼购城,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了虐译?” 一聲冷哼從身側(cè)響起瘪板,我...
    開封第一講書人閱讀 37,752評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎漆诽,沒想到半個(gè)月后侮攀,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,212評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡厢拭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,541評(píng)論 2 327
  • 正文 我和宋清朗相戀三年兰英,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片供鸠。...
    茶點(diǎn)故事閱讀 38,687評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡畦贸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出楞捂,到底是詐尸還是另有隱情薄坏,我是刑警寧澤,帶...
    沈念sama閱讀 34,347評(píng)論 4 331
  • 正文 年R本政府宣布寨闹,位于F島的核電站胶坠,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏繁堡。R本人自食惡果不足惜沈善,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,973評(píng)論 3 315
  • 文/蒙蒙 一乡数、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧闻牡,春花似錦瞳脓、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,777評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至哨啃,卻和暖如春烧栋,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背拳球。 一陣腳步聲響...
    開封第一講書人閱讀 32,006評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工审姓, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人祝峻。 一個(gè)月前我還...
    沈念sama閱讀 46,406評(píng)論 2 360
  • 正文 我出身青樓魔吐,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親莱找。 傳聞我的和親對(duì)象是個(gè)殘疾皇子酬姆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,576評(píng)論 2 349

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)奥溺,斷路器辞色,智...
    卡卡羅2017閱讀 134,633評(píng)論 18 139
  • *面試心聲:其實(shí)這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個(gè)offer,總結(jié)起來就是把...
    Dove_iOS閱讀 27,133評(píng)論 30 470
  • 前言: 本文轉(zhuǎn)自前同事casa的博文,這篇文章是基于runtime實(shí)現(xiàn)的iOS組件化方案浮定,其實(shí)iOS組件化方案基本...
    monkey01閱讀 1,653評(píng)論 1 2
  • iOS網(wǎng)絡(luò)架構(gòu)討論梳理整理中相满。。桦卒。 其實(shí)如果沒有APIManager這一層是沒法使用delegate的立美,畢竟多個(gè)單...
    yhtang閱讀 5,174評(píng)論 1 23
  • 這幾天我一直在想 怎么去理解演講 怎么去練好演講 今天我終于在洗衣服的時(shí)候得到了一點(diǎn)靈感 演講好像做菜 說烹飪可能...
    如果我是一朵發(fā)閱讀 174評(píng)論 0 1