日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

组件生命周期管理和通信方案

發(fā)布時(shí)間:2025/4/5 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 组件生命周期管理和通信方案 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

隨著移動(dòng)互聯(lián)網(wǎng)的快速發(fā)展,項(xiàng)目的迭代速度越來(lái)越快,需求改變?cè)絹?lái)越頻繁,傳統(tǒng)開(kāi)發(fā)方式的工程所面臨的一些,如代碼耦合嚴(yán)重、維護(hù)效率低、開(kāi)發(fā)不夠敏捷等問(wèn)題就凸現(xiàn)了出來(lái)。于是越來(lái)越多的公司開(kāi)始推行"組件化",通過(guò)對(duì)原有業(yè)務(wù)或新業(yè)務(wù)進(jìn)行組件(或模塊)拆分來(lái)提高并行開(kāi)發(fā)效率。

在筆者面試過(guò)程中發(fā)現(xiàn),很多同學(xué)口中的"組件化"也只是把代碼分庫(kù),然后在主項(xiàng)目中使用 CocoaPods 把各個(gè)子庫(kù)聚合起來(lái)。對(duì)于怎樣合理地對(duì)組件分層、如何管理組件(主要包括組件的生命周期管理和組件的通信管理),如何管理不同版本的依賴,以及是否有整套集成和發(fā)布工具,這類(lèi)問(wèn)題的知之甚少。如果完全不了解這些問(wèn)題,那么只是簡(jiǎn)單的對(duì)主項(xiàng)目進(jìn)行組件拆分,并不能提高多少開(kāi)發(fā)效率。

筆者認(rèn)為合理地進(jìn)行組件拆分和管理各個(gè)組件之間的通信是組件化過(guò)程中最大的難點(diǎn)。合理地進(jìn)行組件拆分是為了解耦,并且各個(gè)組件能更容易地獨(dú)立變化。而對(duì)于一個(gè)完整的應(yīng)用來(lái)說(shuō),每個(gè)組件不可能孤零零地存在,必定會(huì)互相調(diào)用。這樣不同組件之間必須能進(jìn)行通信而又沒(méi)有編譯期的依賴。

組件生命周期管理

可能很多同學(xué)在實(shí)施組件化的過(guò)程中知道要解決組件通信的問(wèn)題,卻很少關(guān)注組件的生命周期。這里的生命周期主要是指 AppDelegate 中的生命周期方法。有時(shí)候一些組件需要在這些鉤子方法中做一些事情,這時(shí)候就需要一個(gè)能夠管理組件的工具,并在適當(dāng)?shù)臅r(shí)機(jī)執(zhí)行組件相應(yīng)的邏輯。

比如筆者在項(xiàng)目中是這樣做的:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {[[Ant shareInstance] application:application didFinishLaunchingWithOptions:launchOptions];return YES; }- (void)applicationWillResignActive:(UIApplication *)application {[[Ant shareInstance] applicationWillResignActive:application]; }- (void)applicationDidEnterBackground:(UIApplication *)application {[[Ant shareInstance] applicationDidEnterBackground:application]; }- (void)applicationWillEnterForeground:(UIApplication *)application {[[Ant shareInstance] applicationWillEnterForeground:application]; } 復(fù)制代碼

所有注冊(cè)的組件(模塊)會(huì)在 AppDelegate 相應(yīng)的生命周期方法調(diào)用時(shí)自動(dòng)調(diào)用。例如有如下組件定義:

ANT_MODULE_EXPORT(Module1App)@interface Module1App() <ATModuleProtocol> {NSInteger state; } @end@implementation Module1App - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {state = 0;NSLog(@"Module A state: %zd", state);return YES; }- (void)applicationWillEnterForeground:(UIApplication *)application {state += 1;NSLog(@"Module A state: %zd", state); } @end 復(fù)制代碼

上面示例代碼中第一行的 ANT_MODULE_EXPORT(Module1App) 是導(dǎo)出組件。Ant 會(huì)在 dyld 加載完 image 后將導(dǎo)出的組件進(jìn)行注冊(cè)。當(dāng)應(yīng)用生命周期方法被調(diào)用時(shí),會(huì)實(shí)例化所有注冊(cè)過(guò)的組件,調(diào)用組件相應(yīng)的方法,并進(jìn)行緩存,之后再次調(diào)用就會(huì)從緩存中取出組件的實(shí)例對(duì)象。

一般擁有完整生命周期的組件一般稱為一個(gè)模塊,一個(gè)模塊其實(shí)也是一個(gè)獨(dú)立的組件,它一般是包含一個(gè)完整的業(yè)務(wù),列如:登錄模塊,外賣(mài)模塊,消息模塊等。

組件的生命周期管理并不復(fù)雜,實(shí)現(xiàn)方案都沒(méi)有太大區(qū)別,但它也是組件化中必不可少的部分。

