近来公司的公共库里有点小问题,但是公共库打成了framework,即使手上有源码也很难调试。网上百度了很多方法,有临时方法,也有比较好的方案,写一篇博客记录下来,送给正在调试framework的你,哈哈哈。
所以呢,这篇文章中你会看到:
这个方案呢,只能作为临时方案,因为这个方案还是有一定局限性的。先说实现方式吧。
首先呢,framework的库调试的痛苦在于第一你打不了断点,第二你也看不到堆栈信息。
所以从两方面入手逐个击破就好。 首先如果你有源码的话,只要打开你源码的工程build一下,你就会得到一个framework文件。
framework.png
至于打断点这个就比较玄幻了。首先打开你的主工程,然后从你framework的源码中把你要打断点的.m文件拖到主工程里下图的位置。为打开文件但不会引入文件,这个时候你打下断点试试你就会发现神奇的居然进入了断点。
断点
但是这种方式的缺点是,xCode关了你就还需要重新弄一遍,而且提交代码前要记得把framework替换回去。所以再请教了一个大神之后,他告诉我一个二进制切换方案,自己试了下很好用。由于这种实验我不可能那公司的公共库做实验,所以就完全自己从framework制作开始走了一遍流程。这里都记录一下。
1.framework制作在新版本的xcode上已经十分简单了。首先创建工程的时候选择Cocoa Touch Framework。
01.jpg
然后他会自动生成一个头文件的.h,我这里不想重新走一遍流程就偷懒用后面的图了。
02.jpg
2.然后你就开始创建你要打framework的文件就好,在别的地方写好了拖进来也好。然后在自动生成的头文件中引入你想对外暴露的.h文件就好。至此代码层级的事情就完成了。
3.接下来你需要再做3个buildsetting的设置。
03.jpg
4.然后你要设置你对外暴露的文件
04.png
5.接下来,如果你的库只想支持模拟器就选择模拟器build一下,想支持真机就Generic iOS Device build一下。
05.png
6.build成功之后再Products文件夹下则会生成一个framework文件。如果你是只支持某一种平台的话到这里已经结束了,但是如果你要支持真机和模拟器的话,我们还要将framework合并。这里讲一下合并的方法。就是终端输入一段命令:
终端指令:lipo -create +上面两个文件的路径 +-output+ 合成后文件的输出路径
例如我的是这个样子的:
06.png
这是将刚刚生成的文件替换到两个framework中的任意一个,然后被替换的framework就是支持两种平台的了。
07.png
更为详细的教程你可以看这里,《Xcode9.0 制作.framework》。
我说一个我做库的目录结构吧。 一般情况下我会这样,建一个根目录A,然后根目录下存放两个文件夹,一个叫Demo,一个叫你的库的名字,如DWFlashFlow。然后Demo里面放一个你库的使用Demo就好,以库命名的文件夹里面存放库文件。根目录下存放spec,最后连同根目录一起传到一个远程仓库中。
结构
文件编辑好了我们来编辑一下PodSpec。
Pods为我们提供了很多可选项,让你有丰富的定制可能,这里我说一下我常用的及必须的几个选项。我发一下我的config,你可以直接做模板改就好了
Pod::Spec.new do |s|
s.name = 'DWFlashFlow'
s.version = '1.0.1'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.summary = '网络请求库,核心基于AFN3.0,实现批量请求、链请求及依赖请求。Network request library, core based on AFN3.0, implements batch request, chain request and dependency request.'
s.homepage = 'https://github.com/CodeWicky/DWFlashFlow'
s.authors = { 'codeWicky' => 'codewicky@163.com' }
s.source = { :git => 'https://github.com/CodeWicky/DWFlashFlow.git', :tag => s.version.to_s }
s.requires_arc = true
s.ios.deployment_target = '7.0'
s.source_files = 'DWFlashFlow/**/{DWFlashFlow,DWFlashFlowManager,DWFlashFlowBaseLinker,DWFlashFlowAFNLinker,DWFlashFlowAbstractRequest,DWFlashFlowRequest,DWFlashFlowBatchRequest,DWFlashFlowChainRequest,DWFlashFlowCache}.{h,m}'
s.frameworks = 'UIKit'
s.dependency 'DWNetworkAFNManager', '~> 1.0.0'
end
对照着在Pod search时看到的字段有些属性你自然就懂了。
字段
这里着重说一下source_files这个字段的规则。
DWFlashFlow/**/{DWFlashFlow,DWFlashFlowManager,DWFlashFlowBaseLinker,DWFlashFlowAFNLinker,DWFlashFlowAbstractRequest,DWFlashFlowRequest,DWFlashFlowBatchRequest,DWFlashFlowChainRequest,DWFlashFlowCache}.{h,m}
首先路径是相对于PodSpec所在的目录的,因为我们将Spec放在了根目录下,并且根目录下的DWFlashFlow文件夹下方的是我们的库文件,所以第一个目录我写了一个DWFlashFlow,第二个路径是两个星号,这代表搜索方位将是当前文件夹下的所有文件(包括子文件夹中的文件)。前两个路径就声明了当前库文件的搜索范围就是根目录下DWFlashFlow中的所有文件。接下来在大括号之间的内容就是我们库文件的文件名在这些字符串间选择,然后后面的大括号之间是库文件的扩展名在这之间选择,通过这个路径,我们就确认了所有库文件的文件名。
另一个字段是source:
s.source = { :git => 'https://github.com/CodeWicky/DWFlashFlow.git', :tag => s.version.to_s }
分成两部分,前面一部分是告诉pods去这个地址拉取文件,当然就是填你远端的仓库地址啦,后面的tag就是告诉pods你要拉取的版本。这里你如果就像我这么写的话,就是取s.version的值了。
Spec文件是pod识别库的唯一文件,制作好了我们就要开始上传了。
1.首先如果你要发布的版本是0.0.1版本的话就给当前库打一个0.0.1的tag。然后推到远端。
2.spec中version改为0.0.1。
2.5 这时如果你是第一次制作的话你还要注册一下。
执行命令pod trunk register 邮箱地址 ‘用户名’ –verbose
3.本地校验一下库的合法性
cd到库的根目录,然后终端执行pod spec lint XXX.podspec 这一步会报warning和error,根据信息去修改就好了,如果你想忽略警告的话,命令后面记得加 --allow-warnings(第一个是两个短横线,第二个是一个)
4.上传库
终端执行pod trunk push XXX.podspec
然后你就可以开始等待了,当出现这个页面时就是上传成功了。
我没有图,copy一个。
成功
成功以后你就可以pod search 一下啦。如果你是第一次发布当前库的话,你要执行清除索引命令,因为索引是在上一次没有索引的情况下调用search生成的,里面不会有你的新库的信息,所以要清除旧的索引。
rm ~/Library/Caches/CocoaPods/search_index.json
并且此时执行下pod repo update
来更新一下仓库,再执行search就会搜索到你的库了,如果没搜到,那就是你弄错了。
更详细的公共库制作方式你可以看这里,《将代码提交到CocoaPods超详细的操作步骤和图解 》。
上面的步骤告诉了你如何上传至cocoapods的公共库,接下来我会再说一下上传到你的私有库的方法。
首先打开到.cocoapods/repos
目录下。
Repos
正常的话如果你没有私有库的话,你应该只有一个master文件夹。
在你的远端仓库中创建一个私有仓库,叫什么随你便了,复制一下仓库地址。
然后执行比如说你私有库的名字是REPO_NAME,远程仓库地址是SOURCE_URL,就执行命令
pod repo add REPO_NAME SOURCE_URL
这时候repos目录下应该会多一个你刚才创建仓库名的文件夹。 这里要解释一下,你刚刚创建的仓库只是作为私有库版本管理库存在,他应该是一个空的仓库,而不是你要做私有库的那个地址,希望你清楚。
然后你有了私有仓库,spec文件跟共有库是一样的,只是推得时候命令不一样,这时候你要用的命令是
pod repo push REPO_NAME xxx.podspec
其他都一样,这时候你已经可以通过pod search 搜索到你的私有库了。
不过当你需要安装库的时候你的podfile还需要做一定的改动,就是要告诉pod你的仓库的实际地址。
也就是说你的podfile大概是这个样子的:
source 'https://github.com/CocoaPods/Specs.git'
source 'SOURCE_URL'
target 'TestWork' do
pod 'Test_Frm_Private', '~> 0.0.1'
end
至此你也可以安装私有库了。
如果你想看到更详细的私有库推送,你可以看这里,《如何创建私有 CocoaPods 仓库》。
先说很重要的一个,二进制切换只支持私有库,这是大前提,一会解释原因。
终于走到最后一个流程了,其实podSpec是支持条件语句的,比如说这样:
Pod::Spec.new do |s|
s.name = 'Test_Frm_Private'
s.version = '0.0.1'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.summary = 'A test for pods.'
s.homepage = 'https://coding.net/u/codeWicky/p/Test_Frm_Private'
s.authors = { 'codeWicky' => 'codewicky@163.com' }
s.source = { :git => 'https://git.coding.net/codeWicky/Test_Frm_Private.git', :tag => s.version.to_s }
s.requires_arc = true
s.ios.deployment_target = '7.0'
s.frameworks = 'UIKit'
$lib = ENV['use_lib']
if $lib
puts 'Install Test_Frm_Private via Framework'
s.ios.vendored_framework = "Test_Frm_Private/Framework/Test_Frm_Prvt.framework"
else
puts 'Install Test_Frm_Private via SourceCode'
s.source_files = 'Test_Frm_Private/SourceCode/**/{Test_Frm_Prvt,Test_Frm_Prvt_Handler}.{h,m}'
end
s.preserve_paths = "Test_Frm_Private/**/*"
end
如上,当Podspec是上面这个样子的时候我们可以通过use_lib=1 pod install
让他命中if的第一个分支。原理大概就是pods会把pod前的所有字段作为一个字典供podSpec使用。既然有了条件分支,我们的目标就是根据不同条件改变pods的不同资源了。呐Spec的代码也比较清晰,没什么多余说的,只是framework资源的引入跟.h.m的图引入是有一点小小的区别的,就是对应的字段不一样。注意一下就好了。
那为什么说只有私有库能做这种切换呢?是因为只有podspec支持条件语句,但是当你传到共有库时podspec会被转换成podspec.json。目的也很清楚,开发者既然已经封装成framework了就一定不想让你看到.h.m,所以这个接口就没有给公共库留出来,只有私有库可以。
至此,你就可以通过use_lib=1 pod install
加载framework形式的库,pod install
加载.h.m的库了。
如果你想看更多关于二进制切换的内容,来这里吧《Pod二进制化 》。
好吧,为了追查framework中的一个问题,我饶了如此大一圈,你可能会说既然有缘吗为什么不直接引入.h.m的形式,原因是事实上framework的编译速度会高于.h.m形式的速度,并且一般较大的公司的库都是很成熟、经历了多个版本的修改的不希望你随意去改变的库,所以封装成framework也很方便管理,所以才有了这方面的需求,那就是这样。我也就当记录一下全过程,免得自己忘了。呐,这次没有广告啦,拜拜。
参考资料: