自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

iOS組件化不只是架構(gòu)師的事

移動開發(fā)
為什么要組件化,在看過很多優(yōu)秀的文章后,你一定會問這個問題,組件化能給我們帶來多大的好處?作為一個小公司而言,涉及組件化的機(jī)會很少,沒有大廠的工作經(jīng)驗(yàn),也很難將組件化理解的很透徹。

iOS組件化曾今在業(yè)界是多么的火熱的話題,現(xiàn)在在少有人再次提及這個的話題。網(wǎng)上也很多關(guān)于組件化的文章和思想,最經(jīng)典的要是casa大神和蘑菇街關(guān)于組件化的論戰(zhàn)。想想曾經(jīng)看到這些文章的時候,覺得組件化是多么優(yōu)秀的思想,覺得他們說的都有道理,而casa大神應(yīng)該在很多思想上給了我等碼農(nóng)很多靈感。而兩位大神架構(gòu)師級別的論劍是否讓你真正理解到組件化的重要性。是否讓你在內(nèi)心深處產(chǎn)生共鳴,最 近看到一個項(xiàng)目讓我對組件化多了些思考。

[[249145]]

一、為什么要組件化,組件化到底有什么好處?

為什么要組件化,在看過很多優(yōu)秀的文章后,你一定會問這個問題,組件化能給我們帶來多大的好處?作為一個小公司而言,涉及組件化的機(jī)會很少,沒有大廠的工作經(jīng)驗(yàn),也很難將組件化理解的很透徹??赡芤詾槲覀兊臉I(yè)務(wù)模塊還不夠多,或者說,我們沒有理解到他的好處,其實(shí)組件化***的好處就是,每個組件,每個模塊都可能單獨(dú)成一個app,具有自己的生命周期。這樣就可以分割成不同的業(yè)務(wù)組模塊去處理,之前聽說京東,有團(tuán)隊(duì)專門負(fù)責(zé)消息模塊,有團(tuán)隊(duì)專門負(fù)責(zé)廣告模塊,有團(tuán)隊(duì)專門負(fù)責(zé)發(fā)現(xiàn)模塊,這是你就會發(fā)現(xiàn)如果沒有很好的組件化思想,這樣的多團(tuán)隊(duì)合作就非常的困難,已經(jīng)很難維護(hù)好這個項(xiàng)目的開發(fā)迭代。說了這么多,到底組件化是什么樣子的呢?那我跟著我的腳步,學(xué)習(xí)分析,探討下。

二、組件化的核心思想

組件化的話的核心思想,也是我們進(jìn)行組件化的基礎(chǔ)框架,就是通過怎么樣的方式實(shí)現(xiàn)組件化,或者如何從架構(gòu)層,業(yè)務(wù)層多個層次實(shí)現(xiàn)架構(gòu)呢。要想實(shí)現(xiàn)組件化,其實(shí)就是建立一個中間轉(zhuǎn)換的工具。你也可以理解為路由,通過路由的思想實(shí)現(xiàn)跨業(yè)務(wù)的數(shù)據(jù)溝通,從而一定程度上的降低各層數(shù)據(jù)的耦合。減少各個業(yè)務(wù)層等層級的import發(fā)生的耦合。

三、目前實(shí)現(xiàn)的組件化的方式

目前實(shí)現(xiàn)一般有下面三種思想:

  1. Procotol方案
  2. URL路由方案
  3. target-action方案

Procotol協(xié)議注冊方案

關(guān)于procotol協(xié)議注冊方案看人用的比較少,也很少看到有人分享,我也是在這個項(xiàng)目中看到,就研究了一下。通過JJProtocolManager 作為中間轉(zhuǎn)化。 

  1. + (void)registerModuleProvider:(id)provider forProtocol:(Protocol*)protocol;  
  2. + (id)moduleProviderForProtocol:(Protocol *)protocol; 

所有組件對外提供的procotol和組件提供的服務(wù)由中間件統(tǒng)一管理,每個組件提供的procotol和服務(wù)是一一對應(yīng)的。

例如:

