成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

iOS組件化不只是架構師的事

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

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

[[249145]]

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

為什么要組件化,在看過很多優秀的文章后,你一定會問這個問題,組件化能給我們帶來多大的好處?作為一個小公司而言,涉及組件化的機會很少,沒有大廠的工作經驗,也很難將組件化理解的很透徹。可能以為我們的業務模塊還不夠多,或者說,我們沒有理解到他的好處,其實組件化***的好處就是,每個組件,每個模塊都可能單獨成一個app,具有自己的生命周期。這樣就可以分割成不同的業務組模塊去處理,之前聽說京東,有團隊專門負責消息模塊,有團隊專門負責廣告模塊,有團隊專門負責發現模塊,這是你就會發現如果沒有很好的組件化思想,這樣的多團隊合作就非常的困難,已經很難維護好這個項目的開發迭代。說了這么多,到底組件化是什么樣子的呢?那我跟著我的腳步,學習分析,探討下。

二、組件化的核心思想

組件化的話的核心思想,也是我們進行組件化的基礎框架,就是通過怎么樣的方式實現組件化,或者如何從架構層,業務層多個層次實現架構呢。要想實現組件化,其實就是建立一個中間轉換的工具。你也可以理解為路由,通過路由的思想實現跨業務的數據溝通,從而一定程度上的降低各層數據的耦合。減少各個業務層等層級的import發生的耦合。

三、目前實現的組件化的方式

目前實現一般有下面三種思想:

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

Procotol協議注冊方案

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

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

所有組件對外提供的procotol和組件提供的服務由中間件統一管理,每個組件提供的procotol和服務是一一對應的。

例如:

在JJLoginProvider中:load方法會應用啟動的時候調用,就會在JJProtocolManager進行注冊。JJLoginProvider遵守了JJLoginProvider協議,這樣就可以對外根據業務需求提供一些方法。 

  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; 

這樣就可以在需要登錄業務模塊的地方,通過JJProtocolManager取出JJLoginProtocol對應的服務提供者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路由方案最經典的就是蘑菇街的路由組件化,通過url的方式將調用方法,調用參數,已經回調方法封裝到url中,然后在通過對url的解析獲取到方法名,參數,***通過消息轉發機制調用方法。

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

  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. }]; 

首頁只需調用 [MGJRouter openURL:@"mgj://detail?id=404"] 就可以打開相應的詳情頁。

這里可以看到,我們通過url短鏈的方式,通過將參數拼接到url query部分,這樣就可以,通過這樣解析url中的scheme,host,path,query獲取到調轉什么要的控制器,需要傳什么什么樣的參數,從而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; 

通過這樣的方式,我們就可以實現組件化,但是有時候我們會遇到一個圖片編輯模塊,不能傳遞UIImage到對應的模塊上去的話,這里我們需要傳個新的參數進去,為了解決這個問題,這樣其實,可以把參數直接丟給后面的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.        } 

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

target-action方案

target-action方案是在學習casa大神,CTMediator 的基礎上進行的

casa大神認為,

  1. 根本無法表達非常規對象,如果用url組件化的話,遇到像UIImage這樣的參數,就需要添加一個參數,才能解決
  2. URL注冊對于實施組件化方案是完全不必要的,且通過URL注冊的方式形成的組件化方案,拓展性和可維護性都會被打折
  3. 蘑菇街沒有拆分遠程調用和本地間調用
  4. 蘑菇街必須要在app啟動時注冊URL響應者 
  1. //理論上頁面之間的跳轉只需 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來完成調用。這套組件化方案將遠程應用調用和本地應用調用做了拆分,而且是由本地應用調用為遠程應用調用提供服務,與蘑菇街方案正好相反。

調用方式:

先說本地應用調用,本地組件A在某處調用[[CTMediator sharedInstance] performTarget:targetName action:actionName params:@{...}]向CTMediator發起跨組件調用,CTMediator根據獲得的target和action信息,通過objective-C的runtime轉化生成target實例以及對應的action選擇子,然后最終調用到目標業務提供的邏輯,完成需求。

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

