iOS动态库制作以及遇到的坑

动态库和静态库的区别:Pass。直接进入动态库制作主题

零、实验环境

0.1.接下去内容在Xcode Version 8.3.3 (8E3004b)开发工具中完成。

一、基础准备

1.1.创建一个动态库MangoSDK,如下图


新建动态库1

1.2.创建一个测试工具类,如下图


创建一个工具类

1.3.在MangoSDK.h中#import "MGUtils.h",如下图

import工具类

1.4.点击工程 -> 在targets中选中MangoSDK -> Build Phases -> Headers,如下图所示,可以看到在动态库中创建的文件会自动添加到Build Phases中的project列表中,MangoSDK.h文件是处于Public列表中,所以外部只能看到MangoSDK.h这个头文件,由于我们动态库外部使用者需要调用MGUtilis.h中的方法,所以也需要将MGUtils.h拖拽到Public列表中.

查看build phases

二、编译动态库

2.1.选择动态库对应的Scheme,选择编译设备为对应的真机,如下图

选择Scheme

如果没有连接真机,也可以,只要选择Generic iOS Device选项也是可以编译出对应真机的动态库,如下图

Generic iOS Device选项

2.2.编译动态库(command + shift + B)后,在Xcode工程中的Products(这个目录不是工程源文件目录,而是编译后生成对应的沙盒目录)找到MangoSDK.framework文件,右键show in finder。如下图


show SDK in finder
SDK in finder

2.3.利用lipo -info 查看动态库所支持的CPU指令集,步奏如下

  1. 打开终端
  2. cd 进入MangoSDK.framework,这里需要注意进入的是MangoSDK.framework,而不是MangoSDK.framework所在目录
  3. 在终端输入$lipo -info MangoSDK

通过以上三个步奏后,在终端会显示出MangoSDK.framework所支持的CPU指令集,如下图所示,可以看出默认新建工程后所编译出来的动态库所支持的CPU指令集是arm64(因为我所使用的真机是6sp,其CPU指令集是arm64,后面会介绍CPU指令集是什么)

默认真机编译出的SDK所支持的CPU指令集.png

上面2.3.2需要额外注意一下,cd进入的是MangoSDK.framework,下面演示一下进入MangoSDK.framework所在目录,进行lipo -info的错误结果:
错误的framework路径

下面演示正确的lipo -info姿势:

正确的framework路径

三、指令集的介绍

3.1. 指令集种类

  • armv7|armv7s|arm64都是ARM处理器的指令集
  • i386|x86_64 是iOS模拟器的指令集

3.2.指令集对应的机型

arm64:iPhone6s | iphone6s plus|iPhone6| iPhone6 plus|iPhone5S | iPad Air| iPad mini2(iPad mini with Retina Display)
armv7s:iPhone5|iPhone5C|iPad4(iPad with Retina Display)
armv7:iPhone4|iPhone4S|iPad|iPad2|iPad3(The New iPad)|iPad mini|iPod Touch 3G|iPod Touch4

i386: iPhone5 | iPhone 4s | iPhone 4及前代产品的模拟器
x86_64: iPhone5s | iPhone 6 | ... | iPhone8的模拟器 

3.3.指令集兼容性说明
理论上指令集是向下兼容的,比如连接设备为arm64,那么是有可能编译出的动态库所支持的指令集为armv7s或者是armv7,这个具体看后面的介绍。但是向下兼容并不是说一个armv7s的动态库可以用在arm64架构的设备上,如果连接的设备是arm64的,而导入的动态库是没有支持arm64,那么在编译阶段即会报错

四、Xcode指令集的编译选项说明

4.1. 打开 Target -> Build Setting -> Architectures, 可以看到下图


architectures介绍.png