在JJLoginProvider中:load方法會應(yīng)用啟動的時候調(diào)用,就會在JJProtocolManager進(jìn)行注冊。JJLoginProvider遵守了JJLoginProvider協(xié)議,這樣就可以對外根據(jù)業(yè)務(wù)需求提供一些方法。 

  1. + (void)load 
  2.     [JJProtocolManager registerModuleProvider:[self new] forProtocol:@protocol(JJLoginProtocol)]; 
  3. - (UIViewController *)viewControllerWithInfo:(id)userInfo needNew:(BOOL)needNew callback:(JJModuleCallbackBlock)callback{ 
  4.     CLoginViewController *vc = [[CLoginViewController alloc] init]; 
  5.     vc.jj_moduleCallbackBlock = callback; 
  6.     vc.jj_moduleUserInfo = userInfo; 
  7.     return vc; 

這樣就可以在需要登錄業(yè)務(wù)模塊的地方,通過JJProtocolManager取出JJLoginProtocol對應(yīng)的服務(wù)提供者JJLoginProvider,直接獲取。如下: 

  1. id<jjwebviewvcmoduleprotocol> provider = [JJProtocolManager moduleProviderForProtocol:@protocol(JJWebviewVCModuleProtocol)]; 
  2.    UIViewController *vc =[provider viewControllerWithInfo:obj needNew:YES callback:^(id info) { 
  3.        if (callback) { 
  4.            callback(info); 
  5.        } 
  6.    }]; 
  7.    vc.hidesBottomBarWhenPushed = YES; 
  8.    [self.currentNav pushViewController:vc animated:YES];</jjwebviewvcmoduleprotocol> 

URL路由方案

URL路由方案最經(jīng)典的就是蘑菇街的路由組件化,通過url的方式將調(diào)用方法,調(diào)用參數(shù),已經(jīng)回調(diào)方法封裝到url中,然后在通過對url的解析獲取到方法名,參數(shù),***通過消息轉(zhuǎn)發(fā)機(jī)制調(diào)用方法。

下面是蘑菇街的路由方式:(這里要是想詳細(xì)了解,可以到蘑菇街的路由組件化 中具體學(xué)習(xí)) 

  1. [MGJRouter registerURLPattern:@"mgj://detail?id=:id" toHandler:^(NSDictionary *routerParameters) { 
  2.     NSNumber *id = routerParameters[@"id"]; 
  3.     // create view controller with id 
  4.     // push view controller 
  5. }]; 

首頁只需調(diào)用 [MGJRouter openURL:@"mgj://detail?id=404"] 就可以打開相應(yīng)的詳情頁。

這里可以看到,我們通過url短鏈的方式,通過將參數(shù)拼接到url query部分,這樣就可以,通過這樣解析url中的scheme,host,path,query獲取到調(diào)轉(zhuǎn)什么要的控制器,需要傳什么什么樣的參數(shù),從而push或者present新頁面。

解析scheme,host,path核心代碼: 

  1. NSString *scheme = [nsUrl scheme];//解析scheme 
  2.    NSString *module = [nsUrl host]; 
  3.    NSString *action = [[nsUrl path] stringByReplacingOccurrencesOfString:@"/" withString:@"_"]; 
  4.    if (action && [action length] && [action hasPrefix:@"_"]) { 
  5.        action = [action stringByReplacingCharactersInRange:NSMakeRange(0,1) withString:@""]; 
  6.    } 
  7.   
  8.    NSString *query = nil; 
  9.    NSArray* pathInfo = [nsUrl.absoluteString componentsSeparatedByString:@"?"]; 
  10.    if (pathInfo.count > 1) { 
  11.        query = [pathInfo objectAtIndex:1]; 
  12.    } 

解析query的核心代碼: 

  1. NSMutableDictionary *parameters = nil; 
  2. NSString *parametersString = query; 
  3. NSArray *paramStringArr = [parametersString componentsSeparatedByString:@"&"]; 
  4. if (paramStringArr && [paramStringArr count]>0) { 
  5.     parameters = [NSMutableDictionary dictionary]; 
  6.     for (NSString* paramString in paramStringArr) { 
  7.         NSArray *paramArr = [paramString componentsSeparatedByString:@"="]; 
  8.         if (paramArr.count > 1) { 
  9.             NSString *key = [paramArr objectAtIndex:0]; 
  10.             NSString *value = [paramArr objectAtIndex:1]; 
  11.             parameters[key] = [JJRouter unescapeURIComponent:value]; 
  12.         } 
  13.     } 
  14. return parameters; 

通過這樣的方式,我們就可以實(shí)現(xiàn)組件化,但是有時候我們會遇到一個圖片編輯模塊,不能傳遞UIImage到對應(yīng)的模塊上去的話,這里我們需要傳個新的參數(shù)進(jìn)去,為了解決這個問題,這樣其實(shí),可以把參數(shù)直接丟給后面的arg處理

  1. + (nullable id)openURL:(nonnull NSString *)urlString arg:(nullable id)arg error:( NSError*__nullable *__nullable)error completion:(nullable JJRouterCompletion)completion 

舉個例子: 

  1.     Action *action = [Action new]; 
  2.            action.type = JJ_WebView; 
  3.            Params *params = [[Params alloc] init]; 
  4.            //            params.pageID = JJ_LOGIN; 
  5.            action.params = params; 
  6.            NSDictionary *parms = @{Jump_Key_Action:action, Jump_Key_Param : @{WebUrlString:@"http://www.baidu.com",Name:@"小二"}, Jump_Key_Callback:[JJFunc callback:^(id  _Nullable object) { 
  7.                NSLog(@"%@",object); 
  8.            }]}; 
  9. //            ActionJump(parms); 
  10.              
  11.            [JJRouter openURL:@"router://JJActionService/showWebVC" arg: parms error:nil completion:parms[Jump_Key_Callback]]; 
  12.        } 

我看的項(xiàng)目,這個就是通過url解析和protocol協(xié)議注冊實(shí)現(xiàn)組件化,只是沒有像蘑菇街那樣注冊支持哪些 URL類型。

target-action方案

target-action方案是在學(xué)習(xí)casa大神,CTMediator 的基礎(chǔ)上進(jìn)行的

casa大神認(rèn)為,

  1. 根本無法表達(dá)非常規(guī)對象,如果用url組件化的話,遇到像UIImage這樣的參數(shù),就需要添加一個參數(shù),才能解決
  2. URL注冊對于實(shí)施組件化方案是完全不必要的,且通過URL注冊的方式形成的組件化方案,拓展性和可維護(hù)性都會被打折
  3. 蘑菇街沒有拆分遠(yuǎn)程調(diào)用和本地間調(diào)用
  4. 蘑菇街必須要在app啟動時注冊URL響應(yīng)者 
  1. //理論上頁面之間的跳轉(zhuǎn)只需 open 一個 URL 即可。所以對于一個組件來說,只要定義「支持哪些 URL」即可,比如詳情頁,大概可以這么做的  
  2. [MGJRouter registerURLPattern:@"mgj://detail?id=:id" toHandler:^(NSDictionary *routerParameters) { 
  3.    NSNumber *id = routerParameters[@"id"]; 
  4.    // create view controller with id 
  5.    // push view controller 
  6. }]; 

而casa的組件化主要是基于Mediator模式和Target-Action模式,中間采用了runtime來完成調(diào)用。這套組件化方案將遠(yuǎn)程應(yīng)用調(diào)用和本地應(yīng)用調(diào)用做了拆分,而且是由本地應(yīng)用調(diào)用為遠(yuǎn)程應(yīng)用調(diào)用提供服務(wù),與蘑菇街方案正好相反。

調(diào)用方式:

先說本地應(yīng)用調(diào)用,本地組件A在某處調(diào)用[[CTMediator sharedInstance] performTarget:targetName action:actionName params:@{...}]向CTMediator發(fā)起跨組件調(diào)用,CTMediator根據(jù)獲得的target和action信息,通過objective-C的runtime轉(zhuǎn)化生成target實(shí)例以及對應(yīng)的action選擇子,然后最終調(diào)用到目標(biāo)業(yè)務(wù)提供的邏輯,完成需求。

在遠(yuǎn)程應(yīng)用調(diào)用中,遠(yuǎn)程應(yīng)用通過openURL的方式,由iOS系統(tǒng)根據(jù)info.plist里的scheme配置找到可以響應(yīng)URL的應(yīng)用(在當(dāng)前我們討論的上下文中,這就是你自己的應(yīng)用),應(yīng)用通過AppDelegate接收到URL之后,調(diào)用CTMediator的openUrl:方法將接收到的URL信息傳入。當(dāng)然,CTMediator也可以用openUrl:options:的方式順便把隨之而來的option也接收,這取決于你本地業(yè)務(wù)執(zhí)行邏輯時的充要條件是否包含option數(shù)據(jù)。傳入URL之后,CTMediator通過解析URL,將請求路由到對應(yīng)的target和action,隨后的過程就變成了上面說過的本地應(yīng)用調(diào)用的過程了,最終完成響應(yīng)。

針對請求的路由操作很少會采用本地文件記錄路由表的方式,服務(wù)端經(jīng)常處理這種業(yè)務(wù),在服務(wù)端領(lǐng)域基本上都是通過正則表達(dá)式來做路由解析。App中做路由解析可以做得簡單點(diǎn),制定URL規(guī)范就也能完成,最簡單的方式就是scheme://target/action這種,簡單做個字符串處理就能把target和action信息從URL中提取出來了。

舉個例子: 

  1. /** 
  2. 這里是登錄模塊的target 
  3. **/ 
  4. #import "CTMediator+ModuleLogin.h" 
  5. NSString * const kCTMediatorTargetA = @"A"
  6. NSString * const kCTMediatorActionLoginViewController = @"showLoginController"
  7. @implementation CTMediator (ModuleLogin) 
  8. - (UIViewController *)push_viewControllerForLogin 
  9.    UIViewController *vc = [self performTarget:kCTMediatorTargetA action:kCTMediatorActionLoginViewController params:nil shouldCacheTarget:NO]; 
  10.      
  11.    if ([vc isKindOfClass:[UIViewController class]]) { 
  12.        // view controller 交付出去之后,可以由外界選擇是push還是present 
  13.        return vc; 
  14.    } else { 
  15.        // 這里處理異常場景,具體如何處理取決于產(chǎn)品 
  16.        return [[UIViewController alloc] init]; 
  17.    } 

 

  1. /** 
  2. 登錄模塊的action 
  3. **/ 
  4. - (UIViewController *)Action_showLoginController:(NSDictionary *)param 
  5.    JJLoginViewController *vc =[[JJLoginViewController alloc] init]; 
  6.      
  7.    return vc; 

看上去,target-action路由方案更加的清晰,不過這個還是各取所需吧

接下來,target-action的核心代碼就是: 

  1. /** 
  2. if ([target respondsToSelector:action]) 
  3. 判斷target能否響應(yīng)action方法,只要能夠就執(zhí)行這段核心代碼, 
  4. 核心代碼的主要功能: 
  5. **/ 
  6. - (id)safePerformAction:(SEL)action target:(NSObject *)target params:(NSDictionary *)params 
  7.    //// 創(chuàng)建一個函數(shù)簽名,這個簽名可以是任意的,但需要注意,簽名函數(shù)的參數(shù)數(shù)量要和調(diào)用的一致。 
  8.    NSMethodSignature* methodSig = [target methodSignatureForSelector:action]; 
  9.    if(methodSig == nil) { 
  10.        return nil; 
  11.    } 
  12. //    獲取返回類型 
  13.    const char* retType = [methodSig methodReturnType]; 
  14. //判斷返回值類型 
  15.    if (strcmp(retType, @encode(void)) == 0) { 
  16. // 通過簽名初始化 
  17.        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig]; 
  18. //如果此消息有參數(shù)需要傳入,那么就需要按照如下方法進(jìn)行參數(shù)設(shè)置,需要注意的是,atIndex的下標(biāo)必須從2開始。原因?yàn)椋? 1 兩個參數(shù)已經(jīng)被target 和selector占用 
  19.        [invocation setArgument:¶ms atIndex:2]; 
  20. // 設(shè)置selector 
  21.        [invocation setSelector:action]; 
  22. // 設(shè)置target 
  23.        [invocation setTarget:target]; 
  24. //消息調(diào)用 
  25.        [invocation invoke]; 
  26.        return nil; 
  27.    } 
  28.    if (strcmp(retType, @encode(NSInteger)) == 0) { 
  29.        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig]; 
  30.        [invocation setArgument:¶ms atIndex:2]; 
  31.        [invocation setSelector:action]; 
  32.        [invocation setTarget:target]; 
  33.        [invocation invoke]; 
  34.        NSInteger result = 0; 
  35.        [invocation getReturnValue:&result]; 
  36.        return @(result); 
  37.    } 
  38.    if (strcmp(retType, @encode(BOOL)) == 0) { 
  39.        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig]; 
  40.        [invocation setArgument:¶ms atIndex:2]; 
  41.        [invocation setSelector:action]; 
  42.        [invocation setTarget:target]; 
  43.        [invocation invoke]; 
  44.        BOOL result = 0; 
  45.        [invocation getReturnValue:&result]; 
  46.        return @(result); 
  47.    } 
  48.    if (strcmp(retType, @encode(CGFloat)) == 0) { 
  49.        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig]; 
  50.        [invocation setArgument:¶ms atIndex:2]; 
  51.        [invocation setSelector:action]; 
  52.        [invocation setTarget:target]; 
  53.        [invocation invoke]; 
  54.        CGFloat result = 0; 
  55.        [invocation getReturnValue:&result]; 
  56.        return @(result); 
  57.    } 
  58.    if (strcmp(retType, @encode(NSUInteger)) == 0) { 
  59.        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig]; 
  60.        [invocation setArgument:¶ms atIndex:2]; 
  61.        [invocation setSelector:action]; 
  62.        [invocation setTarget:target]; 
  63.        [invocation invoke]; 
  64.        NSUInteger result = 0; 
  65.        [invocation getReturnValue:&result]; 
  66.        return @(result); 
  67.    } 
  68. #pragma clang diagnostic push 
  69. #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 
  70.    return [target performSelector:action withObject:params]; 
  71. #pragma clang diagnostic pop 