組件通信

業(yè)界關(guān)于組件通信的方案比較多,主要有:url-block, target-action, protocol-class。下面筆者會(huì)對(duì)這三種方案做個(gè)簡(jiǎn)單的介紹。

URL-Block

這是蘑菇街在組件化過(guò)程中使用的一種組件間通信方式,在應(yīng)用啟動(dòng)時(shí)注冊(cè)組件提供的服務(wù),把調(diào)用組件使用的url和組件提供的服務(wù)block對(duì)應(yīng)起來(lái),保存到內(nèi)存中。在使用組件的服務(wù)時(shí),通過(guò)url找到對(duì)應(yīng)的block,然后獲取服務(wù)。

[MGJRouter registerURLPattern:@"mgj://foo/bar" toHandler:^(NSDictionary *routerParameters) {NSLog(@"routerParameterUserInfo:%@", routerParameters[MGJRouterParameterUserInfo]); }];[MGJRouter openURL:@"mgj://foo/bar"]; 復(fù)制代碼

筆者是在15年開(kāi)始學(xué)習(xí)組件化,那個(gè)時(shí)候就是使用的蘑菇街的這種發(fā)案。不過(guò)筆者從來(lái)沒(méi)有在實(shí)際項(xiàng)目中使用這種方案。casa 在這篇文章中批判了這種方案。筆者對(duì) case 的觀點(diǎn)很是贊同。

如果項(xiàng)目中需要很多組件的服務(wù),那么就需要在內(nèi)存中維護(hù)大量的 url-block項(xiàng),造成內(nèi)存問(wèn)題,對(duì)于服務(wù)注冊(cè)的代碼應(yīng)該放在什么地方也是一個(gè)問(wèn)題。筆者一直認(rèn)為 url-block 注冊(cè)是一種很粗暴的方式,比如某個(gè)應(yīng)用在啟動(dòng)時(shí)注冊(cè)了100個(gè)服務(wù),但某些服務(wù)在用戶使用過(guò)程中根本就沒(méi)有觸發(fā),這就造成了內(nèi)存浪費(fèi)。比如我們點(diǎn)擊應(yīng)用中的按鈕跳轉(zhuǎn)到某個(gè)頁(yè)面,如果用戶沒(méi)有點(diǎn)擊按鈕,下個(gè)頁(yè)面就永遠(yuǎn)不會(huì)創(chuàng)建,我們一般不會(huì)提前創(chuàng)建這個(gè)頁(yè)面的。筆者更傾向于在需要服務(wù)的時(shí)候才進(jìn)行服務(wù)對(duì)象的創(chuàng)建,在特定場(chǎng)景下也提供服務(wù)對(duì)象的緩存。

使用 url 傳參也是一個(gè)不可忽略的問(wèn)題,對(duì)于一些基礎(chǔ)數(shù)據(jù)類(lèi)型,使用這種方案倒是沒(méi)有問(wèn)題,但是對(duì)于一些非常規(guī)對(duì)象就無(wú)能為力了,如 UIImage, NSData 等類(lèi)型。

還有一個(gè)問(wèn)題是 casa 在文章中沒(méi)有指出的,這個(gè)問(wèn)題在他的 target-action 方案中也存在。下面用一個(gè)例子來(lái)說(shuō)明一下。

比如在一個(gè)組件 A 中提供了一個(gè)服務(wù):

[MGJRouter registerURLPattern:@"mgj://foo/bar" toHandler:^(NSDictionary *routerParameters) {NSLog(@"routerParameterUserInfo:%@", routerParameters[MGJRouterParameterUserInfo]); }]; 復(fù)制代碼

然后在一個(gè)組件 B 中使用了服務(wù):

[MGJRouter openURL:@"mgj://foo/bar"]; 復(fù)制代碼

從上面示例代碼中可以看到,兩個(gè)不同組件能通信其實(shí)是通過(guò)一個(gè)字符串來(lái)定義的。如果服務(wù)使用方在寫(xiě)代碼時(shí)寫(xiě)錯(cuò)了一個(gè)字符,那么使用方根本就不可能調(diào)起正確的服務(wù),一旦出現(xiàn)這個(gè)問(wèn)題,在開(kāi)發(fā)過(guò)程中很難被發(fā)現(xiàn)。如果我們對(duì)組件多,注冊(cè)的服務(wù)多,那么在使用時(shí)就存在很大的溝通問(wèn)題,提供方和接入方可能會(huì)在每個(gè)字符串所代表的意義上浪費(fèi)大量的時(shí)間。而這些問(wèn)題都可以在工程設(shè)計(jì)上避免的。雖說(shuō)我們?cè)趯?xiě)代碼時(shí)要低耦合,但并不代表不要耦合,有時(shí)候需要一些耦合來(lái)提高代碼的健壯性和可維護(hù)性。

