关于ios模块化开发解决方案网上也有一些介绍,但真正落实在在具体的实例却很少看到,计划编写系统文章来介绍关于我对模块化解决方案的理解,里面会有包含到一些关于解耦、路由、封装、私有pod管理等内容;并编写的一个实例项目放在git进行开源[jiamoduledemo],里面现在已经放着一些封装的功能模块;会不断的进行更新,假如你感兴趣可以star一下,项目也不断的更新完善优化;如果你有更好的方案或者说好的建议可以lssues,我会在短时间进行更新并修改相应的问题;
一:项目中存在的问题
1:当公司里面有多个项目同时进行,并且有可能是多个人分别不同项目时,就会存在如上图出现的情况,其实每个app中都是有很多共同的模块,当然有可能你会把相同功能模块代码复制一份在新项目中,但这其实并不是最好的方式,在后期不断迭代过程中,不同的人会往里面增加很多带有个人色彩的代码;这样就像相同的模块项目后期对于多个项目统一管理也是灾难性,有可能会失控,哪怕项目转移别人接手也会无形中浪费很多时间,增加维护成本,所以实例中更注重对于一些相同模块进行提取,求同存异;而模块化结合私有pods进行管理,对于常用功能的封装,只要开放出一些简单开关配置方式,就可以实现一个功能,比如日志记录、网络请求模块、网络状态变化提示等;
2:对于页面之间相互耦合,而页面之间的传参也各不相同,由于不同的开发人员或者简便方式等原因,传参的类型都有差异,包含如实体、简单基本类型等,先前项目对于路由方式也不支持,导致要实现收到消息推送进行不同的页面跳转存在硬编码情况,对于功能扩展存在相当大的问题;而右边则是模块化后页面之间的交互方式;页面之间也不存在耦合关系,都只跟jiamediator这个中介者相依赖;而传参都统一成以字典的形式;虽然可能牺牲一些方便跟随意,却可以解耦模块化;并且加入对路由方式的处理;约定好相关的协议进行交互;用这种路由方式代替那些第三方的路由插件则是因为它的灵活性,最主要还是省去了第三方路由插件在启动时要注册路由的问题;
二:解决方案实现之模块化
1:jiacore(基础功能封装)
jiacore是整个app最基础模块,所有的模块化都要依赖,主要包含一些全局的功能模块,比如jiabaseviewcontroller、jiaappdelegate等;目前已经把一些默认的功能进行集成在里面,包含网络状态变化判断及提示、日志记录功能等;并把一些相关配置的内容用jiacoreconfigmanager这个管理类进行统一设置,比如是否打开日志记录功能;jiacoreconfigmanager类则是开放给具体app设置全局的相关配置;下面就以其中一个日志记录功能进行讲解:
1
2
3
4
5
6
7
|
//jiacore基础模块相关配置
jiacoreconfigmanager *jiacoreconfig=[jiacoreconfigmanager sharedinstance];
jiacoreconfig.recordlogger=yes;
然后具体app的prefixheader.pch引入命名空间并进行设置记录日志的等级:
#import "jiacocoalumberjack.h"
//ddlog等级
static const int ddloglevel = ddloglevelverbose;
|
这样就完成的一个app对于日志记录模块的引入,jiacore已经帮你完成的关于日志记录的相关配置,并且错误内容以一种可读性较好的格式记录到file文件中,而且这些file文件生成规则也都定义好了,当然如何时你要是在xcode控制台显示不同等级色彩,只要安装xcodecolors插件并简单进行设置就可以了,对于不同等级不同色彩都已经在jiacore配置完成;
2:jspatch热更新功能
在jiacore里面也默认集成了热更新的功能,只要传入简单的对象数组就会启动热更新;其中jiapathchmodel已经是定义好的模型,在app中把接口请求转化成模型数组,其中patchid是唯一值名称、md5则是js文件的md5值、url是js的下载路径、ver则是对哪个版本起作用;因为一般我们在外面的app都是多版本共存,热更新也要进行版本区分,只下载与本版本相对应的热更新js文件加载;而md5值则是为了增加安全性,避免js文件被别人进行修改而影响app的运行,在jiacore会对下载后的js文件进行md5计算并比较;对于没有在jspatchmutablearray以前的js文件会被删除;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
//热更新内容
jiapathchmodel *sample=[[jiapathchmodel alloc]init];
sample.patchid = @ "patchid_sample1" ;
sample.md5 = @ "2cf1c6f6c5632dc21224bf42c698706b" ;
sample.url = @ "http://test.qshmall.net:9090/demo1.js" ;
sample.ver = @ "1" ;
jiapathchmodel *sample1=[[jiapathchmodel alloc]init];
sample1.patchid = @ "patchid_sample2" ;
sample1.md5 = @ "e8a4eaeadce5a4598fb9a868e09c75fd" ;
sample1.url = @ "http://test.qshmall.net:9090/demo2.js" ;
sample1.ver = @ "1" ;
//jiacore基础模块相关配置
jiacoreconfigmanager *jiacoreconfig=[jiacoreconfigmanager sharedinstance];
jiacoreconfig.jspatchmutablearray=[@[sample,sample1] mutablecopy];
|
3:jiagt模块(个推封装)
消息推送对于一个app是相当重要性,一般是采用第三方的sdk进行集成,其实大部分的sdk处理代码都是差不多,在这实例中对差异化的内容进行提取,实例中将以个推进行模块化,因为消息推送的大部分代码都集中在appdelegate中,造成的一大堆杂乱代码,当然也有一部分人对appdelegate进行扩展分类进行移除代码,实例中将采用另外一种解决方案进行抽取,可以达到完全解耦,在具体的app里面将不会再出现个推sdk相关内容,只要简单进行配置跟处理消息就可以,下面只是简单的列出部分代码,其它封装代码见源代码;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
//设置个推模块的配置
jiagtconfigmanager *gtconfig=[jiagtconfigmanager sharedinstance];
gtconfig.jiagtappid=@ "0uuwznwonianok07jerwgas" ;
gtconfig.jiagtappkey=@ "26leo4stbra7teymujdxlx3" ;
gtconfig.jiagtappsecret=@ "2282vl0iwzd9kl3zpdyoul7" ;
#pragma mark 消息推送相关处理
/**
* @author wujunyang, 16-07-07 16:07:25
*
* @brief 处理个推消息
*
* @param notificationmessage
*/
-( void )gtnotification:(nsdictionary *)notificationmessage
{
nslog(@ "%@" ,notificationmessage[@ "payload" ]);
nslog(@ "-----接收到个推通知------" );
}
/**
* @author wujunyang, 16-07-07 16:07:40
*
* @brief 处理远程苹果通知
*
* @param remotenotificationmessage
*/
-( void )receiveremotenotification:(nsdictionary *)remotenotificationmessage
{
nslog(@ "%@" ,remotenotificationmessage[@ "message" ]);
nslog(@ "-----接收到苹果通知------" );
}
/**
* @author wujunyang, 16-09-21 14:09:33
*
* @brief 获得注册成功时的devicetoken 可以在里面做一些绑定操作
*
* @param devicetoken <#devicetoken description#>
*/
-( void )receivedevicetoken:(nsstring *)devicetoken
{
nslog(@ "-----当前devicetoken:%@------" ,devicetoken);
}
|
4:jiaanalytics模块(友盟统计封装)
jiaanalytics模块是在友盟统计sdk跟aspect相结合基础上完成,对于页面的进出统计采用aop切面方式进行,把原本应该在每个页面生命周期的统计代码移除,app运用只要简单配置友盟相对应的信息,也可以设置要统计页面的过滤条件,目前已经有三种如要统计的开头页面的前缀字符串数组、要统计的页面名称字符串数组、不统计的页面名称字符串数组;可以结合使用,达到精确统计页面的目的;而且把统计的代码放在异步线程进行,不会影响主线程的响应;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
__weak typeof(self) ws = self;
dispatch_async(dispatch_get_global_queue(dispatch_queue_priority_default, 0), ^{
[uiviewcontroller aspect_hookselector:@selector(viewwillappear:) withoptions:aspectpositionafter usingblock:^(id<aspectinfo> info, bool animated){
uiviewcontroller *controller = [info instance];
bool filterresult=[ws fileterwithcontrollername:nsstringfromclass([controller class ])];
if (filterresult) {
[ws beginlogpageview:[controller class ]];
}
} error:null];
[uiviewcontroller aspect_hookselector:@selector(viewwilldisappear:) withoptions:aspectpositionafter usingblock:^(id<aspectinfo> info, bool animated){
uiviewcontroller *controller = [info instance];
bool filterresult=[ws fileterwithcontrollername:nsstringfromclass([controller class ])];
if (filterresult) {
[ws endlogpageview:[controller class ]];
}
} error:null];
});
|
三:解决方案实现之页面解耦
jiamediator起到一个中介的作用,所有的模块间响应交互都是通过它进行,每个模块都会对它进行扩展分类(例如:jiamediator+模块a),分类主要是为了用于本地间调用而又不想用路由的方式,若要用路由的方式则要注意关于路由约束准确编写,它将会直接影响到能否正确响应到目标;实例中也有关于使用通知的方式进行回调参数的回传问题;
实例代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
nsdictionary *curparams=@{kdesignermoduleactionsdictionarykeyname:@ "wujunyang" ,kdesignermoduleactionsdictionarykeyid:@ "1001" ,kdesignermoduleactionsdictionarykeyimage:@ "designerimage" };
switch (indexpath.row) {
case 0:
{
uiviewcontroller *viewcontroller=[[jiamediator sharedinstance]jiamediator_designer_viewcontrollerfordetail:curparams];
[self presentviewcontroller:viewcontroller animated:yes completion:nil];
break ;
}
case 1:
{
uiviewcontroller *viewcontroller=[[jiamediator sharedinstance]jiamediator_designer_viewcontrollerfordetail:curparams];
[self.navigationcontroller pushviewcontroller:viewcontroller animated:yes];
break ;
}
case 2:
{
nsstring *curroue=@ "jiascheme://designer/nativefetchdetailviewcontroller?name=wujunyang&id=1001&image=designerimage" ;
uiviewcontroller *viewcontroller=[[jiamediator sharedinstance]performactionwithurl:[nsurl urlwithstring:curroue] completion:^(nsdictionary *info) {
}];
[self.navigationcontroller pushviewcontroller:viewcontroller animated:yes];
break ;
}
case 3:
{
nsdictionary *userparadictionary=@{kusermoduleactionsdictionarykeyid:@ "1" };
uiviewcontroller *viewcontroller=[[jiamediator sharedinstance] jiamediator_user_viewcontrollerfordetail:userparadictionary];
[self.navigationcontroller pushviewcontroller:viewcontroller animated:yes];
break ;
}
default :
break ;
}
|
四:模块化结合私有pods方案
实例中只是把相关模块化的提取都在一个工程进行体现,最后还是要落实结合pods进行管理,把每个模块分开管理,不同的app可以简单通过pods指令就可以达到引入模块的效果,对于一些相同模块可以在不同的app重复引用,减小重复开发成本;
以上所述是小编给大家介绍的ios中关于模块化开发解决方案(纯干货),希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对服务器之家网站的支持!
原文链接:http://www.cnblogs.com/wujy/p/5919105.html