4.2. 介绍一下三个编译选项

  • Architectures:指明选定Target要求被编译生成的二进制包所支持的指令集
  • Build Active Architecture Only: 指明是否只编译当前连接设备所支持的指令集,如果是,那么只编译出连接设备所对应的指令集,如果否,则编译出所有其它有效的指令集(由Architectures和Valid Architectures决定)
  • Valid Architectures:指明可能支持的指令集并非Architectures列表中指明的指令集都会被支持
    编译产生的动态库所支持的指令集将由上面三个编译选项所影响,首先一个动态库要成功编译,则需要这三个编译选项的交集不为空,举几个例子:
示例1:
  Architectures 为armv7、arm64
  Valid Architectures 为armv7、armv7s、arm64
  Build Active Architecture Only 为 debug:YES release:NO
  链接设备:iPhone 6s (arm64架构的设备)
  编译(command + shift + B,保证Build Active Architecture Only 为 debug:YES 生效)
  结果:编译成功,生成的动态库支持的指令集为arm64

示例2:
  Architectures 为armv7、arm64
  Valid Architectures 为 armv7s
  Build Active Architecture Only 为 debug:YES release:NO
  链接设备:iPhone 6s (arm64架构的设备)
  编译(command + shift + B,保证Build Active Architecture Only 为 debug:YES 生效)
  结果:编译失败,因为当前是debug模式,在该模式下Build Active Architecture Only  为YES,表示只编译支持该指令集的动态库,
      但是由于Architectures和Build Active Architecture Only的交集中并不存在arm64,故三者的交集为空,故编译失败,无法生成动态库。

示例3:
  Architectures 为armv7、arm64
  Valid Architectures 为armv7、armv7s、arm64
  Build Active Architecture Only 为 debug:NO release:NO
  链接设备:iPhone 6s (arm64架构的设备)
  编译(command + shift + B,保证Build Active Architecture Only 为 debug:YES 生效)
  结果:编译成功,因为当前是debug模式,在该模式下Build Active Architecture Only  为NO,
    表示可以编译的结果可能为当前连接的设备所支持的指令集以及其向下兼容的指令集(armv64、armv7s、armv7),其和另外两个编译选项的交集为armv7,故所生成的动态库支持的指令集为armv7

所以,我们在步奏二、编译动态库中之所以会生成支持arm64的动态库是因为:

1.Build Active Architecture Only 为 debug:YES release:NO
2.我们使用的是编译,所以生效的选项为debug:YES,表示只生成当前机型对应的指令集(iPhone 6sp为 arm64)
3. Architectures和Valid Architectures的交集为armv7、arm64,故三个选项的最终交集只有arm64,所以生成的动态库只支持arm64指令集

五、制作支持iPhone 4及以后机型的动态库

5.1. 支持iPhone 4 及以后机型的动态库的意思是:生成的动态库支持的指令集为armv7、armv7s、arm64,所以Architectures的三个指令可以设置为:

  • Build Active Architecture Only 统一为NO(如果不修改,则不能使用编译去生成动态库,而是需要去修改scheme的run模式为release,并且command + R运行动态库,为了简便,这里统一设置为NO)

  • Architectures和Valid Architectures都设置为armv7、armv7s、arm64
    设置后的Architecture 为:


    支持所有真机的指令集的Architectures设置
  • 按照步奏 二、编译动态库 进行操作,最后通过lipo -info可以看到我们的动态库已经支持了armv7、armv7s、arm64,如下图所示:

    支持armv7、armv7s、arm64的动态库

六、制作支持iPhone 4及以后模拟器的动态库

6.1. 支持iPhone 4 及以后模拟器的动态库的意思是:生成的动态库支持的指令集为i386、x86_64,所以Architectures的三个指令可以设置为:

  • Build Active Architecture Only 统一为NO
  • Architectures和Valid Architectures最少都要包含armv7s、arm64(少了armv7s则不会支持i386,少了arm64则不会支持x86_64,挺神奇的,还没弄懂),设置后的Architecture 和步奏5.1第一张配图一样
  • 参照步奏 二、编译动态库 进行操作,不过需要将步奏2.1中编译设备选择为模拟器(任意iPhone模拟器都可以),最后通过lipo -info可以看到我们的动态库已经支持了i386、x86_64,如下图所示:
