博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
组件化工具BeeHive(二):组件化实践
阅读量:7073 次
发布时间:2019-06-28

本文共 7446 字,大约阅读时间需要 24 分钟。

前言

使用BeeHive来进行项目组件化,其实是使用BeeHive来构建一个中间层,通过中间层来解耦各个模块。在文章有简单介绍过BeeHive的一些组件化思路,本文将更多的从使用者的角度来分析BeeHive。

1. 用法

通过构建中间层来组件化项目,共需要三步:

  1. 创建protocol
  2. 创建impClass
  3. 存储protocol-impClass映射关系

以为例

1.1. 创建protocol

protocol表示模块对外暴露的接口,调用模块时只需要依赖模块对应的protocol,就可以实现对模块的调用。

下列代码表示,模块A对应的协议BHServiceProtocol的定义,调用者可以通过-[getModuleAMainViewController]-[pushToModuleAOneViewController]这两个方法来调用模块A。

// BHServiceProtocol.m#import "BHServiceProtocol.h"#import 
@protocol ModuleAServiceProtocol
- (UIViewController *)getModuleAMainViewController;- (void)pushToModuleAOneViewController;@end复制代码

这个协议需要继承BeeHive中的协议BHServiceProtocol,协议BHServiceProtocol中定义如下两个可选方法+[singleton:]+[shareInstance]。 如果协议对应的响应者impClass实现了这两个方法,并且+[singleton:]方法返回YES,则调用响应类的+[shareInstance]方法来创建响应者对象。否则,直接调用[[implClass alloc] init]来创建对象。

#import 
#import "BHAnnotation.h"@protocol BHServiceProtocol
@optional+ (BOOL)singleton;+ (id)shareInstance;@end复制代码

1.2. 创建impClass

impClass是protocol对应的响应类,它需要遵守这个protocol协议,它可以是模块中一个已经存在的业务类,也可以是这个模块的一个封装类。

如果模块对外暴露的方法全部来自于同一个业务类,则可以将这个业务类设置成impClass; 如果模块对外暴露的方法全部来自于多个不同的业务类,则需要给这个模块创建一个封装类,通过这个封装类来实现对模块的调用,impClass指向这个封装类。(这种方式也叫做target-action)

第一种方式比较常用,BeeHive的官方demo基本上是使用的这种方法。

模块A的impClass是ModuleAService类,它是一个封装类,内部实现了对模块A中两个不同类的调用。

//ModuleAService.m #import "ModuleAOneViewController.h"#import "ModuleAViewController.h"#import "ModuleAService.h"@implementation ModuleAService- (UIViewController *)getModuleAMainViewController{    return [ModuleAViewController new];}- (void )pushToModuleAOneViewController{    UITabBarController *tab = (UITabBarController *)[UIApplication sharedApplication].delegate.window.rootViewController;    UINavigationController *nav = tab.selectedViewController;    ModuleAOneViewController *one = [ModuleAOneViewController new];        [nav pushViewController:one animated:YES];    }复制代码

另外,模块C对外暴露的方法只有一个,所以模块C使用的是第一种方式,它的impClass直接指向ModuleCViewController这个业务类。

1.3. 设置protocol-impClass映射关系

在BeeHive中,所有protocol-impClass的映射关系都由BHServiceManager管理,BHServiceManager主要提供了两个方法:

- (void)registerService:(Protocol *)service implClass:(Class)implClass;- (id)createService:(Protocol *)service;复制代码

方法名中的service指的就是上文中所说的protocol,所以方法一的作用是注册protocol-impClass的映射关系,方法二的作用是通过protocol获取对应的响应类。

BHServiceManager类中,有一个叫做allServicesDict的属性,它保存了所有的protocol-impClass的映射关系,上述方法一和方法二就是根据这个属性来执行的。 allServicesDict是一个可变字典,其中key是protocol的字符串名称,value是impClass的字符串名称。

具体注册方式有下列三种

1.3.1. 使用BeeHive类的-[registerService:service:]

方法-[registerService:service:]的实现

- (void)registerService:(Protocol *)proto service:(Class) serviceClass{    [[BHServiceManager sharedManager] registerService:proto implClass:serviceClass];}复制代码

这个方法内部就是调用了BHServiceManager-[registerService:implClass:]方法,将传入的protocolimpClass添加到BHServiceManager类的属性allServicesDict中。

1.3.2. 使用宏BeeHiveService