總結(jié):

CTMediator根據(jù)獲得的target和action信息,通過objective-C的runtime轉(zhuǎn)化生成target實(shí)例以及對應(yīng)的action選擇子,然后最終調(diào)用到目標(biāo)業(yè)務(wù)提供的邏輯,完成需求。

下面是三種方式的代碼實(shí)現(xiàn)Git的地址:

https://github.com/lumig/JJRouterDemo

彩蛋: 

  1. // url 編碼格式 
  2. foo://example.com:8042/over/there?name=ferret#nose 
  3. \_/ \______________/ \________/\_________/ \__/ 
  4. |         |              |         |        | 
  5. scheme authority         path      query   fragment 
  6. scheme://host.domain:port/path/filename 
  7. scheme - 定義因特網(wǎng)服務(wù)的類型。最常見的類型是 http 
  8. host - 定義域主機(jī)(http 的默認(rèn)主機(jī)是 www) 
  9. domain - 定義因特網(wǎng)域名,比如 w3school.com.cn 
  10. :port - 定義主機(jī)上的端口號(http 的默認(rèn)端口號是 80) 
  11. path - 定義服務(wù)器上的路徑(如果省略,則文檔必須位于網(wǎng)站的根目錄中)。 
  12. filename - 定義文檔/資源的名稱 

 