在 Swift 中可以使用枚舉來(lái)解決上面的問(wèn)題,我們可以像下面這樣做:

protocol URLPatternCompatible {var URLPattern: String { get } }enum SomeService {case orderDetailcase others }enum SomeService: URLPatternCompatible {var URLPattern: String {switch self {case .orderDetail:return "mgj://foo/bar/orderdetail"case .others:return "mgj://foo/bar/others"}} }// 組件 A (服務(wù)提供方) MGJRouter.register(.orderDetail) { ... }// 組件 B (服務(wù)使用方) MGJRouter.open(.orderDetail) 復(fù)制代碼

SomeService 的定義可以放到一個(gè)專門(mén)的組件中,服務(wù)提供方和使用方都依賴這個(gè)專門(mén)的組件。我們這里不僅將字符串放到了一個(gè)統(tǒng)一的地方進(jìn)行維護(hù),而且還將一些在運(yùn)行期才能發(fā)現(xiàn)的問(wèn)題提前暴露到編譯器。這里我們通過(guò)耦合來(lái)達(dá)到提高代碼的健壯性和可維護(hù)性的目的。

Target-Action

Target-actin 是 casa 在批判蘑菇街的方案時(shí)提出的一種方案。它解決了 url-block 方案中內(nèi)存問(wèn)題、url 傳參問(wèn)題、沒(méi)有區(qū)分本地調(diào)用和遠(yuǎn)程調(diào)用等問(wèn)題。其核心就是使用了 NSObject 的 - (id)performSelector:(SEL)aSelector withObject:(id)object; 方法。

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

casa 在文章中也給出了 demo,在具體的項(xiàng)目中,我們可以這樣使用:

// CTMediator+SomeAction.h - (UIViewController *)xx_someAction:(NSDictionary *)params;// CTMediator+SomeAction.m - (UIViewController *)xx_someAction:(NSDictionary *)params {return [self performTarget:@"A" action:@"someAction" params:params shouldCacheTarget:NO] } 復(fù)制代碼

上面是提供給服務(wù)調(diào)用方的一個(gè)簡(jiǎn)潔的接口。其實(shí)就是對(duì) CTMediator 方法的封裝。我們一般將 CTMediator 的這個(gè)分類(lèi)放到一個(gè)獨(dú)立的組件中。調(diào)用方依賴這個(gè)獨(dú)立的組件就可以了。

在某個(gè)組件中調(diào)用服務(wù):

// 組件 A 中 UIViewController *vc = [CTMediator sharedInstance] xx_someAction:@{@"key": value}]; 復(fù)制代碼

針對(duì)上面服務(wù)的定義,服務(wù)提供方的定義就必須是下面這樣:

// TargetA.h @interface Target_A : NSObject - (UIViewController *)someAction:(NSDictionary *)params; @end// TargetA.m - (UIViewController *)someAction:(NSDictionary *)params { ... }復(fù)制代碼

在這整個(gè)過(guò)程中可以看到,服務(wù)的調(diào)用方只需要依賴 CTMediator 這個(gè)中間件及其分類(lèi)(定義服務(wù))。服務(wù)提供方和調(diào)用方?jīng)]有任何依賴。確實(shí)做到了組件解耦。可以肯定的是 target-action 方案確實(shí)解決了 url-block 方案的一些問(wèn)題。但是仔細(xì)一看,也是存在一些問(wèn)題的。

跟 url-block 方案一樣,兩個(gè)不同組件能通信其實(shí)仍然是通過(guò)一個(gè)字符串來(lái)定義的。為什么這么說(shuō)呢,我們可以看一下下面的代碼:

// CTMediator+SomeAction.m - (UIViewController *)xx_someAction:(NSDictionary *)params {return [self performTarget:@"A" action:@"someAction" params:params shouldCacheTarget:NO] }// TargetA.h @interface Target_A : NSObject - (UIViewController *)someAction:(NSDictionary *)params; @end 復(fù)制代碼

從上面的代碼中可以看到,服務(wù)能調(diào)起主要是調(diào)用了 CTMediator 的

- (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget; 方法。這里不管是 targetName 還是 action 都是字符串,在實(shí)現(xiàn)中 CTMediator 會(huì)示例化一個(gè) Target_targetName 類(lèi)的對(duì)象,并且創(chuàng)建一個(gè) Action_actionName 的 selector,所有我們?cè)诜?wù)提供的組件中的 Target 以及 Action 是不能隨便定義的。Target 必須是以 Target_開(kāi)頭,方法必須以 Action_開(kāi)頭。這種強(qiáng)制要求感覺(jué)不是一種工程師的思維。這里想去耦合,卻以一種不是很正確的方式造成了隱式的耦合。這也是讓我拋棄 CTMediator 轉(zhuǎn)而去開(kāi)發(fā)自己的組件化通信方案的原因之一。

