iOS集成第三方常见坑

0.466字数 1997阅读 1395

最近这段时间,一直在研究webrtc,写iOS的时间很少,但是项目催得急,所以今天做了点iOS的工作。集成SDK本应该是很简单的一件事情,但当集成到一个维护长达五年的项目中,问题就多得一批。这里就顺便把常见的坑总结下。

小坑

笔者这里的小坑大都是指不需要我们改很多东西的情况,比如价格flag,或者设置一下search path之类的。

NSAppTransportSecurity

如果需要集成的SDK做得并不是那么好,很有可能在请求http的时候不成功。这个时候需要在plists文件设置一下,暂时退回到http协议。

  1. 在项目的info.plist中添加一个Key:NSAppTransportSecurity,类型为字典类型。
  2. 然后给它添加一个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

这个问题大部分情况下还是比较好解决的,解决思路大致分为如下几个:

  1. 大部分情况下是忘记添加了某个系统framework或dylib,比如你在项目中使用了sqlite3,但是没有添加libsqlite3.dylib,就会出现这个问题。解决办法是增加对应的framework或dylib。
  2. 如果是在C++里调用C函数,检查是否有添加extern "C",这可以通过观察错误提示中的函数名形式来决定,如果是C函数而以C ++的方式调用就需要添加extern "C"。
  3. 如果是把其它工程的xcodeproj文件加入到当前项目中,检查Build Phases中的Target Dependencies有没有添加依赖,以及General中的Linked Frameworks and Libraries有没有添加相关的.a文件。
  4. 如果添加.a文件编译无错而添加xcodeproj文件编译出错可参考3
  5. 如果添加.a文件编译出错,首先检查其对应的头文件是否添加正确,或者在Build Setting中有没有添加对应的Header Search Path路径;其次检查.a文件的c++编译选项与当前项目的c++编译选项是否一致;最后检查.a文件与当前项目的CPU架构信息是否一致
  6. 如果是使用了静态库,真机Debug测试时正常,而在执行for iOS Device测试时报这个错误,很可能是因为静态库支持的架构不全。出现这种情况是Build Setting中的Build Active Architecture Only在Debug下设为Yes,从而使得真机Debug测试正常。

符号文件冲突🎯

特征就是出现duplicate symbol这种标志。解决方法由易到难有如下几个:

  1. 对项目buildsetting里的other linker flags进行修改。详细的方法可以看看这里iOS 解决一个因三方静态库冲突产生的duplicate symbol的问题
  2. 这个方法就是从.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。真实蛋疼。

解决办法:

  1. 解决掉老版本AF和新版AF:通过重命名老版本AF所有文件,这样就彻底把老版本和新版本区分开。得出的经验是,如果你决定改第三方库源码,那就马上全部重命名,因为对你而言这个库已经不是第三方库了。。重命名的过程当然是痛苦的,因为用到的地方太多,这就是前人挖坑后人踩坑。
  2. 将libsodium打包成动态库:因为libsodium使用c语言写的,这里读者需要懂点linux/unix下的交叉编译知识。用automake来实现跨平台编译。幸好这个库已经把相关的config文件以及生成iOS平台的shell脚本写好了。****但这中间有一个严重的坑,坑,坑!
    • 那就是不要去github项目主页上下载源码。因为下下来没有config文件。
    • 应该去下打包好的项目。也就是xx.tag.xxx这种格式的。
    • 然后下载下来,把iOS.sh从dist_build目录放到项目根目录下。
    • 然后执行sh iOS.sh。过一段时间,.a库就生成好了。
  3. 生成的.a库打包成动态库,大致步骤就是建一个动态库工程,把生成的静态库项目拖到动态库项目中。设置好需要暴露的头文件,然后用上面的脚步跑一遍。
  4. 把生成的动态库用到项目中。

之所有没有把源码直接拖到项目中,尝试过但是一拖进去就一直保持。后来用这种方法简单粗暴!

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
  • 图文/夏天的小蘑菇 喜欢读书,喜欢写字,喜欢旅行,喜欢走走停停。这些风景图是去年夏天7月初在加拿大的爱德华王子岛和...
  • 展开自己的联想,记住这下面的14对单词感觉很不错,虽然还只是记住汉字。 练习5 故事记忆法 我的故事是这样子的:我...
  • 他住在大海边 等一封信 朝阳带来的光 是他一天的希望 海鸥飞过他的信箱 他在长椅上对着太阳 海风吹过 说他的信正在路上