1 workspace 管理多个project
1.1 应用场景
项目有可能要使用其他的项目文件,或者引入其他的静态库文件,workspace 即可以单独管理多个项目,又可以通过配置,让各个项目相互依赖,可以减少来回切换项目
1.2 步骤
- 1 File > New Workspace
- 新建一个空的workspace
- 2 File > New Project
- 建一个工程myProduct和一个framework工程myStaticFramework
- 3 File > Add file to workspace
- 将myProduct和myStaticFramework工程添加进去
- 4 Build Setting > User Head Search Paths (或者Head Search Paths)
- 添加myStaticFramework路径
- 5 Build Phases > Link Binary With Libraries
- 添加myStaticFramework工程编译出来的myStaticFramework.framework
- 6 Copy Files
- (1)Destination改为Framework
- (2)添加myStaticFramework工程编译出来的myStaticFramework.framework
2 一个Project多target工作
2.1 应用场景
在实际开发过程中难免会遇到同一个项目要发多个版本来服务于业务的需求,而两个版本仅有微小的不同.
比如我们要发的版本有:
1.数据版DataTrack-Debug(给数据组,用于埋点统计)
2.测试版Test-Debug(给测试组,用于上线前测试)
3.企业版Enterprise-Debug(给第三方测试,用于更专业更客观的测试)
4.线上版Online(给测试组,用于测试后端上线后对前端的影响)
2.2 步骤
- 复制target
- 选择需要复制的target–>右键点击–>选择Duplicate
- 新target在项目中会生成
- XXX copy target
- XXX copy Scheme
- XXX copy-Info.plist
- 改名字
- 修改Target名字,双击修改
- 修改新的Plist文件
- 如果需要挪动plist,则挪动完了以后需要关联一下
- target–>General–>Choose Info.plist File
- 修改Scheme名称
- Target–>Edit Scheme–>Manage Schemes–>修改名称
3 静态库与动态库
库(Library)是一段编译好的二进制代码,加上头文件就可以供别人使用。编译的时候只需要link一下,link(链接阶段)的不同的形式(静态或动态)决定库是静态库还是动态库
编译过程:
源文件 -> 预编译 -> 编译 -> 汇编 -> 链接 -> 可执行文件
一般会有两种情况要用到库:
- 某些代码需要给别人使用,但是我们不希望别人看到源码,就需要以库的形式进行封装,只暴露出头文件
- 公司中常用且变动很少的模块可以编译成库,这样做的好处一是可以节省编译时间,二来对于代码的管理也非常方便
3.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既可以是静态库也可以是动态库
3.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。
3.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
3.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];
4 Framework、.a、.dylib/.tbd
4.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是静态库
4.2 .a
- 这类静态库与Framework基本类似,在打包成.a文件的同时,还需要提供头文件,而Framework编译完成暴露的头文件都已经放好了
4.3 .dylib/.tbd
- 基本上都是系统提供的动态库
5 如何选择使用Framework还是StaticLibrary
- 假如不想在同一个App中包含多份三方库(减小包大小),可以使用StaticLibrary,库本身和App共享第三方库。但是产出物的结构可能会比较乱。
- 假如不想考虑和App的代码冲突问题,库本身独立使用需要的库,想提供比较好的库结构,可以使用Framework。但是假如库本身和App都使用了同一个三方库,会存在两份三方库,会增加包大小。
6 iOS自动化版本号和编译号
agvtool
- 是一个增加版本号的命令行工具
版本号(Version)
- 给用户看的,用于标明当前的发行版本
- 存储在
Info.plist
文件的CFBundleShortVersionString (Bundle versions string, short)
中
编译号(Build)
- 内部使用的,用于标明未发行或者已内部发行的应用程序
- 存储在
Info.plist
文件的CFBundleVersion (Bundle version)
中
注意:如果应用程序包含了多个目标(target),
agvtool
工具将会把所有目标都设置为同一个版本号和编译号。
6.1 启用 agvtool
设置 Current Project Version 为选定的值
- Xcode 工程文件
project.pbxproj
包含了CURRENT_PROJECT_VERSION (Current Project Version)
编译设定,这个编译设定指定了当前工程的版本。agvtool 会搜索此编译选项。如果这个值存在就运行,否则不运行,这个值用来更新编译号
- Xcode 工程文件
设置 Versioning System 为 Apple Generic
- 默认情况下苹果不使用任何版本系统,设置为 Apple Generic 确保 Xcode 包含全部的 agvtool 生成的版本信息
6.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
7 其他:
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.常见模拟器&真机所使用的CPU架构
- 模拟器使用的CPU架构
- iPhone3gs~iPhone5 : i386
- iPhone5s~iPhone7plus : x86_64
- 真机设备CPU架构
- iPhone3gs~iPhone4s : armv7
- iPhone5~iPhone5c : armv7s(只要支持armv7即可)
- iPhone5s~iPhone7plus : arm64
8 参考:
3 iOS 最新framework和.a静态库制作及使用全解(含工程套工程,多工程联调)
5 App Extension Programming Guide
7 Automating Version and Build Numbers Using agvtool
mechanism 机制 conditionally 有条件的 Explore 探索 implement实施
Deploying 部署 Adopting 采用 technologies 技术 facilitate 促进 corresponding 对应的