上文中,定义了ModuleA模块的协议ModuleAServiceProtocol和响应类ModuleAService,可以使用如下代码来注册它们之间的关系:

BeeHiveService(ModuleAServiceProtocol, ModuleAService)复制代码

使用宏来注册时,务必在本模块中调用宏。如果在主工程中调用,且主工程没有导入这个模块(更准确的说是impClass对应的类没有导入),会导致程序crash。

在第四节中已经讲过了注册Module类的宏BeeHiveMod,这两个宏的实现原理是一样的,都是在mach-o文件中增加一个section来存储数据,然后在启动项目时取出数据,最终也是调用BHServiceManager-[registerService:implClass:]方法来注册,详细过程这里就不在赘述。

mach-o文件的section:__DATA:BeehiveServices中存储的是一个json格式的字符串:

"{ \"ModuleAServiceProtocol\" : \"ModuleAService\"}"复制代码
1.3.3. 使用plist文件

使用plist文件注册,需要在初始化BeeHive时指定plist文件的路径

[BHContext shareInstance].serviceConfigName = @"BeeHive.bundle/BHService";复制代码

plist文件的格式:

需要注意的是BeeHive.bundle必须添加到项目的主工程的target上,因为BeeHive内部是在[NSBundle mainBundle]的目录下寻找BeeHive.bundle。 当使用cocoapods来加载BeeHive时,默认情况下,BeeHive.bundle是存在于BeeHive.framework中,这个时候使用[NSBundle mainBundle]时获取不到BeeHive.bundle的,解决办法是改用[NSBundle bundleForClass:self.class]或将BeeHive.bundle添加到项目的主工程的target上。

2. 使用场景

一个典型的场景,当调用模块A时,如果当前还没有登录,则调用登录模块,登录成功之后,再调用模块A;如果已经登录了,则直接调用模块A。

以项目为例

模块A对外的协议ModuleAServiceProtocol

//BHServiceProtocol.h#import "BHServiceProtocol.h"#import 
@protocol ModuleAServiceProtocol
- (void)pushToModuleAViewController;@end复制代码

模块A的响应类

//ModuleAService.m#import "ModuleAViewController.h"#import "ModuleAService.h"@BeeHiveService(ModuleAServiceProtocol, ModuleAService)@implementation ModuleAService- (void )pushToModuleAViewController{        id
moduleAService = [[BeeHive shareInstance] createService:@protocol(LoginServiceProtocol)]; [moduleAService loginIfNeedWithCompleteBlock:^(BOOL succeed) { if (succeed) { UINavigationController *root = (UINavigationController *)[UIApplication sharedApplication].delegate.window.rootViewController; ModuleAViewController *moduleA = [ModuleAViewController new]; [root pushViewController:moduleA animated:YES]; } }];}@end复制代码

不管有没有登录,首先调用登录模块,具体的跳转逻辑被保存在block中,然后传给登录模块,登录完成之后,执行这个block。

登录模块的协议LoginServiceProtocol

//LoginServiceProtocol.h#import "BHServiceProtocol.h"#import 
@protocol LoginServiceProtocol
- (void)loginIfNeedWithCompleteBlock:(void (^)(BOOL))completeBlock;@end复制代码

登录模块的响应类

//LoginService.m #import "LoginViewController.h"#import "LoginService.h"@BeeHiveService(LoginServiceProtocol, LoginService)@implementation LoginService- (void)loginIfNeedWithCompleteBlock:(void (^)(BOOL))completeBlock{    if ([LoginViewController isLogined]) {        completeBlock(YES);    }else{        LoginViewController *login = [LoginViewController new];        login.completeBlock = completeBlock;                UIViewController *root = [UIApplication sharedApplication].delegate.window.rootViewController;        [root presentViewController:login animated:YES completion:nil];    }}@end复制代码

如果已经登录,直接执行传入的block;如果没有登录,则弹出登录界面,登录成功之后,执行block。

3. impClass的生命周期

通过上文可知,impClass的对象是最终是由BHServiceManager类创建的,但是BHServiceManager类并没有持有impClass的对象,本质上,BHServiceManager相当于是一个对象工厂。

如果impClass是一个模块的封装类,impClass的对象只在当前作用域有效,超过了这个作用域,这个对象会被释放掉。 如果impClass是一个模块的业务类,则impClass对象的生命周期依赖于模块内部的具体实现了。

如果想长期持有这个impClass对象,通常有两种方式:

1.在模块调用处,强引用被创建的impClass对象。

