一、静态和动态

在项目中使用 pod 实现模块化,对于子模块和第三类库的导入方式存在两种:静态库、动态库。

当在 podfile 中指定 use_frameworks! 时,子模块和第三方类库将被打包成 .framework 动态库,模块之间的代码不能直接引用,需要添加依赖;

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

动态库和静态库的区别:

  • 资源加载方式
  • 包的大小
  • 编译速度

1.1 资源加载方式

  1. s.dependency 'xx’

    静态方式中各模块的 podspec 文件不用设置依赖,就可以直接 #import 其他模块的类头文件。

    而动态方式则会报错。

  2. 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! 之后,运行工程,报错,一个一个的解决。

  1. 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 拷贝一份,拖入工程死马等活马医。

    编译运行,这个问题解决了~

  2. Library not loaded: @rpath/libswiftCore.dylib

    Build Settings 中 Aways Embed Swift Standard Libraries 修改成 YES。

  3. Argument list too long: recursive header expansion failed

    Search Paths -> Header Search Paths,去掉 $(PODS_ROOT)/**,去掉不必要的 recursive search。

其余的就是解决一些资源加载问题,资源重名问题,动态库的引用问题 #import “” 改为 #import <>。

二、文章

关于Argument list too long的问题

iOS 静态:动态 Pod的更多相关文章

  1. iOS 静态库,动态库与 Framework 浅析

    静态库与动态库的区别 首先来看什么是库,库(Library)说白了就是一段编译好的二进制代码,加上头文件就可以供别人使用. 什么时候我们会用到库呢?一种情况是某些代码需要给别人使用,但是我们不希望别人 ...

  2. iOS 静态库,动态库与 Framework

    iOS 静态库,动态库与 Framework     静态库与动态库的区别 首先来看什么是库,库(Library)说白了就是一段编译好的二进制代码,加上头文件就可以供别人使用. 什么时候我们会用到库呢 ...

  3. iOS 静态库和动态库的区别&静态库的生成

    linux中静态库和动态库的区别 一.不同 库从本质上来说是一种可执行代码的二进制格式,可以被载入内存中执行.库分静态库和动态库两种. 1. 静态函数库 这类库的名字一般是libxxx.a:利用静态函 ...

  4. (转)iOS静态库与动态库的区别

    一.什么是库? 库是共享程序代码的方式,一般分为静态库和动态库. 静态库:链接时完整地拷贝至可执行文件中,被多次使用就有多份冗余拷贝. 动态库:链接时不复制,程序运行时由系统动态加载到内存,供程序调用 ...

  5. iOS静态库转Framework动态库

    参考文章: iOS静态库(.a 和framework)  XCode6制作动态及静态Framework  说说iOS中静态库的开发  dyld: Library not loaded: @rpath/ ...

  6. iOS - 静态库的创建与使用

    在日常项目开发中,不论是为了两个公司项目上的业务交流还是为了减少项目的编译时间,有的时候我们会把项目中的私密内容打包成静态库,或者是把项目中变动较少一部分打包成静态库以便提高编译效率,那么下面我们就来 ...

  7. IOS静态库

    如何在Xcode中创建C++静态库 http://jingyan.baidu.com/article/03b2f78c111fca5ea237ae26.html iOS 如何创建和使用静态库 http ...

  8. [IOS 静态库]

    http://www.2cto.com/kf/201402/276718.html 一.什么是库? 库是共享程序代码的方式,一般分为静态库和动态库. 二.静态库与动态库的区别? 静态库:链接时完整地拷 ...

  9. WWDC2014之iOS使用动态库 framework【转】

    from:http://www.cocoachina.com/industry/20140613/8810.html JUN 12TH, 2014 苹果的开放态度 WWDC2014上发布的Xcode6 ...

随机推荐

  1. sql -- 利用order by 排名作弊

    表结构: 需求: 方法1:union ,,) order by user_total desc ) a union (,,) order by user_total desc ) b) 方法2:直接在 ...

  2. 彻底理解使用JavaScript 将Json数据导出CSV文件

    前言 将数据报表导出,是web数据报告展示常用的附带功能.通常这种功能都是用后端开发人员编写的.今天我们主要讲的是直接通过前端js将数据导出Excel的CSV格式的文件. 原理 首先在本地用Excel ...

  3. 为什么要使用webpack?

    在网页中会引用到哪些常见的静态资源? js (.js  .jsx  .coffee  .ts) css (.css  .less  .sass  .scss scss是sass的plus版) imag ...

  4. django 从零开始 12 快速集合queryset对象

    使用序列化将查询到的quweyset对象进行一个格式转换          还没看文档理解 待写 from django.core.serializers import serializers 导入该 ...

  5. Vue2.0 【第四季】第1节 实例入门-实例属性

    目录 Vue2.0 [第四季]第1节 实例入门-实例属性 第1节 实例入门-实例属性 一.Vue和Jquery.js一起使用 二.实例调用自定义方法 Vue2.0 [第四季]第1节 实例入门-实例属性 ...

  6. layui的form表单的checkbox全选与全不选问题

    <!DOCTYPE html> <html> <head>     <meta charset="utf-8">     <m ...

  7. [项目分享]JSP+Servlet+JDBC实现的学生信息管理系统

    本文存在视频版本,请知悉 项目简介 项目来源于:https://gitee.com/liu_xu111/JavaWeb01 这次分享一个学生管理系统,我感觉这是程序员在大学时期的毕设和课程设计选择最多 ...

  8. 异步编程RxJava-介绍

    前言前段时间写了一篇对协程的一些理解,里面提到了不管是协程还是callback,本质上其实提供的是一种异步无阻塞的编程模式:并且介绍了java中对异步无阻赛这种编程模式的支持,主要提到了Future和 ...

  9. 035.集群安全-Pod安全

    一 Pod安全 1.1 PodSecurityPolicy启用 为了更精细地控制Pod对资源的使用方式,Kubernetes从1.4版本开始引入了PodSecurityPolicy资源对象对Pod的安 ...

  10. Lambda表达式学习笔记

    Lambda基础语法 Java8中引入了一个新的操作符" -> ",该操作符被称为箭头操作符或Lambda操作符,箭头操作符将Lambda表达式拆分成两部分: 左侧:Lamb ...