責(zé)任編輯:未麗燕 來源: 簡書
相關(guān)推薦

2015-11-24 10:05:07

私有云虛擬化負(fù)載遷移

2017-03-25 21:13:38

JavaScript排序

2010-08-05 09:29:08

jQuery

2013-04-25 13:58:15

編程

2011-04-28 20:21:44

和信創(chuàng)天終端管理虛擬終端管理系統(tǒng)

2024-11-26 11:02:17

2018-03-13 15:00:22

智慧交通高鐵無人駕駛

2021-11-05 11:17:45

互聯(lián)網(wǎng)996大廠

2012-09-18 10:20:17

2011-06-29 16:29:19

2015-03-31 09:28:28

Hadoop大數(shù)據(jù)技術(shù)大數(shù)據(jù)未來道路

2018-06-27 17:24:24

華為

2022-11-02 11:48:03

Vanilla OSGNOMEUbuntu

2021-07-26 22:33:41

切片結(jié)構(gòu)體代碼

2025-04-17 02:00:00

數(shù)據(jù)分析SQL大數(shù)據(jù)

2025-04-08 03:00:00

2018-06-28 18:10:41

華為

2016-10-13 18:06:09

云計算多云模型

2015-02-04 09:45:40

2015-12-15 17:19:55

戴爾云計算
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號