支持i386、x86_64的动态库

七、合并模拟器和真机动态库

7.0.合并动态库的前提是已经按照第五、第六步奏生成了两个对应的动态库,否则直接进行合并操作的话,会提示找不到文件,这一点可以翻下去看粘贴的脚本命令,仔细读一读就知道原理了。
7.1.通过步奏五和步奏六生成的动态库名字是一样的,但是不通用于模拟器和真机,如果不合并动态库的话,那么在切换模拟器和真机的时候需要同时切换动态库,这是我们不希望看到的,所以我们需要将模拟器和真机动态库合并为一个通用的动态库。
7.2. 由于在终端中使用lipo -create -output命令合并动态库有点复杂(搞了半个小时合并出来的动态库不能使用),故还是使用脚本生成动态库好了,步奏如下:

  • 新建一个target脚本,如下图
新建一个target脚本
  • 粘贴以下脚本内容到指定位置,如下图
添加脚本选项

粘贴脚本
if [ "${ACTION}" = "build" ]
then
INSTALL_DIR=${SRCROOT}/Products/${PROJECT_NAME}.framework

DEVICE_DIR=${BUILD_ROOT}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework

SIMULATOR_DIR=${BUILD_ROOT}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework


if [ -d "${INSTALL_DIR}" ]
then
rm -rf "${INSTALL_DIR}"
fi

mkdir -p "${INSTALL_DIR}"

cp -R "${DEVICE_DIR}/" "${INSTALL_DIR}/"
#ditto "${DEVICE_DIR}/Headers" "${INSTALL_DIR}/Headers"

# 使用lipo命令将其合并成一个通用framework  
# 最后将生成的通用framework放置在工程根目录下新建的Products目录下  
lipo -create "${DEVICE_DIR}/${PROJECT_NAME}" "${SIMULATOR_DIR}/${PROJECT_NAME}" -output "${INSTALL_DIR}/${PROJECT_NAME}"

#open "${DEVICE_DIR}"
#open "${SRCROOT}/Products"
fi
  • 编译新target,如图所示
选择cheme为脚本target
  • 编译完成后生成的framework位于工程源代码根目录下的Products文件夹下面,通过lipo -info可以看到动态库已经支持i386、x86_64、armv7、armv7s、arm64,如下图所示
支持模拟器和真机

八、使用动态库

8.1.将步奏七生成的动态库拖入新工程中,在新工程的AppDelegate.m中键入如下代码

#import <MangoSDK/MangoSDK.h>

@interface AppDelegate ()
@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    ViewController *vc = [[ViewController alloc] init];
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    [self.window makeKeyAndVisible];
    self.window.backgroundColor = [UIColor whiteColor];
    self.window.rootViewController = vc;
    [MGUtils mg_logMessage:@"framework test"];
    return YES;
}

8.2.在新工程的target中的General -> Embedded Binaries中添加MangoSDK.framework,如下图

Embedded Binaries中添加MangoSDK.framework

8.3.分别使用真机和模拟器运行新工程,执行成功,控制台输出如下:

成功输出

九、使用别人提供的动态库遇到的坑

9.1.第一类坑为别人提供的第三方库所支持的CPU指令集不全,出现的错误信息类似如下图:

动态库不支持当前连接设备

上面的截图中,我们连接的设备是iPhone 7 模拟器,其CPU架构为x86_64,但是我导入的framework是真机编译出来的动态库(支持的指令集为armv7、armv7s、arm64,并没有x86_64),所以就报了这样的类似的错误,进一步可以使用步奏 二、编译动态库 中的2.3小点查看别人提供的动态库所支持的指令集,这个坑属于那个提供动态库的同事造成的,让他去填就可以。

9.2.运行过程中出现 image not found异常或者控制台没有异常输出,如下图

没有往Embedded Binaries中添加MangoSDK.framework

