iOS集成第三方常见坑
最近这段时间,一直在研究webrtc,写iOS的时间很少,但是项目催得急,所以今天做了点iOS的工作。集成SDK本应该是很简单的一件事情,但当集成到一个维护长达五年的项目中,问题就多得一批。这里就顺便把常见的坑总结下。
小坑
笔者这里的小坑大都是指不需要我们改很多东西的情况,比如价格flag,或者设置一下search path之类的。
NSAppTransportSecurity
如果需要集成的SDK做得并不是那么好,很有可能在请求http的时候不成功。这个时候需要在plists文件设置一下,暂时退回到http协议。
- 在项目的info.plist中添加一个Key:NSAppTransportSecurity,类型为字典类型。
- 然后给它添加一个Key:NSAllowsArbitraryLoads,类型为Boolean类型,值为YES
pod 本地仓库太旧了或者pod search 搜索不到相关库
比如在执行pod install的时候出现找不到,比如什么头文件找不到之类的。解决办法就是升级下pod库
unrecognized selector sent to instance
很多时候这个问题是因为分类的问题。解决办法:
工程--->Building Setting ——》Linking———>other linker flags 添加 -ObjC 或者-all_load
文件件.h找不到
直接在Building Setting里面设置搜索路径。比如在有同学在集成支付宝的时候就经常遇到:
解决办法:
大坑
这里的大坑就是并不那么简单的就可以解决的,至少需要你去了解下一些基本原理才能搞定的。
Undefined symbols for architecture xxx
这个问题大部分情况下还是比较好解决的,解决思路大致分为如下几个:
- 大部分情况下是忘记添加了某个系统framework或dylib,比如你在项目中使用了sqlite3,但是没有添加libsqlite3.dylib,就会出现这个问题。解决办法是增加对应的framework或dylib。
- 如果是在C++里调用C函数,检查是否有添加extern "C",这可以通过观察错误提示中的函数名形式来决定,如果是C函数而以C ++的方式调用就需要添加extern "C"。
- 如果是把其它工程的xcodeproj文件加入到当前项目中,检查Build Phases中的Target Dependencies有没有添加依赖,以及General中的Linked Frameworks and Libraries有没有添加相关的.a文件。
- 如果添加.a文件编译无错而添加xcodeproj文件编译出错可参考3
- 如果添加.a文件编译出错,首先检查其对应的头文件是否添加正确,或者在Build Setting中有没有添加对应的Header Search Path路径;其次检查.a文件的c++编译选项与当前项目的c++编译选项是否一致;最后检查.a文件与当前项目的CPU架构信息是否一致
- 如果是使用了静态库,真机Debug测试时正常,而在执行for iOS Device测试时报这个错误,很可能是因为静态库支持的架构不全。出现这种情况是Build Setting中的Build Active Architecture Only在Debug下设为Yes,从而使得真机Debug测试正常。
符号文件冲突🎯
特征就是出现duplicate symbol这种标志。解决方法由易到难有如下几个:
- 对项目buildsetting里的other linker flags进行修改。详细的方法可以看看这里iOS 解决一个因三方静态库冲突产生的duplicate symbol的问题
- 这个方法就是从.a中把冲突的.o删去。详细步骤如下。
- 查看库所包含的CPU架构
打开终端输入如下命令:
cd /Users/fww/Desktop/temp
lipo -info temp.a
输出结果:
Architectures in the fat file: temp.a are: i386 x86_64 armv7 arm64
- 分离不同架构的静态库
也就是说这里将会从xxx.a中分离出i386 、x86_64、 armv7 、arm64 四个架构下的静态库,分别取名temp_i386.a,temp_x86_64.a,temp_armv7.a,temp_arm64.a: 在终端中继续输入如下命令:
lipo -extract_family i386 -output temp_i386.a temp.a lipo -extract_family x86_64 -output temp_x86_64.a temp.a lipo -extract_family armv7 -output temp_armv7.a temp.a lipo -extract_family arm64 -output temp_arm64.a temp.a
验证:
lipo -info temp_i386.a input file temp_i386.a is not a fat file Non-fat file: temp_i386.a is architecture: i386
lipo -info temp_x86_64.a input file temp_x86_64.a is not a fat file Non-fat file:temp_x86_64.a is architecture: x86_64
lipo -info temp_armv7.a input file temp_armv7.a is not a fat file Non-fat file: temp_armv7.a is architecture: armv7
- 删除冲突的xxx.o
ar -d temp_i386.a GDataXMLNode.o ar -d temp_x86_64.a GDataXMLNode.o ar -d temp_armv7.a GDataXMLNode.o ar -d temp_arm64.a GDataXMLNode.o
- 合并为新的库
删除冲突的库之后,将不同架构下的静态库再重新合并起来,取名:temp_new.a
lipo -create -output temp_new.a temp_i386.a temp_x86_64.a temp_armv7.a temp_arm64.a
底层库比如openssl之类的冲突
如果遇到这种问题就不能用上面讲的删除冲突的.o文件了。笔者亲自用如下方法解决了前公司一个非常大的问题。也就是在集成网易云信的时候,grpc中的boringssl和云信sdk中用到的openssl冲突了。刚开始网易云信的哥们直接说搞不定,后来在笔者亲自试验下,给网易云信的哥们说了方法,结果成功了。后来他们把这个步骤写在了官方网站上。
链接[ 解决 openSSL 和 boringSSL 冲突的问题
当时的思路出发点是来自于动态库和静态库加载机制的不同,静态库是一开始就加载了,而动态库是在运行的时候才加载。从而错开了两者符号加载的时机。
打包动态库脚本如下:
#change your project name here
project_name="targetName"
#archs,include iphone (armv7, arm64) and iphone simulator (i386, x86_64)
archs="armv7 armv7s arm64"
for arch in $archs
do
echo "building $arch..."
if [ "$arch" = "i386" -o "$arch" = "x86_64" ]
then
xcrun_sdk="iphonesimulator"
export cflags_config="-fembed-bitcode-marker"
else
xcrun_sdk="iphoneos"
export cflags_config="-fembed-bitcode -Qunused-arguments"
fi
xcodebuild clean build ARCHS=$arch -sdk $xcrun_sdk TARGET_BUILD_DIR="./build-$arch" BUILT_PRODUCTS_DIR="./build-$arch" OTHER_CFLAGS="$OTHER_CFLAGS $cflags_config"
done
cp -rf ./build-arm64/$project_name.framework $project_name.framework
echo "generate product..."
lipo -create `find ./build-* -name $project_name` -output $project_name.framework/$project_name
echo "clean cache..."
#rm -rf ./build ./build-*
echo "done!"
一个库必须使用动态库use_frameworks!,另一个不能使用动态库
今天遇到了这个蛋疼的问题。著名的加密库libsodium和集成的另一个sdk出现了这种问题。而且这个sdk依赖了一大堆其他库,比如AF,SD。
由于这个SDK必须是动态库,那么最终他依赖的第三方库也必须用动态库的方式引入。这点自己弄了好了才得出这个结论。
并且有项目之前用过老版本的AF,并且还改了一大堆代码,这个库又用的新版AF。真实蛋疼。
解决办法:
- 解决掉老版本AF和新版AF:通过重命名老版本AF所有文件,这样就彻底把老版本和新版本区分开。得出的经验是,如果你决定改第三方库源码,那就马上全部重命名,因为对你而言这个库已经不是第三方库了。。重命名的过程当然是痛苦的,因为用到的地方太多,这就是前人挖坑后人踩坑。
- 将libsodium打包成动态库:因为libsodium使用c语言写的,这里读者需要懂点linux/unix下的交叉编译知识。用automake来实现跨平台编译。幸好这个库已经把相关的config文件以及生成iOS平台的shell脚本写好了。****但这中间有一个严重的坑,坑,坑!
- 那就是不要去github项目主页上下载源码。因为下下来没有config文件。
- 应该去下打包好的项目。也就是xx.tag.xxx这种格式的。
- 然后下载下来,把iOS.sh从dist_build目录放到项目根目录下。
- 然后执行
sh iOS.sh
。过一段时间,.a库就生成好了。
- 生成的.a库打包成动态库,大致步骤就是建一个动态库工程,把生成的静态库项目拖到动态库项目中。设置好需要暴露的头文件,然后用上面的脚步跑一遍。
- 把生成的动态库用到项目中。
之所有没有把源码直接拖到项目中,尝试过但是一拖进去就一直保持。后来用这种方法简单粗暴!