Protocol-Class

Protocol-Class 方案也是常用的組件化通信方式之一。這里把它放到最后,肯定是因?yàn)楣P者使用的是這種方案咯(笑)。

Protocol-Class 方案就是通過(guò) protocol 定義服務(wù)接口,服務(wù)提供方通過(guò)實(shí)現(xiàn)該接口來(lái)提供接口定義的服務(wù)。具體實(shí)現(xiàn)就是把 protocol 和 class 做一個(gè)映射,同時(shí)在內(nèi)存中保存一張映射表,使用的時(shí)候,就通過(guò) protocol 找到對(duì)應(yīng)的 class 來(lái)獲取需要的服務(wù)。這種方案的優(yōu)缺點(diǎn)先不說(shuō),可以先看一下具體的實(shí)踐:

示例圖:

示例代碼:

// TestService.h (定義服務(wù)) @protocol TestService <NSObject> /// 測(cè)試 - (void)service1;@end// 組件 A (服務(wù)提供方) ANT_REGISTER_SERVICE(TestServiceImpl, TestService) @interface TestServiceImpl() <TestService> @end@implementation TestServiceImpl- (void)service1 {NSLog(@"Service test from Impl"); }@end// 組件 B (服務(wù)使用方) id <TestService> obj = [Ant serviceImplFromProtocol:@protocol(TestService)]; [obj service1]; 復(fù)制代碼

像上面的方案一樣,我們會(huì)將服務(wù)的定義放到獨(dú)立的組件中。這個(gè)組件僅僅只包含了服務(wù)的聲明。不管是服務(wù)提供方還是服務(wù)使用方都依賴這個(gè)獨(dú)立的組件,服務(wù)提供方還是服務(wù)使用方互不依賴。

這里將系統(tǒng)提供的服務(wù)定義為協(xié)議,通過(guò)耦合提高了代碼的健壯性和可維護(hù)性。這里定義服務(wù)的 protocol 對(duì)服務(wù)提供方做了一個(gè)限定:你可以提供哪些服務(wù),同時(shí)也給服務(wù)使用方做了限定:你可以使用哪些服務(wù)。這種設(shè)計(jì)將系統(tǒng)有哪些服務(wù)都交代的清清楚楚,通過(guò)服務(wù)的 protocol 我們就知道了每個(gè)服務(wù)的功能,調(diào)用需要的參數(shù),返回值等。這里的定義服務(wù)的同時(shí)也可以作為系統(tǒng)服務(wù)的接口文檔,這節(jié)省了服務(wù)提供方和使用方很多的溝通時(shí)間,讓其能關(guān)注業(yè)務(wù)的開(kāi)發(fā)。這在大型項(xiàng)目,多團(tuán)隊(duì)開(kāi)發(fā)中優(yōu)勢(shì)尤為明顯。

當(dāng)然 protocol-class 這種方案缺點(diǎn)也很明顯,需要在內(nèi)存中保存 protocol 到 Class 的映射關(guān)系。但是我們可以通過(guò)將服務(wù)分類(lèi),讓系統(tǒng)注冊(cè)的 protocol-class 項(xiàng)盡量少一些,不要一個(gè)服務(wù)定義一個(gè)實(shí)現(xiàn)。對(duì)于一個(gè)有100個(gè)服務(wù)的系統(tǒng),定義10個(gè)服務(wù)實(shí)現(xiàn),每個(gè)實(shí)現(xiàn)提供10個(gè)服務(wù),肯定要比100個(gè)服務(wù)實(shí)現(xiàn)占用的內(nèi)存少很多。這就要求我們?cè)趯?shí)踐過(guò)程中能對(duì)系統(tǒng)中的服務(wù)能做好劃分。

總結(jié)

以上就是筆者對(duì)組件化的一些思考,很多觀點(diǎn)可能也不太成熟,如果有什么不合理的地方,也歡迎各位同學(xué)提出建議。組件解耦在 iOS 中其實(shí)有多種解決方案,各位同學(xué)可以根據(jù)項(xiàng)目實(shí)際情況選擇適合自己的方案。

上面代碼中的 Ant 是筆者最近開(kāi)發(fā)的一個(gè)負(fù)責(zé)組件生命周期管理和通信的開(kāi)源工具。因?yàn)楣P者公司從17年開(kāi)始就一直使用 Swift 進(jìn)行開(kāi)發(fā),原來(lái)的工具是用 Swift 編寫(xiě)的,使用了很多 Swift 的特性,在 OC 中使用就顯得不倫不類(lèi)了,就針對(duì) OC 進(jìn)行了重新設(shè)計(jì),于是就有了 Ant 。

轉(zhuǎn)載于:https://juejin.im/post/5c6b576d5188256204690203

總結(jié)

以上是生活随笔為你收集整理的组件生命周期管理和通信方案的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。