iOS 静态:动态 Pod
一、静态和动态
在项目中使用 pod 实现模块化,对于子模块和第三类库的导入方式存在两种:静态库、动态库。
当在 podfile 中指定 use_frameworks! 时,子模块和第三方类库将被打包成 .framework 动态库,模块之间的代码不能直接引用,需要添加依赖;

反之(默认情况)将打包成 .a 静态库。

动态库和静态库的区别:
- 资源加载方式
- 包的大小
- 编译速度
1.1 资源加载方式
s.dependency 'xx’
静态方式中各模块的 podspec 文件不用设置依赖,就可以直接 #import 其他模块的类头文件。

而动态方式则会报错。

s.resources
s.resources = ['Classes/**/*.{xib,storyboard,Bundle,png,gif,jpg,jpeg,txt}', 'Resource/**/*']
图片等资源是都放入 mainbundle,直接用 imageNamed: 访问,不用增加很多获取 bundle 的代码。
s.resource = 'xx/xxx.bundle'
s.resource_bundles = { 'xxx' => ['/Classes/**/*.{xib,storyboard,Bundle,png,gif,jpg,jpeg,txt}', 'Resource/**/*'] }
这两种写法,资源都在模块自己的 bundle 里面,文件名为 xxx.bundle,工程中需要通过
bundleForClass等获取资源路径。
1.2 包的大小

在图中,上面的是使用 use_frameworks! 的动态包, 下面的是默认(或使用 use_modular_headers!)的静态包,几次验证,都是动态的更小。
这里需要注意,不同的证书 ipa 的大小不同
动态 pod:adhoc 58.9M,pub 130.6M
静态 pod:adhoc 58.3M,pub 131.6M
差别这么大的原因是:工程使用 swift 的代码。