2.实现BeeHive中BHServiceProtocol协议的+[singleton]方法,并返回YES。这样,被创建的impClass对象会被保存在单例[BHContext shareInstance]中。(如果同时实现了+[shareInstance]方法,则使用这个方法来创建impClass的对象)

可以使用下列BHContext的方法来移除保存的impClass对象

- (void)removeServiceWithServiceName:(NSString *)serviceName;复制代码

4. 异常处理

BeeHive可以通过下列设置来开启异常模式,在这个模式下,如果遇到BeeHive内部的一些错误,会直接抛出异常。一般在调试模式下,应该开启。生产模式下,应该关闭。

[BeeHive shareInstance].enableException = YES;[[BeeHive shareInstance] setContext:[BHContext shareInstance]];复制代码

4.1 注册时异常

注册方式共有三种:

  1. 使用BeeHive类的-[registerService:service:]
  2. 使用宏BeeHiveService
  3. 使用plist文件

注册时,可能存在下列三种情况:

  1. protocol和impClass对应的协议或类不存在
  2. protocol和impClass存在,但impClass没有遵循对应的protocol
  3. protocol和impClass存在,且impClass遵循对应的protocol
方式一 方式二 方式三
情况一 编译时报错 启动时crash 注册成功
情况二 注册不成功,如果是异常模式,则crash 注册不成功,如果是异常模式,则crash 注册成功
情况二 注册成功 注册成功 注册成功

当注册方法和被注册的模块没有写在一起时,删除了模块,而它的注册方法没有被删除,这个时候就会出现情况一,比如在pod中解除了对模块的依赖。 要避免情况一中的两个报错,最好是将注册方法写在本模块中,比如Module类的-[modInit:]方法中,这样删除模块的时候,也删除了对应的注册方法。

不管plist文件中protocol和impClass是否存在,是否匹配,只要它们的key符合格式,就会被注册成功。

4.2. 调用时异常

在调用模块时,首先需要创建impClass,一般是通过BeeHive类的-[createService:]方法,这个方法需要一个protocol

- (id)createService:(Protocol *)proto;复制代码

创建好impClass的对象之后,然后这个对象调用protocol中声明的方法。

在这个调用过程中,可能会遇到下列三种情况:

protocol未注册 protocol已注册,但对应impClass的类不存在 protocol已注册,且对应impClass的类存在,但执行的方法没实现
处理结果 将impClass的值设置为nil,如果是异常模式,则crash。 将impClass的值设置为nil 抛出异常

4.3. 小结

在调试阶段时,可以开启异常模式,这样就能检测一些潜在的问题出来,比如impClass没有遵循protocol、使用未注册的protocol来创建impClass。

关于异常处理,需要注意的是,impClass必须实现被调用的方法。另外,将注册方法写在本模块中。

转载地址:http://rlkml.baihongyu.com/

你可能感兴趣的文章
用户数据是关键 欧盟或调查微软收购领英交易
查看>>
制定网络安全计划目标,比方说先……
查看>>
UOS3.0:给企业以安全感与确定性
查看>>
花了8500万美元之后,FB关闭了那个开发者平台
查看>>
与高通纠纷受关注 苹果利润或遭诺基亚侵权诉讼蚕食
查看>>
《Objective-C入门经典》——2.5节问与答
查看>>
《R语言与数据挖掘最佳实践和经典案例》—— 1.3 数据集
查看>>
《SolidWorks 2017中文版机械设计从入门到精通)》——1.10 范例
查看>>
《简明电路分析》——1.2节电学主要参数
查看>>
Firefox 表示不计划开发 Windows 10 手机 APP
查看>>
《海外社交媒体营销》一一2.5 选择正确的工具和软件
查看>>
《微信公众平台开发最佳实践》—— 第1章 微信公众平台介绍 1.1 微信及其三大平台...
查看>>
Manjaro Linux 17.0.2 pre 5 发布
查看>>
《Oracle数据库性能优化方法论和最佳实践》——1.2 性能优化目标的确定和衡量...
查看>>
Npcap —— 基于 Winpcap/ Libpcap 的网络包抓取库
查看>>
uTorrent 被发现悄悄安装挖矿程序,BitTorrent 公司否认
查看>>
微软 PowerShell 成为黑客恶意软件传播工具
查看>>
《Python数据科学实践指南》——2.3 获取键盘输入
查看>>
Docker 收购 Tutum,进一步完善其生态布局
查看>>
解密浏览器缓存机制
查看>>