项目背景
项目需求:
在已有项目A的前提下,抽离出一个和项目A有公共业务甚至模块的项目B,对于公共业务和模块当然是想维护一份代码实现两个项目同步;差异性在于两个项目的主题风格、项目模块架构不一致,有各自独立的业务和功能。
开发思路:
1、copy出另一个target实现项目B的业务模块;否定原因:两个项目的UI、架构不一致,在didFinishlaunch时就有差异,这种需要通过判断当前target的方式进行差异性开发,代码会比较杂乱和难以维护,图片等资源文件的分离使用也很难做到,另外还会增大开发包的体积
2、组件化开发;将公共基础模块和业务模块完全独立,使用pod管理不同组件,两个独立项目中进行各自的引用;否定原因:1)开发时间紧迫,人手不足,完全抽离组件不太理想 ,2)对于公共业务模块,两个项目的风格也不尽相同,某种程度上不能完全独立,3)现状是公共的业务模块也需要开发升级,抽离出去就需要不停维护,这种不稳定状态实际上不适合封装成组件
3、workspace工作区开发;采纳原因:1)不同于组件抽离,workspace内的静态库可以灵活抽取,不用像组件那样需要完全和外部解耦,可以把工具,三方库,category等自定义的和项目AB公共的业务模块都放在静态库, 2)由于静态库被项目引用后,图片资源通过bundle管理并且内置于项目,所以图片资源可以独立两份于两个项目中,3)公共业务只需在静态库中进行开发,两个项目引用静态库就好,静态库把外部使用的类的头文件设置好就可以了;
4、具体开发过程和遇到的问题,就往下看吧
项目配置
实际开发项目:一个workspace中一个静态库和两个项目
一、创建workspace工作区,cocoaTouch static library,projectA 和 projectB
1.1 workspace文件,不多说,直接上图,创建好后只会生成一个.xcworkspace文件,无其他文件生成1.2 创建静态库,由于苹果不支持开发者使用动态库,所以我们使用静态库(当然也可以选择动态库CocoaTouch Framework再改成静态库,还是有不同的)
1.3 两个项目的创建和日常创建项目无异,只是要和静态库一样,选择addTo和group到刚刚创建的workspace 最终的文件目录, 如图,两个项目一个静态库在一个workspace下
二、静态库的配置和项目中的使用
1、由于我们是在workspace中创建静态库,和独立的静态库使用不同(将.a文件拖拽入项目即可),而是在同一个workspace里,一起编译,具体往下看
1.1 前往静态库的target的Build phases,添加copy Files,以便项目中引用静态库暴露的头文件
1.2 项目引用.a静态库,Link binary with libraries下,add
libCommonLibrary.a
1.3 访问静态库的类,#import<CommonLibrary/CommonLibrary.h>
至此,项目中如何应用静态库和访问其类就结束了,心好累吧
静态库中xib和图片资源的管理
先解释一下这个过程,在项目A中引入.a静态库,对于[NSBundle mainBundle]返回的是项目A的bundle,那么.a中的xib和图片资源肯定是不在项目A的bundle中的,所以在.a中如何加载xib和图片呢,下面要讲,如何管理静态库中的资源文件
- 关于Bundle
对于bundle可以理解为一个捆绑包,个人理解bundle为一个独立的空间,而我们的可执行(executable)工程,打包完之后,也是一个捆绑包,我们称之为主bundle,这个主bundle包含了可执行代码,如各个viewcontroller的可执行代码,和相关资源例如图片资源等。
使用bundle管理静态库中的xib和图片资源
-
在静态库中创建bundle
-
xib文件的bundle,包含xib文件和xib上所加载的图片资源;最后我才发现的,xib用的图片必须和xib文件在同一个bundle中,xib才可以自动加载图片,如图
图片资源的bundle,和xib的bundle创建过程一致,图片的bundle其实完全可以看做是一个文件夹
-
至此,我创建了三个bundle,一个xib bundle,两个图片bundle(用于两个不同风格的项目,内部图片名称一样)
bundle如何使用
-
首先,bundle其实是静态库中的资源,因为当前是在项目中引用静态库,所以bundle需要拖拽进项目,供项目应用,如下
xib又和图片资源不同,不同点在于xib会变化,修改,那就需要静态库重新编译获取xib的bundle,而图片bundle上面说了其实就是个文件夹用于管理图片,所以在image的bundle创建好之后,就可以从静态库的target中删除了,它的管理就在项目中删加图片就是了
最后一点,现在bundle都在项目中了,静态库中访问Bundle其实就是在访问项目中的bundle,那怎么访问呢?我在静态库中定义了一个BundleTool
//其中一个项目的名称是qmp_ios,这样可以判断静态库当前是哪个项目加载的
+ (BOOL)isQMP{
return [[[NSBundle mainBundle]bundlePath]containsString:@"qmp_ios"];
}
//xib资源所在的bundle
+ (NSBundle *)commonBundle{
NSString *path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"CommonBundle.bundle"];
NSBundle *bundle1 = [NSBundle bundleWithPath: path];
return bundle1;
}
//qmp_ios引入的图片资源QMPimgBundle.bundle
+ (NSBundle *)qmpImgBundle{
NSString *path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"QMPimgBundle.bundle"];
NSBundle *bundle1 = [NSBundle bundleWithPath: path];
return bundle1;
}
//xinzhi_ios引入的图片资源XZimgBundle.bundle
+ (NSBundle *)xzImgBundle{
NSString *path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"XZimgBundle.bundle"];
NSBundle *bundle1 = [NSBundle bundleWithPath: path];
return bundle1;
}
+ (NSString *)getBundlePath: (NSString *) assetName{
NSBundle *myBundle = [BundleTool commonBundle];
if (myBundle && assetName) {
return [[myBundle resourcePath] stringByAppendingPathComponent: assetName];
}
return nil;
}
//用此类替代[UIImage imageNamed] 访问正确路径下的图片,bundle下的图片可以直接[UIImage imageNamed: bundle+图片名]
+ (UIImage*)imageNamed:(NSString*)imageName{
if ([BundleTool isQMP]) {
if ([imageName containsString:@"activity_user"]) {
NSLog(@"----");
}
return [UIImage imageNamed:[NSString stringWithFormat:@"QMPimgBundle.bundle/%@",imageName]];
}else{
return [UIImage imageNamed:[NSString stringWithFormat:@"XZimgBundle.bundle/%@",imageName]];
}
}
至此,bundle的创建和使用就此结束,而在项目内部(不牵扯静态库)用到的图片及xib和平时普通的用法还是一样的,因为项目内的Bundle就是xib和图片所在的bundle
静态库中的类和项目中的类如何相互调用
先讲,这种workspace机构,.a静态库只能被项目引用,不能引用项目(项目是可以引用项目的),那就意味着项目可以随意调用.a暴露出来的静态库中的类,那.a如何调用项目的类呢,以上讲了背景,我们的.a不是完全独立解耦的,项目A和B的公共业务模块还在.a里,所以.a 是存在需要调用项目业务模块的情况的;
- 静态库想要访问项目中的类?
如何调用? 我选择使用protocol,.a中定义了一套跳转协议AppPageSkipProtocol,和一个跳转工具类LibraryPageSkipTool(其中有一个代理属性id< AppPageSkipProtocol >delegate),需要跳转到项目中的地方,就用delegate调用跳转协议中的api去负责跳转,传入对应参数即可 - 跳转协议根据实际情况,看是否需要传入参数,定义block回调,根据实际情况随时修改添加都可以
- 跳转工具的delegate,在项目的didFinishLaunch里,用项目的跳转工具类实例作为delegate传给.a 的跳转工具类,如 [LibraryPageSkipTool shared].delegate = [AppPageSkipTool shared],项目的跳转工具类AppPageSkipTool内部,遵循协议AppPageSkipProtocol,实现协议方法就可以了
如何使用cocoaPods
上面讲过,静态库被引入项目之后,编译执行环境都是项目的环境,所以静态库需要的环境(build phases,三方库,系统库),项目中也需要实现配置,那么......
- Podfile如何配置, 如图,静态库中用到的三方库,项目中也需要引入,项目中另外可以添加项目内部需要的库
workspace 'WorkSpaceQMP.xcworkspace'
target 'CommonLibrary' do
platform :ios,'8.0'
project 'CommonLibrary/CommonLibrary.xcodeproj'
pod 'AFNetworking', '~> 3.1.0'
pod 'MJRefresh', '~> 3.1.0'
pod 'SDWebImage'
end
target 'qmp_ios' do
platform :ios,'8.0'
project 'qmp_ios/qmp_ios.xcodeproj'
pod 'AFNetworking', '~> 3.1.0'
pod 'MJRefresh', '~> 3.1.0'
pod 'SDWebImage'
pod 'YYText'
在对应的.a和项目的build phases的Link build Library中添加各自的libPod库
-
关闭workspace,此时文件目录,啊,多么愉快的feel
最后一个步骤,git管理
我的一个不知道是否错了的步骤: 创建仓库并初始化——>clone到本地文件夹——>代码复制到此文件夹——>add,commit,push——>发现两个项目是空,git提示subModule的相关信息
- 正确步骤: 创建仓库并初始化——>终端 cd到代码根目录——>git init初始化仓库,add,commit——>重点[git remote add origin 你的远程库地址] ——>获取远程库同步git pull --rebase origin master——>提交git push -u origin master
如果Git pull报错: refusing to merge unrelated histories的解决办法,执行下面的命令:git pull origin master --allow-unrelated-histories就可以了
参考:(https://www.cnblogs.com/xiangxinhouse/p/8254120.html)
创建本地库和fetch远程分支这些前面的步骤这里略过。可以自行百度。
解决办法:
1.cmd进入项目的根目录。
2.执行下面的命令:git pull origin master --allow-unrelated-histories。可以提交成功。
参考:https://www.cnblogs.com/eedc/p/6168430.html
遇到的问题
- 实际情况下的.a 配置是很麻烦的,需要引入很多第三方库,其实整个过程和实际项目中使用是一样的,能使用pod管理的尽量使用cocoaPods管理,三方库搞定一个再导入另一个,不然你都不知道哪个库出的问题
- 静态库配置出现arm64的问题,按照网上的做法大概就可以,千万记住,项目要配置静态库相同的环境,所需要的系统库一定全部导入,有时候这种情况也会出现arm64的问题,实际是项目没有导入需要支持的三方库
- 先配置静态库,xcode先build静态库,.a成功了,再build项目
- Archive问题:报错XcodeDefault.xctoolchain/usr/bin/strip: can't open temporary file:。。。,解决在.a的build setting中将strip linked product置为NO
- 打包完成发现最终的.ipa文件特别大,比原先大了二三十兆,网上搜索一些缩减静态库的方法如下:
1、在Build setting里面配置:
2、set Generate Debug Symbols to NO
3、Strip Debug Symbols During Copy flag set to Yes
4、同时Valid Architectures可以根据实际情况缩减;
目前Xcode默认支持iOS的指令集有armv7,armv7s,arm64;armv7只出现在iPhone4、iPhone4S的机器上;armv7s只出现在iPhone5、iPhone5C上;后面的机器一般都是arm64芯片;iPhone4、iPhone4S一般项目可以考虑不支持就在Valid Architectures将armv7删掉。(经测试,只要设置Valid Architectures就可以了,其他的设置效果不太明显)