iOS集成第三方常见坑

作者: 纸简书生 | 来源:发表于2017-09-05 20:41 被阅读670次

    最近这段时间,一直在研究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. 把生成的动态库用到项目中。

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

    相关文章

      网友评论

        本文标题:iOS集成第三方常见坑

        本文链接:https://www.haomeiwen.com/subject/baepjxtx.html