.a & .framework

1.库?

库是代码的集合、共享代码的一种方式。分为:

  • 开源库:源代码公开,能看到具体的实现。(如SD、AF)
  • 闭源库:源码不公开,是经过编译后的二进制文件,看不到具体实现。

其中,闭源库有两种形式:

  • 静态库:.a 和 .framework
  • 动态库:.dylib 和 .framework

问:framework 为啥即是静态库又是动态库?

答:系统提供的.framework是动态库,我们自定义的是静态库。

系统提供的有UIKit.framework等,自定义的包括AFN.framework等。

在早期,很多应用基于.framework动态库实现热更新,而苹果不允许这种行为,它禁止使用自定义动态库的应用上架。我们自己建的.framework中 Mach-O Type 需要设置为 Static Library,即静态库。现在苹果已经允许我们将.framework设置成动态库,只是苹果会在集成自定义动态库的应用上架时,再经过一次AppStore的签名,防止在线更新动态库。当然,自建的.dylib还是不允许。

2.静态库

静态库是在编译阶段进行链接,成为应用可执行文件的一部分,会增加包体的大小。

多个应用使用相同的静态库时,各应用都会完整地复制一份到可执行文件中,内存中有冗余。

因为可执行文件变大,启动时需加载的内容变多,可能会导致应用启动变慢。

3.动态库

动态库是在启动阶段进行链接,不包含在应用的可执行文件中,可有效减小包体大小。

多个应用使用相同的动态库时,系统只会在内存中加载一次,多个应用共享,节省内存。

动态库在多个应用程序间共享,系统和其他应用可更新此库,而不用更新所有依赖它的应用。

4.封装格式

.a与.framework都是二进制文件的封装格式。

1.静态库.a

.m等文件在编译后会变成.o文件,而.a是多个.o的集合。

.o是Mach-O文件类型,每个.o都包含 Mach header、Segment、section、Blob。

多个.o中可能有相同的 Mach header 等冗余,所以包体会大于动态库的framework。

2.动态库.framework

.framework可将库的代码与资源文件打包到一起,方便管理和分发。

1
.framework = (.a + .h + sourceFile)

.a是一个纯二进制文件,.framework 中除了有二进制文件之外还有资源文件。

.a不能直接使用,至少要有.h文件配合,.framework 文件可以直接使用。

framework中所有.o文件的前三段合并到了一起,只有一个公共的 Mach header、Segment、Section,从而优化了.a中每个.o都含这些头块区的冗余问题,减小了包体。

自己建的 framework 虽然叫动态库,但它的实质却是 Embedded Framework。

  • 基于沙盒,最终打包到ipa的/framework目录(包括pods),与其他应用隔离。
  • 需要签名,即使跟别的应用同名,也不共享。
  • 可以上架,但不允许热更新(AppStore会再一次签名)。

不同应用使用同名自定义动态库framework,内存中并非只加载一份,而是分别打包、签名、加载各自的。它只是保留了与系统动态库相同的链接方式,但是仅能在各自的应用中使用。

5.制作.a

  • 新建 Cocoa Touch Static Library 工程,编写逻辑代码。
1
2
3
4
5
6
7
8
9
10
11
12
//.h文件
@interface Aframe : NSObject
- (void)mathAdd;
@end

//.m文件
#import "Aframe.h"
@implementation Aframe
- (void)mathAdd{
NSLog(@"1 + 1 = 2");
}
@end
  • Targets->Build Settings 参数设置:
1
"Build Active Architecture Only" 设置为"NO"
  • Targets->Build Phases 参数设置:
1
"Copy Files" 中添加需要暴露的.h 文件
  • Run 修改 scheme
1
选择左上角静态库->Run->Info->Build Configuration 设置为Release
  • 编译生成静态库.a文件:
1
2
分别选择真机和 iPhone 模拟器进行编译。
找到真机和模拟器编译成功生成的.a文件,Show In Finder,分别在 Release-iphoneosRelease-iphonesimulator目录中。
  • 合成通用版的静态库.a文件
1
2
打开终端输入以下命令行:
lipo -create /xx.a(真机.a文件路径) /xx.a(模拟器.a文件路径) -output Desktop/xx.a
  • 测试自己制作的静态库文件

将.h 以及上步导出的.a文件导入自己的工程中,调用.h 中的接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#import "AppDelegate.h"
#import "Aframe.h"

@implementation AppDelegate

- (void)testLibA{
Aframe *aframe = [Aframe new];
[aframe mathAdd];
}

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[self testLibA];
return YES;
}
@end

6.制作.framework

  • 新建 Cocoa Touch Framework 工程,编写逻辑代码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//.h文件
#import <Foundation/Foundation.h>

@interface MathTool : NSObject
- (void)mathAdd;
@end

//.m文件
#import "MathTool.h"

@implementation MathTool
- (void)mathAdd{
NSLog(@"1 + 1 = 2");
}
@end
  • 更改参数:
1
2
TARGETS->Build Settings->Dead Code Stripping 设置为NO。
TARGETS->Build Settings->Link With Stantard Libraries 设置为NO。
  • 设置需要暴露的头文件
1
TARGETS->Build Phases->Headers 的 public选项下 把需要暴露的头文件拖进来。
  • 在新建项目时,自动生成的xxx.h文件里将要暴露的头文件 import 进来。
1
2
3
4
5
6
7
8
9
10
11
#import <UIKit/UIKit.h>

//! Project version number for Framework.
FOUNDATION_EXPORT double FrameworkVersionNumber;

//! Project version string for Framework.
FOUNDATION_EXPORT const unsigned char FrameworkVersionString[];

//import all the public headers of your framework

#import <Framework/MathTool.h>
  • Edit Scheme
1
左上角scheme->Run->Info->Build Configuration 设置为 Release
  • 编译 framework
1
2
分别选中模拟器和Generic iOS Device 编译。
选中 Products 下的xxx.framework,show in Finder。
  • 合成通用 framework
1
2
3
lipo -create /xx.framework/xx(真机.framework文件路径) 
/xx.framework/xx(模拟器.framework文件路径)
-output Desktop/xx

这一步可能会报错,但依然会生成一个xx.lipo文件,将其后缀去掉并复制到/xx.framework(真机.framework文件路径)下,替换目录中原有的framework。

  • 测试 framework

将上一步中替换完成的xx.framework 复制到新的工程里,导入头文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#import <Framework/Framework.h>

@implementation AppDelegate

- (void)testFramework{
MathTool *mathTool = [MathTool new];
[mathTool mathAdd];
}

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[self testFramework];
return YES;
}
@end

.a & .framework
https://davidlii.cn/2018/01/25/framework.html
作者
Davidli
发布于
2018年1月25日
许可协议