浅谈iOS组件化

背景:随着应用不停得进行版本迭代、功能模块的逐步增长,使得应用的代码体积愈来愈大,业务和功能间的依赖关系愈来愈复杂,耦合度愈来愈高,严重得下降了业务方的开发效率。在此背景下,组件化开发的思路得以进入众多app开发架构的设计方案之中。css

简介:组件化开发的方案是将app分红了多个组件进行开发,各个组件之间能够互相通讯,可是组件之间不能有直接的约束、不能直接引用。html

实现方式:1 URL--Block方式;2 Target--Action方式。json

1 URL-Block方式架构

蘑菇街App采用的组件化模式,每次启动时须要实例化各个模块的组件,而且经过URL注册响应者,每次注册时会将URL做为一个key值,与对应的Block保存在本地的注册表之中。组件管理器就是经过本地保存的这张注册表去知道有哪些模块哪些接口,接口的形式就是URL-Block。app

[MGJRouter registerURLPattern:@"mgj://cart/ordercount" toObjectHandler:^id(NSDictionary *routerParamters){ // do some calculation return @42; }] NSNumber *orderCount = [MGJRouter objectForURL:@"mgj://cart/ordercount”]

缺点:(1)采用key-block形式须要内存维护一张注册表,形成没必要要的内存常驻。组件化

           (2)没有区分远程调用和本地调用。测试

2 Target-Action方式ui

每一个组件都经过Action暴露可调用接口,组件经过自带得Target-Action来响应。url

[[CTMediator sharedInstance] performTarget:targetName action:actionName params:@{…}][[CTMediator sharedInstance] performTarget:targetName action:actionName params:@{…}]
根据targetName和actionName,能够经过Runtime机制转化生成Target的实例以及Action的选择子

 
 

NSString *targetClassString = [NSString stringWithFormat:@"Target_%@", targetName];spa

NSString *actionString = [NSString stringWithFormat:@"Action_%@:", actionName];

    

Class targetClass = NSClassFromString(targetClassString);

id target = [[targetClass alloc] init];

SEL action = NSSelectorFromString(actionString);

最后经过得到实例对象及选择子调用目标业务逻辑。


[target performSelector:action withObjects:params];

同时Target-Action的方式中区分了远程调用和本地调用,由本地调用服务为远程调用提供服务

 

- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *,id> *)options

{

    return [[[CTMediator sharedInstance] performActionWithUrl:url completion:nil] boolValue];

}

因为远程调用只能经过jsonString的形式来提供复杂参数,因此咱们在远程调用时只能将很是规的参数转化成jsonString再经过urlEncode后,经过GET的形式来提供复杂参数。在openURL方法中接收到url后,要对url进行解析获得咱们想要的参数才能调用本地调用的服务。结论:远程调用比本地调用多了一个解析URL的过程,所以远程调用和本地调用必须拆开,不能走同一个接口。

 

- (id)performActionWithUrl:(NSURL *)url completion:(void (^)(NSDictionary *))completion

{

/*

....(在此对url进行解析,而且获得targetName和actionName以及params)

*/

//而后再走本地调用的接口

id result = [self performTarget:targetName action:actionName params:params];

return result;

}

使用category定制不一样组件的中间件

由于调用方不知道将要调起的组件须要传递哪些参数、以及哪些target能够调用,因此能够使用category的优点来针对每一个组件写一个有针对性的中间件的类别。

 

//CTMediator+CTMediatorModuleAActions.m

- (void)CTMediator_presentImage:(UIImage *)image

{

    if (image) {

        [self performTarget:kCTMediatorTargetA

                     action:kCTMediatorActionNativePresentImage

                     params:@{@"image":image}];

    } else {

        // 这里处理imagenil的场景,如何处理取决于产品

        [self performTarget:kCTMediatorTargetA

                     action:kCTMediatorActionNativeNoImage

                     params:@{@"image":[UIImage imageNamed:@"noImage"]}];

    }

}

这样作得好处:

(1)在category的方法中能够作参数验证,例如上面的方法中,接收的参数为image对象,就能够校验调用方传的参数是否为image。是image的话在category方法中将其封装再走本地调用组件的方法。

(2)category统一了组件调用入口,所以不管在调试仍是在源码阅读上都为开发者提供了极大的便利。   

(3)因为category统一了组件调用入口,所以能够在category中使用宏或者调用声明。

(4)使用category能够解决组件间调用去Model化带来的参数繁杂的问题。

拆分组件

(1)基础功能组件

(2)基础UI组件

(3)产品业务组件

总结:组件化适用于业务稳定、逻辑复杂的app,可以解决项目模块间得耦合问题,有助于多人大团队的协同开发。方便组件的单独开发、单独测试。


参考文章:

http://www.th7.cn/Program/IOS/201603/776240.shtml

http://www.tuicool.com/articles/AB7jqeY

http://www.jianshu.com/p/afb9b52143d4