一 创建.a
创建真机.a
- Valid Architectures设置:armv7|armv7s|arm64|i386|x86_64
- Architectures设置: armv7|arm64
- Build Active Architecture Only :NO
- 用真机运行或者选择Generic iOS Device (此时)
创建模拟器.a
- Valid Architectures设置为:armv7|armv7s|arm64|i386|x86_64
- Architectures设置: i386|x86_64
- Build Active Architecture Only :NO
合成支持模拟器的和真机的
- lipo -create 真机库.a的路径 模拟器库.a的的路径 -output 合成库的名字.a
注意:
如果Valid Architectures中添加了armv7s,就一定得添加armv7
二 查看.a文件包含的文件
取出armv7平台的包:
- lipo 查看的.a -thin armv7 -output armv7/抽出的.a
查看库中所包含的文件列表:
- ar -t armv7/抽出的.a
三 查看 .o 的反编译代码
在 Mac 上可以用 Mac 的反编译工具的otool 这个命令来反编译 .o 文件,比如 otool -tv xx.o
四 常见模拟器&真机所使用的CPU架构
苹果 A7 处理器支持两个不同的指令集,32位 ARM 指令集(armv6 | armv7 | armv7s),64位 ARM 指令集 (arm64)
i386/x86_64:
- i386 是针对 intel 通用微处理器 32位架构的,x86_64 是针对 x86 架构的 64位处理器
- 模拟器
ARMv8/ARM64
- iPhone 5s ~ iPhone X, iPad Air ~ iPad 5th
- ARMv7s:
- iPhone 5/5c, iPad 4th
- ARMv7:
- iPhone 3GS~4s, iPad 1st ~ iPad 3rd
五 Build Settings Architecture
Architectures
指定工程支持指令集的集合,设置多个 Architecture 则会在生成的包中包含多个指令集
默认 Standard architectures(armv7,arm64) (之前的Xcode版本中默认设置还包括armv7s,现在去掉了,估计是因为armv7s相较armv7优化不大,能向下兼容,而设置后还会增大二进制包的大小)
模拟器
- 选择什么生成什么架构的,如果是Standard architectures,则声称i386、x86_64
真机
- 根据 和 Valid Architectures 的交集确定最终数据包中包含的指令集。比如:armv7、armv7s、arm64
Valid Architectures
- 合法的指令集
- Architectures 和 Valid Architectures 的交集会确定最终数据包中包含的指令集
- 默认设置为 armv7 armv7s arm64
- 合法的指令集
Build Active Architecture Only
是否只针对当前连接设备所支持的指令集编译
默认 Debug 为 YES, Release 为 NO
六 lipo
lipo是管理Fat File的工具, 可以查看cpu架构, 提取特定架构,整合和拆分库文件
- 查看信息,支持的cpu架构列表
- lipo -info xxxx.a
- 整合成Fat文件
- lipo -create xxxx.a xxxx1.a -output xxxx.a
- 提取特定的cpu架构的thin文件
- lipo xxxx -thin cpu(armv7/arm64等) -output xxxx.a
- 移除掉特定的cpu架构的文件
- lipo -remove cpu(armv7/arm64等) xxxx -output xxxx
七 静态库与动态库
库(Library)是一段编译好的二进制代码,加上头文件就可以供别人使用。编译的时候只需要link一下,link(链接阶段)的不同的形式(静态或动态)决定库是静态库还是动态库
编译过程:
源文件 -> 预编译 -> 编译 -> 汇编 -> 链接 -> 可执行文件
一般会有两种情况要用到库:
- 某些代码需要给别人使用,但是我们不希望别人看到源码,就需要以库的形式进行封装,只暴露出头文件
- 公司中常用且变动很少的模块可以编译成库,这样做的好处一是可以节省编译时间,二来对于代码的管理也非常方便
7.1 静态库(.a、.framework)
静态库在链接(link)阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中,对应的链接方式称为静态链接。一个静态库可以简单看成是一组目标文件(.o/.obj文件)的归档集合
静态库的特点:
- 静态库嵌入App
- 在App项目编译的时候会被编译到目标程序中,所以得到的App二进制文件会变大
- 浪费空间和资源的弊端
- 不同的应用程序如果调用相同的库,那么在内存里会有多份该共享库的实例
- 静态库更新,程序需要重新编译
- 静态库依赖的其他类库,需要手动导入
- 减少耦合
- 不可以包含其他静态库
- 每一个静态库都是独立的,不会重复引用
- StaticLibrary需要设置头文件搜索路径,Framework不需要
- 共享运行环境(一个应用程序中)
- 假如其运行环境中包换库中同一个类,会发生代码冲突,必须剥离其中一方的此类,然后共享此类
framework和.a两种静态库的区别
- .a是一个纯二进制文件
- .framework中除了有二进制文件之外还有资源文件
- .a文件不能直接使用,至少要有.h文件配合
- .framework文件可以直接使用。
- .a + .h + sourceFile = .framework。
- .a只是静态库
- framework既可以是静态库也可以是动态库
7.2 动态库(.framework、.dylib、.tbd)
动态库在链接(link)阶段,不会被链接到目标代码中,而是在程序运行是才被载入
动态库(也称共享库)的特点:
程序运行时才被载入
- 避免浪费空间和资源的弊端
- 不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例
- 增量更新
- 动态库更新,程序不需要重新编译
动态库可以包含静态库,也能自动link所需要的依赖库
与运行环境隔离
- 运行环境中包换库中同一个类,不会发生冲突,同名的两个类会在各自的环境中独立运行,互不干扰,哪怕是单例类
注意:
1 苹果在iOS 8之前并不支持自己开发的framework.
2 自己开发的framework无论是动态还是静态的,都要复制到目标程序,苹果又把这种Framework叫做Embedded Framework
3 Xcode中需要导入我们自己制作的动态库时,需要在Xcode中的Build phase->Copy Files选项添加,其中destination选择“Frameworks”,不然会报错:image not found。
7.3 判断库是动态库还是静态库,用file命令
$ cd myframework.Frameworks
$ ls -l
myframework
$ file myframework
#静态库,显示archive
> myframework.Frameworks: Mach-O universal binary with 2 architectures: [arm_v7:current ar archive] [arm64]
> myframework.Frameworks (for architecture armv7): current ar archive
#动态库,dynamically
> myframework: Mach-O universal binary with 2 architectures: [arm_v7] [arm64:Mach-O 64-bit dynamically linked shared library arm64]
> myframework (for architecture armv7): Mach-O dynamically linked shared library arm_v7
7.4 合并两个静态库
lipo -create 模拟器静态库(动态库) 真机静态库(动态库) -output 新静态库的名称(动态库)
注意:
动态库不是合成.framework,是合成.framework里面的一个黑色文件(二进制文件),生成的也是这个黑色文件,只需将合成的黑色文件替换就好了
如果制作的是动态库的话,一定要embeded binaries中将库导入一下(target —> General —> Embedded Binaries下添加你的动态链接库)
- 或者你也可以将动态库当做一个资源文件使用在Build Phases->Copy Bundle Resources中添加,然后就可以用bundle的方式使用动态库了
如果库中包含系统的分类,一定要在target linker flag 里面配置-ObjC以及-all_load
NSString *documentsPath = [NSString stringWithFormat:@"%@/Documents/FWPaySdk.framework/FWPaySdk",NSHomeDirectory()]; NSError *err = nil; NSBundle *bundle = [NSBundle bundleWithPath:documentsPath]; if ([bundle loadAndReturnError:&err]) { NSLog(@"bundle load framework success."); } else { NSLog(@"bundle load framework err:%@",err); } Class worder = NSClassFromString(@"FWorder"); FWorder * order = (FWorder *)[[worder alloc] init];
八 Framework、.a、.dylib/.tbd
8.1 .Framework(框架)
Headers
- Framework需要暴露的头文件
binary文件
- 整个Framework的核心,所有代码都被编译成了这样一坨二进制文件,
- 添加的依赖库不会被编译进来,用的时候需要重新link其他依赖库。
.bundle
- 资源文件都打包放在这里。在制作Framework的时候不可以把图片直接放在项目中,否则制作好之后图片是一张一张的出现在项目中非常乱,需要新建一个bundle将图片放进去,这里的bundle提供整个SDK的图片资源
- 图片放进bundle之后不可以用
[UIImage ImageWithName:]
读取图片。要先找到bundle包再拿图片
#define BUNDLE_NAME @"Resources.bundle" #define BUNDLE [NSBundle bundleWithPath: [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent: BUNDLE_NAME]] #define LOADIMAGE(name) [UIImage imageWithContentsOfFile:[[BUNDLE resourcePath] stringByAppendingPathComponent:name]]
Info.plist和Modules
- 记录Framework的版本等相关信息,一般都会删掉
注意:
1 Framework是动态库还是静态库,取决于编译成的Mach-O(就是那个二进制文件)
2 Buiding Setings -> Mach-O Type: 默认选择的是Dynamic Library(动态库),选择Static Library是静态库
8.2 .a
- 这类静态库与Framework基本类似,在打包成.a文件的同时,还需要提供头文件,而Framework编译完成暴露的头文件都已经放好了
8.3 .dylib/.tbd
- 基本上都是系统提供的动态库
九 如何选择使用Framework还是StaticLibrary
- 假如不想在同一个App中包含多份三方库(减小包大小),可以使用StaticLibrary,库本身和App共享第三方库。但是产出物的结构可能会比较乱。
- 假如不想考虑和App的代码冲突问题,库本身独立使用需要的库,想提供比较好的库结构,可以使用Framework。但是假如库本身和App都使用了同一个三方库,会存在两份三方库,会增加包大小。
十 iOS自动化版本号和编译号
agvtool
- 是一个增加版本号的命令行工具
版本号(Version)
- 给用户看的,用于标明当前的发行版本
- 存储在
Info.plist
文件的CFBundleShortVersionString (Bundle versions string, short)
中
编译号(Build)
- 内部使用的,用于标明未发行或者已内部发行的应用程序
- 存储在
Info.plist
文件的CFBundleVersion (Bundle version)
中
注意:如果应用程序包含了多个目标(target),
agvtool
工具将会把所有目标都设置为同一个版本号和编译号。
10.1 启用 agvtool
- 设置 Current Project Version 为选定的值
- Xcode 工程文件
project.pbxproj
包含了CURRENT_PROJECT_VERSION (Current Project Version)
编译设定,这个编译设定指定了当前工程的版本。agvtool 会搜索此编译选项。如果这个值存在就运行,否则不运行,这个值用来更新编译号
- Xcode 工程文件
- 设置 Versioning System 为 Apple Generic
- 默认情况下苹果不使用任何版本系统,设置为 Apple Generic 确保 Xcode 包含全部的 agvtool 生成的版本信息
10.2 设置版本和编译号
- 进入到包含 .xcodeproj 工程文件的目录
- 更新版本号
/*
注意:
如果存在多个Xcode需要选择一个,否则报错。
sudo xcode-select -switch /Applications/Xcode.app/Contents/Developer
*/
agvtool new-marketing-version 2.1
- 更新编译号
//自动更新编译号
agvtool next-version -all
//更新编译号
agvtool new-version -all 2.1
- 查看版本号
agvtool what-marketing-version
- 查看当前编译号
agvtool what-version
十一 其他:
1 User Head Search Paths 和Head Search Paths的区别
共同点
- 用来存放 Project 中头文件的搜索根源,没有被add到项目里的头文件
区别
Xcode设置了Header Search Paths,则一定会去搜索的。
UserHeader Search Paths只有在Always Search User Paths(是否搜索用户路径)为Yes时才会被搜索
- <> 是从系统目录空间 (对应 Header Search Paths)中搜索文件,只在Header Search Paths 中搜索
- “” 是从用户目录空间(对应 User Header Search Paths)中搜索文件,如果没有则去Header Search Paths 搜索
2 iOS Code Sign On Copy
xcode中embed的framework,都是需要签名的。如果在生成framework时,签过名了,就不用在选上 code sign on copy选项了。如果没有签过名,必须选上,不然无法在真机上运行!
3 Link Binary With Libraries的Status含义
- Optional
- 根据系统版本选择加载framework
- Require
- 直接加载framework
对于链接的系统动态库而言,由于ios的系统是在不断的升级中,必然会引入不少新功能和新API,而大多数都是会以.framework的方式提供给开发者。如果开发者使用了这些新引入的.framework,那么因为app的用户的系统版本不是统一的,所以用户机器上不一定都存在该动态库,此时就应该把链接的这些新的.framework改成Optional的,然后在程序代码中去动态判断用户版本,来启动对应的新功能,如果不修改成Optional而保持Required的话,在部分不存在该动态库用户的机器上,就会导致app一启动就退出,因为找不到对应的动态库
4 iOS动态库Framework没有import相应文件的提示 或 Could not build module
原因是没有把对外部公开的类,放到public里面
注意:如果Public中的头文件,import了其他的头文件,也要放倒public里
参考
2.iOS开发——制作同时支持armv7,armv7s,arm64,i386,x86_64
3.iOS Device Compatibility Reference
5.iOS Build Settings Architectures
6.关于Xcode “Build Setting”中的Architectures详解
10 iOS 最新framework和.a静态库制作及使用全解(含工程套工程,多工程联调)