針對請求的路由操作很少會采用本地文件記錄路由表的方式,服務端經常處理這種業務,在服務端領域基本上都是通過正則表達式來做路由解析。App中做路由解析可以做得簡單點,制定URL規范就也能完成,最簡單的方式就是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.        // 這里處理異常場景,具體如何處理取決于產品 
  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能否響應action方法,只要能夠就執行這段核心代碼, 
  4. 核心代碼的主要功能: 
  5. **/ 
  6. - (id)safePerformAction:(SEL)action target:(NSObject *)target params:(NSDictionary *)params 
  7.    //// 創建一個函數簽名,這個簽名可以是任意的,但需要注意,簽名函數的參數數量要和調用的一致。 
  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. //如果此消息有參數需要傳入,那么就需要按照如下方法進行參數設置,需要注意的是,atIndex的下標必須從2開始。原因為:0 1 兩個參數已經被target 和selector占用 
  19.        [invocation setArgument:¶ms atIndex:2]; 
  20. // 設置selector 
  21.        [invocation setSelector:action]; 
  22. // 設置target 
  23.        [invocation setTarget:target]; 
  24. //消息調用 
  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 

總結:

CTMediator根據獲得的target和action信息,通過objective-C的runtime轉化生成target實例以及對應的action選擇子,然后最終調用到目標業務提供的邏輯,完成需求。

下面是三種方式的代碼實現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 - 定義因特網服務的類型。最常見的類型是 http 
  8. host - 定義域主機(http 的默認主機是 www) 
  9. domain - 定義因特網域名,比如 w3school.com.cn 
  10. :port - 定義主機上的端口號(http 的默認端口號是 80) 
  11. path - 定義服務器上的路徑(如果省略,則文檔必須位于網站的根目錄中)。 
  12. filename - 定義文檔/資源的名稱 

 

責任編輯:未麗燕 來源: 簡書
相關推薦

2015-11-24 10:05:07

私有云虛擬化負載遷移

2017-03-25 21:13:38

JavaScript排序

2011-04-28 20:21:44

和信創天終端管理虛擬終端管理系統

2010-08-05 09:29:08

jQuery

2013-04-25 13:58:15

編程

2018-03-13 15:00:22

智慧交通高鐵無人駕駛

2021-11-05 11:17:45

互聯網996大廠

2024-11-26 11:02:17

2025-04-08 03:00:00

2015-02-04 09:45:40

2018-06-28 18:10:41

華為

2016-10-13 18:06:09

云計算多云模型

2021-01-06 10:51:39

云計算云服務IT

2015-12-15 17:19:55

戴爾云計算

2011-09-15 13:25:02

2011-11-17 13:25:43

垃圾郵件

2012-09-18 10:20:17

2018-06-27 17:24:24

華為

2022-11-02 11:48:03

Vanilla OSGNOMEUbuntu

2015-03-31 09:28:28

Hadoop大數據技術大數據未來道路
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产一区二区三区久久久久久久久 | 免费中文字幕日韩欧美 | 97精品国产97久久久久久免费 | 国产视频1区| 亚洲福利在线观看 | 久久久.com | 一区二区三区视频在线观看 | 香蕉国产在线视频 | 青春草国产 | 一级片在线视频 | 亚洲精品一区二区三区蜜桃久 | 亚洲一二三区av | 日韩欧美在线播放 | 97影院在线午夜 | 国产91丝袜在线播放 | 午夜视频在线播放 | 国产99免费视频 | 伊人免费观看视频 | 国产亚洲一区二区在线观看 | 国产成人在线视频播放 | 成人免费视频在线观看 | 久久av网站| 日韩中文字幕久久 | 男女羞羞免费网站 | 成年人黄色小视频 | 精品久久久久久久久久久久久久 | 精品国产伦一区二区三区观看说明 | 中文字幕一区在线观看视频 | 成人自拍视频网站 | 啪啪精品 | 日韩一区二区在线免费观看 | 91国内在线观看 | 欧美一区二区在线 | 国产精品久久国产精品 | 国产视频二区 | 国产精品视频免费观看 | 精品在线一区 | 亚洲一一在线 | 国产日韩欧美精品一区二区三区 | 91在线一区 | 久久精品二区亚洲w码 |