出现这种问题的原因是我们没有往Embedded Binaries中添加MangoSDK.framework,所以进行如下操作即可解决这个异常。话说像讯飞之类的framework为什么不需要往Embedded Binaries添加对应SDK,即可以成功运行?这一点我还没有去研究,知道的小伙伴还请不吝赐言哟

Embedded Binaries中添加MangoSDK.framework

以上便是我近来和动态库打交道过程中遇到的坑,如果你完整了看完了我这篇文章,还是建议你可以去实践一下制作一个动态库。说多了都是泪,由于我之前没有制作过动态库,同事提供了我一个SDK后,我集成出现了第九点中的两个坑,寻同事,同事曰:别人可以,你为什么不可以?iOS你比我熟!如果当初我对动态库的整个流程熟悉的话,那么我就可以对其说:首先你提供的动态库只支持armv7、armv7s,我的手机是arm64架构指令集的,其次,因为别人使用的设备恰好是iPhone 5,所以没毛病。但是你必须给我重新编译一个支持i386、x86_64、armv7、armv7s、arm64的动态库。所以熟悉动态库的制作-嵌入整个流程还是必要的。

如果以上内容有错,还请不吝指出,谢谢大家。


上面我们将 i386 x86_64 armv7 arm64 几个平台都合并到了一起,所以使用动态库上传appstore时需要将i386 x86_64两个平台删除后,才能正常提交审核
在SDK当前路径下执行以下命令删除i386 x86_64两个平台
bak文件是备份目录,上传appstore之后需要替换回bak目录下的SDK

mkdir ./bak
cp -r MangoSDK.framework ./bak
lipo MangoSDK.framework/MangoSDK -thin armv7 -output MangoSDK_armv7
lipo MangoSDK.framework/MangoSDK -thin arm64 -output MangoSDK_arm64
lipo -create MangoSDK_armv7 MangoSDK_arm64 -output MangoSDK
mv MangoSDK MangoSDK.framework/

十、动态库包含第三方库怎么做?

按照惯例,给MangoSDK这个目录增加cocoapod支持,文件内容如下:

# Uncomment this line to define a global platform for your project
platform :ios, '11.0'

target 'MangoSDK1' do

pod 'AFNetworking', '~> 3.0'

end

pod install 之后 打开cocoapods工程,为MGUtils.m增加如下代码

#import "MGUtils.h"
#import "AFNetworking.h"
@implementation MGUtils

+(void)mg_logMessage:(NSString *)msg {
    NSLog(@"%s- %@", __func__, msg);
    AFHTTPSessionManager *session = [AFHTTPSessionManager manager];
    [session GET:@"https://www.baidu.com" parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {
        NSLog(@"成功");
    } failure:^(NSURLSessionDataTask *task, NSError *error) {
        NSLog(@"失败%@", error.description);
    }];
}
@end

然后按照上文的打包方式打包,将生成的MangoSDK.framework拖入目标工程
打开target-> General,修改内容如下图所示:


手动往Embedded Binaries添加链接

注意,如果你的主工程的cocoaPods是使用动态库的集成方式,即profile文件中有use_frameworks!,那么制作MangoSDK的时候,也需要在profile中声明集成方式为use_frameworks!。建议制作两个SDK,兼容集成Swift的项目。


通过pod下载的第三方库默认是满足上文中的architecture中设置的第一个指令集,比如arm64,如果我们的MangoSDK要打出64e、64、7s、7的SDK,那么也需要第三方库都包含这几个指令,那么接下去,就看看怎么让第三方库SDK包含我们想要的指令,如下图:


WeChat6c2784e9bdff6a6e2ae9a9b6aa24d90d.png

WeChatb86d84cff7e08fae04547a7d8fa62afa.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 207,113评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,644评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,340评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,449评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,445评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,166评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,442评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,105评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,601评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,066评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,161评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,792评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,351评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,352评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,584评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,618评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,916评论 2 344

推荐阅读更多精彩内容