1.3 编译速度
动态库 Pod 方式:
Total pre-main time: 819.46 milliseconds (100.0%)
dylib loading time: 542.43 milliseconds (66.1%)
rebase/binding time: 35.86 milliseconds (4.3%)
ObjC setup time: 57.79 milliseconds (7.0%)
initializer time: 183.27 milliseconds (22.3%)
slowest intializers :
libSystem.B.dylib : 10.33 milliseconds (1.2%)
libMainThreadChecker.dylib : 28.76 milliseconds (3.5%)
AFNetworking : 83.46 milliseconds (10.1%)
Realm : 27.37 milliseconds (3.3%)
CYKJBasicModule : 17.54 milliseconds (2.1%)
静态库 Pod 方式:
Total pre-main time: 591.00 milliseconds (100.0%)
dylib loading time: 447.06 milliseconds (75.6%)
rebase/binding time: 30.51 milliseconds (5.1%)
ObjC setup time: 20.26 milliseconds (3.4%)
initializer time: 93.04 milliseconds (15.7%)
slowest intializers :
libSystem.B.dylib : 7.67 milliseconds (1.2%)
libMainThreadChecker.dylib : 25.41 milliseconds (4.3%)
Realm : 21.13 milliseconds (3.5%)
CYKJMain : 57.87 milliseconds (9.7%)
1.4 工程实例
在项目开发中的场景是一个第三方类库 bongSDK.framework 引入了 Realm.framework 和 RealmSwift.framework,RealmSwift.framework 是通过 swift 语言写的,它的内部调用 Realm。
最初静态方式的 pod 遇到了难以理解的报错,因为知识的欠缺和时间的紧迫,放弃了静态这条路,使用 use_frameworks! 动态 pod 的方式。
动态方式在 pod install 阶段没有报错,但子模块需要添加依赖,更困难的是图片、xib、storyboard 等资源需要获取到模块的 bundle 才能加载,导致工程大面积的图片加载错误,页面跳转崩溃。因此不得不增加很多获取 bundle 路径的代码,修改的位置几百上千处。
+ (NSBundle *)bundleWithClassName:(Class)cls moduleName:(NSString*)module
{
if (module == nil) {
return [NSBundle mainBundle];
}
NSBundle * bundle = [NSBundle bundleForClass:cls];
NSURL * bundleURL = [bundle URLForResource:module withExtension:@"bundle"];
if (bundleURL == nil) {
__block UINavigationController* nav;
[[UIApplication sharedApplication].windows enumerateObjectsUsingBlock:^(__kindof UIWindow * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
UIViewController * windowVC = obj.rootViewController;
if ([windowVC isKindOfClass:[UINavigationController class]]) {
nav = (UINavigationController *)windowVC;
*stop = YES;
}
}];
if (nav != nil) {
Class callerCls = [nav.viewControllers.firstObject class];
bundle = [NSBundle bundleForClass:callerCls];
bundleURL = [bundle URLForResource:module withExtension:@"bundle"];
}
if (bundleURL == nil) {
return [NSBundle mainBundle];
}
}
return [NSBundle bundleWithURL:bundleURL];
}
图片加载则更加困难,因为很多图片是在 xib 中写的,通过断点发现,系统并没有调用 imageNamed: 方法,导致使用 runtime 替换方法实现图片位置修改的方式失败,通过查找资料,发现 xib 中的 UIButton、UIImageView 会调用 - initWithCoder: 方法,底层会调用 UINibDecoder 类的 decodeObjectForKey。
runtime 替换 decodeObjectForKey 方法后,打印输出发现,UIButton、UIImageView 控件加载的图片名称在 UIResourceName 字段。由此就有了如下的处理方式:
+ (void)load
{
_imageViewImageArray = [NSMutableArray arrayWithCapacity:2];
propKey = [CYKJXUtil stringByReversed:@"emaNecruoseRIU"];
btnKey = [CYKJXUtil stringByReversed:@"tnetnoClufetatSnottuBIU"];
// hook UINibDecoder - decodeObjectForKey:
NSString* clsName = [NSString stringWithFormat:@"redoce%@biNIU", @"D"];
clsName = [CYKJXUtil stringByReversed:clsName];
[HookTool exchangeInstanceMethod:NSClassFromString(clsName)
originalSEL:@selector(decodeObjectForKey:)
swizzledSEL:@selector(swizzle_decodeObjectForKey:)];
// hook UIImageView - initWithCoder:
[HookTool exchangeInstanceMethod:UIImageView.class
originalSEL:@selector(initWithCoder:)
swizzledSEL:@selector(swizzle_imageView_initWithCoder:)];
// hook UIButton - initWithCoder:
[HookTool exchangeInstanceMethod:UIButton.class
originalSEL:@selector(initWithCoder:)
swizzledSEL:@selector(swizzle_button_initWithCoder:)];
}
- (id)swizzle_decodeObjectForKey:(NSString *)key
{
Method originalMethod = class_getInstanceMethod([HookTool class], @selector(swizzle_decodeObjectForKey:));
IMP function = method_getImplementation(originalMethod);
id (*functionPoint)(id, SEL, id) = (id (*)(id, SEL, id)) function;
id value = functionPoint(self, _cmd, key);
// 保存图片名称
if ([key isEqualToString:propKey]) {
[_imageViewImageArray addObject:value];
}
// 保存 button 状态数据
if ([key isEqualToString:btnKey]) {
if ([value isKindOfClass:[NSDictionary class]]) {
_buttonImageDictionary = value;
}
}
return value;
}
#pragma mark - UIImageView
- (id)swizzle_imageView_initWithCoder:(NSCoder *)aDecoder
{
// 执行顺序:initWithCoder -》DecoderWithKey -》setImage:,所以每次给 imageView 设置图片时,需要将之前的置空。
// tabbarItem 的图片设置不会执行 initWithCoder,如果不置空,会导致 imageView 设置成和 tabbarItem 一样的图片。
[_imageViewImageArray removeAllObjects];
UIImageView * instance = (UIImageView *)[self swizzle_imageView_initWithCoder:aDecoder];
// 设置 image
if (_imageViewImageArray.count > 0) {
UIImage * normalImage = [HookTool imageAfterSearch:_imageViewImageArray[0]];
if (normalImage) {
instance.image = normalImage;
}
}
// 设置 highlightedImage
if (_imageViewImageArray.count > 1) {
UIImage * highlightedImage = [HookTool imageAfterSearch:_imageViewImageArray[1]];
if (highlightedImage) {
instance.highlightedImage = highlightedImage;
}
}
return instance;
}
#pragma mark - UIButton
- (id)swizzle_button_initWithCoder:(NSCoder *)aDecoder
{
// 执行顺序:initWithCoder -》DecoderWithKey -》setImage:,所以每次给 button 设置图片时,需要将之前的置空。
// tabbarItem 的图片设置不会执行 initWithCoder,如果不置空,会导致 button 设置成和 tabbarItem 一样的图片。
[_imageViewImageArray removeAllObjects];
_buttonImageDictionary = nil;
UIButton * instance = (UIButton *)[self swizzle_button_initWithCoder:aDecoder];
@autoreleasepool {
[_buttonImageDictionary enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key,
id _Nonnull obj,
BOOL * _Nonnull stop) {
if (_imageViewImageArray.count == 0) {
*stop = YES;
}
else {
switch ([key integerValue]) {
case ButtonImageOrder_Normal:
[HookTool setImageForButton:instance object:obj state:UIControlStateNormal];
break;
case ButtonImageOrder_Highlighted:
[HookTool setImageForButton:instance object:obj state:UIControlStateHighlighted];
break;
case ButtonImageOrder_Selected:
[HookTool setImageForButton:instance object:obj state:UIControlStateSelected];
break;
case ButtonImageOrder_Disabled:
[HookTool setImageForButton:instance object:obj state:UIControlStateDisabled];
break;
}
}
}];
}
return instance;
}
如上可见,这种动态方式对于编码并不友好,资源必须要特定的 bundle,一旦资源路径出错,轻则图片未加载,重则程序崩溃。
所以需要研究下如果使用静态方式 pod 子模块代码。
首先将 use_frameworks! 删除,重新执行 pod install,等 Pod installation complete! 之后,运行工程,报错,一个一个的解决。
dyld: Library not loaded: @rpath/Realm.framework/Realm
现在不用 pod 导入 realm,而是将 realm.framework 拖入 basicModule 工程。这里找了官方最新的 realm.framework,它分为静态版和动态版,添加到工程的
Embedded Binaries,编译时报错Unknown type name namespace。不管通过修改 .h 为 .hpp,还是修改 build settings -> Compile Sources As -> Objectoive-C++ 都没有效果,无计可施之时想到了,可以将 use_frameworks! 时 cocoapods 生成 的 Realm.framework 拷贝一份,拖入工程死马等活马医。
编译运行,这个问题解决了~
Library not loaded: @rpath/libswiftCore.dylib
Build Settings 中
Aways Embed Swift Standard Libraries修改成 YES。Argument list too long: recursive header expansion failed
Search Paths -> Header Search Paths,去掉
$(PODS_ROOT)/**,去掉不必要的recursivesearch。
其余的就是解决一些资源加载问题,资源重名问题,动态库的引用问题 #import “” 改为 #import <>。

二、文章
iOS 静态:动态 Pod的更多相关文章
- iOS 静态库,动态库与 Framework 浅析
静态库与动态库的区别 首先来看什么是库,库(Library)说白了就是一段编译好的二进制代码,加上头文件就可以供别人使用. 什么时候我们会用到库呢?一种情况是某些代码需要给别人使用,但是我们不希望别人 ...
- iOS 静态库,动态库与 Framework
iOS 静态库,动态库与 Framework 静态库与动态库的区别 首先来看什么是库,库(Library)说白了就是一段编译好的二进制代码,加上头文件就可以供别人使用. 什么时候我们会用到库呢 ...
- iOS 静态库和动态库的区别&静态库的生成
linux中静态库和动态库的区别 一.不同 库从本质上来说是一种可执行代码的二进制格式,可以被载入内存中执行.库分静态库和动态库两种. 1. 静态函数库 这类库的名字一般是libxxx.a:利用静态函 ...
- (转)iOS静态库与动态库的区别
一.什么是库? 库是共享程序代码的方式,一般分为静态库和动态库. 静态库:链接时完整地拷贝至可执行文件中,被多次使用就有多份冗余拷贝. 动态库:链接时不复制,程序运行时由系统动态加载到内存,供程序调用 ...
- iOS静态库转Framework动态库
参考文章: iOS静态库(.a 和framework) XCode6制作动态及静态Framework 说说iOS中静态库的开发 dyld: Library not loaded: @rpath/ ...
- iOS - 静态库的创建与使用
在日常项目开发中,不论是为了两个公司项目上的业务交流还是为了减少项目的编译时间,有的时候我们会把项目中的私密内容打包成静态库,或者是把项目中变动较少一部分打包成静态库以便提高编译效率,那么下面我们就来 ...
- IOS静态库
如何在Xcode中创建C++静态库 http://jingyan.baidu.com/article/03b2f78c111fca5ea237ae26.html iOS 如何创建和使用静态库 http ...
- [IOS 静态库]
http://www.2cto.com/kf/201402/276718.html 一.什么是库? 库是共享程序代码的方式,一般分为静态库和动态库. 二.静态库与动态库的区别? 静态库:链接时完整地拷 ...
- WWDC2014之iOS使用动态库 framework【转】
from:http://www.cocoachina.com/industry/20140613/8810.html JUN 12TH, 2014 苹果的开放态度 WWDC2014上发布的Xcode6 ...
随机推荐
- Mysql或者SQL Server数据库的运行机制和体系架构
一.MySQL主要分为以下几个组件: 连接池组件 管理服务和工具组件 SQL接口组件 分析器组件 优化器组件 缓冲组件 插件式存储引擎 物理文件 二.MySql的组成:Mysql是由SQL接口,解析器 ...
- JS动画之缓动函数分析及动画库
上一篇讲了JS动画定时器相关知识,这一篇介绍下缓动函数及流行的动画库. 熟悉的图 实际使用 jquery animate()+jquery.easing插件的使用: $(selector).anima ...
- EventEmitter:从命令式 JavaScript class 到声明函数式的华丽转身
新书终于截稿,今天稍有空闲,为大家奉献一篇关于 JavaScript 语言风格的文章,主角是函数声明式. 灵活的 JavaScript 及其 multiparadigm 相信"函数式&quo ...
- spring boot 调度任务
1.引入spring boot所依赖的jar包 <parent> <groupId>org.springframework.boot</groupId> < ...
- 最简单易懂的实现CRC16校验
public String getCRC16(byte[] bytes) { //CRC寄存器全为1 int CRC = 0x0000ffff; //多项式校验值 int POLYNOMIAL = 0 ...
- 【WPF学习】第五十八章 理解逻辑树和可视化树
在前面章节中,花费大量时间分析了窗口的内容模型——换句话说,研究了如何在其他元素中嵌套元素,进而构建完整的窗口. 例如,考虑下图中显示的一个非常简单的窗口,该窗口包含两个按钮.为创建该按钮,在窗口中嵌 ...
- 18 JpaRepository和JpaSpecificationExecutor
继承JpaRepository后的方法列表 JpaRepository findAll() List<T> findAll(Sort) List<T> findAll(Iter ...
- vue-cli实现最简单的全选、全不选、反选功能。不容错过呦!!!!!话不多说直接上代码。。。。
<template> <div class="hello"> <input type="button" name="al ...
- Android 版的多合一Office应用也正式向iOS开放了
导读 在 Android 版的多合一 Office 应用「偷跑」不久后(官方证实上线时间比计划要早),为 iOS 准备的版本现在终于也结束 beta 测试正式上线了. 和只提供「有限」平板支持的 An ...
- C++ for循环练习
#include <stdio.h> //题目:现有公鸡5元一只,母鸡3元一只,小鸡1元3只(小鸡不能单买 最少3只起卖),此时顾客有100元,请问有多少种购买方案. int main() ...