[需求及问题点]
有一天,产品提了个需求:最近,我们买了XXX的人脸识别SDK,但是很贵.所以我们想这样搞,把我们买的xxxSDK结合后端接口包装包装,再搞成一个给商户App们用的人脸识别SDK出来,同时,这个界面要美观大气….
-=-=-=-=-=-=-=
往往这种时候,把xxx的SDK用起来不难.但是对于SDK(.a或则.framework)中的静态库,我们不清楚里面用了哪些东西,而同时,我们在二次包装这个SDK的同时,如果使用了AFN等三方库,有可能导致使用这个二次SDK的小伙伴的App工程不能使用AFN等.
-=-=-=-=-=-=
解决的方法是, 使用CocoaPods打包成静态库,结合cocoapods-packager这个插件完成.framework静态库的打包(.a也可以)
1 首先了解.a与.framework的区别
一、库:
库是共享程序代码的方式,一般分为静态库和动态库二、静态库与动态库的区别:
静态库:连接时完整地拷贝至可执行文件中,被多次使用就有多份冗余拷贝。
动态库:连接时不复制,程序运行时由系统动态加载到内存,供程序调用,系统只加载一次,多个程序共用,节省内存。三、iOS静态库形式和动态库形式:
静态库:.a和.framework
动态库:.dylib和.framework四、framework静态库和动态库的区分:
系统的.framework是动态库,我们自己建立的.framework是静态库五、.a和.framwork的区别:
.a是一个纯二进制文件,.framework中除了有二进制文件外还有资源文件。
.a文件不能直接使用,至少要有.h文件配合,.framework文件可以直接使用。
.a + .h + sourceFile = .framework
2 具体操作(前提安装好cocoapods)
1. 使用cocoaPods开发静态库
为什么要使用cocoaPods的命令行工具来开发静态库?
其实主要不是真的用于开发本文中这个二次包装的SDK,这个二次SDK完全可以由你自己通过Xcode创建一个demo工程,在里面完成这个二次SDK的所有功能.
那么为什么要使用pod lib create xxxLib
来创建呢, 主要是为了得到这个
这是一个 SOCR 文件夹的全部内容 , 有个这个文件夹及文件,我们才可以接着操作
2.打开SOCR.podspec文件,修改类库配置信息,结果像这样。
#
# Be sure to run `pod lib lint SOCRLib.podspec' to ensure this is a
# valid spec before submitting.
#
# Any lines starting with a # are optional, but their use is encouraged
# To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html
#
Pod::Spec.new do |s|
# 名字要一致
s.name = 'SOCRLib'
# 版本要和git的tag版本一致
s.version = '0.2.0'
# 描述
s.summary = '活体识别SDK二次封装'
# This description is used to generate tags and improve search results.
# * Think: What does it do? Why did you write it? What is the focus?
# * Try to keep it short, snappy and to the point.
# * Write the description between the DESC delimiters below.
# * Finally, don't worry about the indent, CocoaPods strips it!
s.description = <<-DESC
描述信息:这是一个二次封装SDK的lib,将会用到很多著名第三方SDK,该功能用于打包包含这些著名第三方(该插件通过对引用的三方库进行重命名很好的解决了类库命名冲突的问题。http://www.cnblogs.com/brycezhang/p/4117180.html)
DESC
# 主页信息网址
s.homepage = 'https://github.com/tianNanYiHao/SOCRLib'
# 截图地址
# s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
# 证书 一般用下面的格式 如果用了其他的格式 需要相应的修改
s.license = { :type => 'MIT', :file => 'LICENSE' }
# 作者信息及邮箱
s.author = { 'tianNanYiHao' => '[email protected]' }
# spec配置文件的位置
s.source = { :git => '/Users/tiannanyihao/Desktop/SOCRLib', :tag => s.version.to_s }
# 媒体文件
# s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'
# 工程依赖系统版本
s.ios.deployment_target = '8.0'
# 源文件 包含 h,m
s.source_files = 'SOCRLib/Classes/**/*.{h,m}'
# 资源文件 .png/.bundle等(多个)
# 'SOCRLib/Assets/*.png',
s.resource_bundles = {
'SOCRLib' =>[
'SOCRLib/Assets/com.baidu.idl.face.faceSDK.bundle',
'SOCRLib/Assets/com.baidu.idl.face.model.bundle',
'SOCRLib/Assets/CWResource.bundle'
]
}
# 公开头文件 打包只公开特定的头文件
s.public_header_files = 'SOCRLib/Classes/head/SOCR.h'
# 调试公开所有的头文件 这个地方下面的头文件 如果是在Example中调试 就公开全部,需要打包就只公开特定的h文件
# s.public_header_files = 'Pod/Classes/**/*.h'
# 私有头文件
# subcfiles.private_header_files = "MyLibrary/cfiles/**/*.h"
# 是否是静态库 这个地方很重要 假如不写这句打出来的包 就是动态库 不能使用 一运行会报错 image not found
s.static_framework = true
# 载入第三方.a (如paynuc.a这种)
#s.vendored_libraries = 'SOCRLib/Classes/openssl/include/*.{a}'
# 载入第三方.a头文件
#s.xcconfig = { 'USER_HEADER_SEARCH_PATHS' => 'SOCRLib/Classes/openssl/include/openssl/*.{h}' }
# 链接设置 重要
s.xcconfig = {'OTHER_LDFLAGS' => '-ObjC'}
# 第三方开源框架(多个)
s.dependency 'Masonry'
# 第三方非开源framework(多个)
s.vendored_frameworks = [
'SOCRLib/Classes/framework/IDLFaceSDK.framework',
'SOCRLib/Classes/framework/PayEgisFace.framework'
]
# 系统动态库(多个)
s.frameworks = 'UIKit','CoreMedia','AVFoundation','Foundation'
# 系统类库(多个) 注意:系统类库不需要写全名 去掉开头的lib
s.libraries = 'stdc++'
end
以上.podspec文件的配置等详细的看下文的参考链接,不具体再写.
3.进入Example文件夹,执行pod install
这一步就是讲.podspec配置文件的信息加载的example工程里面去,让Example项目安装依赖项并更新配置。
========== 插入一条记录 ==========
在进入 Example文件路径后, 进行pod install ,
然后跳出 EXample文件 进行pod lib lint SOCR.podspec
命令时,
使用 pod spec lint SOCR.pods --verbose
去查看详细错误
说是找不到 SOCR.pods
此中一脸懵逼以及找问题查资料不予言表~反正就是:在EXample中
pod install
的时候,并没有完成EXample的配置更新
这里使用pod update
,如图,顺便更新了下 repo
到这步,算是保证了EXample的完整性
但是,问题还有
继续 验证lib
出现了如下验证错误,导致验证失败
解决方案
这里提示 引入头文件的时候, 使用 <>方式, 进去这个.m文件查看,发现
#import <Masonry>
这里改成#import "Masonry"
当然到最后, 我也没有验证通过, 原因是x86问题, SOCR使用的三方库并不支持x86
========== 插入一条记录 END ==========
4 提交git,并且给git仓库打上tag
记住,tag必须和.podspec文件中的 版本号一致!
同时,一旦修改了.podspec, 必须进入Example文件, 进行pod install
操作,然后退出到上级目录中 提交git
5 编译打包 .a/.framework静态库(前提是安装cocoapods-packager插件)
//打包静态framework 用这个
pod package 名称.podspec --force --no-mangle --embedded
//打包静态.a 用这个
pod package BZLib.podspec --library --force
3 一些问题补充
借用同事的文章
1.在使用别人的framework打自己的framework时,最后打出来发现是报错的,需要把别人的framework一起和自己的加到工程中。
这个怎么理解? 其实就是打包出静态.framework以后, 比如我打包后拿到 SOCR.framework(里面包含.h以及活体识别SDK的framework的.h头文件包)
在给别人使用的时候, 别人要把活体识别的SDK中包含的所有bundle以及.framework文件都导入项目中,不然跑不了
-=-=-
2.在第三方的framework的设置需要在spec中也同样设置好,在打包之后使用的工程中也同样需要设置
这个这么理解? 其实就是工程配置方面, 要和直接使用活体SDK的配置一样,这个打出来的静态库不会帮你主动配置某些系统动态库或者.tbd,需要使用这个二次封装的SDK的人,按照活体SDK的配置, 一毛一样的配置在工程里面(如:-ObjC之类的)
-=-=-=-
3.是否是静态库的开关, 在spec文件中一定要有 否则打出来的库是跑不起来的
-=-=-=-=-=-分隔线-=-=-=-=-=-=-
补充2
打出的.framework添加到项目工程中去的时候, 还是报错了: 打出的静态库SOCR.framework中使用了Masonry, 项目工程中也使用了Masonry. 报错在于两个库还是冲突了,这显然不符合预期,~~
-=-=-=
解决方案:在项目工程中,other link 里面存在Masonry的相对路径 将项目中的 other link 保留 -ObjC,其他的删除即可 如图~~
不过,问题是同事打出的.framework好似没出现我这个问题,他可以直接使用.而我必须在工程中处理,难道他的项目中 other Link 以及删除了 这些相对路径? 这个有待考证~~~
后经验证:
后经验证: 同事打出的包,在新的工程下跑的时候, 增加CocoaPods管理第三方库的支持,不过,他就是在
OtherLink
这里 改成了-ObjC
而不是CocoaPods install 以后默认生成的$(inherited) -ObjC -l"Masonry" -framework "Foundation" -framework "UIKit"
所以! 如果编译后还是 第三方静态库出现重复冲突问题, 可以 删除Other Link
下的 值, 替换为-ObjC
但个人测试过有风险,尤其全部替换掉的话, 工程中其他所使用的第三方库有可能管理不了 或者包 路径问题
注意:在使用pod 'Masonry' 等命令的时候, 工程中 other link flag 下 $(inherited) -framework "Masonry"
并不能删除!!!!! 删除会导致本项目工程中的Masonry不能使用
注意:不是不能删除,而是谨慎删除,如果实在是编译不过.可以修改为 -ObjC
当当当! 经过各种问题排除,我发现另一种解决方案!
在项目工程中的 Podfile, 经过pod init 创建后,默认是如下设置
图中第二个红色框
#use_framework!
此处把注释打开. 重新pod install , 重新编译工程,问题得以解决
对于 use_framework 这个是用于引入 swift 项目时, 需要添加的命令!
虽然,MasoLnry 并不是Swift项目, 但确实打开这个注释后, 工程能够运行!
这里打开 use_frameworks! 的意思是, 当使用动态库或swift第三方库的时候打开
然鹅! 我这里打出的确实是静态库, 我猜测,CocoaPods packager 插件调用Xcode打包的时候, 由于Xcode已经由Swift重构过, 所以是不是我们打出的静态库被认为是swift的静态库呢? 嗯, 有待考证!
记录一下吧,求大神解答
补充3
如果有关于资源加载 NSBundle 加载出错的bug.
那么这种情况下是由于 一般使用了某个 cocoapods的第三方库, 在使用CocoaPods package 打包 framework静态库的时候, 某个第三方的.bundle 资源虽然被打入这个静态framework(如 SOCR.framework)中,但实际无效.
解决方案 将 缺失的 某个第三方库的 .bundle 也导入 项目工程中
这个符合 上面所说的, 所有资源文件, 还是都要导一遍进入项目工程的!!!!
4 参考贴
使用CocoaPods开发并打包静态库
podfile中 use_frameworks! 和 #use_frameworks!区别
[Cocoapods]项目添加Cocoapods支持遇到的坑
通过CocoaPods打包framework
教我的同事写的SpecFile,可以的话去star一下~