iOS组件依赖避免冲突的小技巧分享

问题缘由

本文以 YBImageBrowser[1] 组件举例。

YBImageBrowser 依赖了 SDWebImage,在使用 CocoaPods 集成到项目中时,可能会出现一些依赖冲突的问题,最近社区提了多个 Issues 并且在 Insights -> Traffic -> Popular content 中看到了此类问题很高的关注度,所以不得不着手解决。

严格的版本限制

一个开源组件的迭代过程中,保证上层接口的向下兼容就不错了。为了优化性能并且控制内存,YBImageBrowser 没有直接用其最上层的接口,而是单独使用了下载模块和缓存模块,SDWebImage 的迭代升级很容易导致笔者的组件兼容不了,所以之前一直是类似这样依赖的:

s.dependency 'SDWebImage', '~> 5.0.0'

这样做的好处是限制足够小版本范围,降低 SDWebImage 接口变动导致组件代码错误的风险。但如果 SDWebImage 升级到 5.1.0,不管相关 API 是否变动,CocoaPods 都视为依赖冲突。

其它组件依赖了不同版本的 SDWebImage

当两个组件依赖了同一个组件的不同版本,并且依赖的版本没有交集,比如:

A.dependency 'SDWebImage', '~> 4.0.0'
B.dependency 'SDWebImage', '~> 5.0.0'

那么 A 和 B 同时集成进项目会出现依赖冲突。

解决方案

使用 CocoaPods 集成项目非常便捷,对于组件使用者来说,总是想在任何场景下都能轻易集成,并且能在将来享受组件的更新优化,显然前面提到的问题可能会影响集成的便捷性。

更模糊的版本限制

很多时候一个大版本的组件不会改动 API,并且对于社区流行的组件我们可以寄一定希望于其做好向下兼容,所以放宽依赖的版本限制能覆盖将来更多的版本(规则参考:podspec dependency[2]):

s.dependency 'SDWebImage', '>= 5.0.0'

为什么不干脆去掉版本限制呢?

因为 YBImageBrowser 3.x 是基于 SDWebImage 5.0.0 开发的,笔者可以明确不兼容 5.0.0 之前的版本,所以在 SDWebImage 将来迭代版本出现相关 API 不兼容之前,这个限制都是“完美”覆盖所有版本的。

避免依赖冲突的暴力方案

当有其它组件依赖了不同版本的 SDWebImage,粗暴的解决方案如下:

• 直接修改其它组件依赖的 SDWebImage 版本。

• 将 YBImageBrowser 手动导入项目,并且修改代码去适应当前的 SDWebImage 版本。

• 社区朋友一个 Issue 中提到的方法:在 ~/.cocoapods/repos 目录下找到 YBImageBrowser 文件夹,更改对应版本的 podspec.json 文件里对 SDWebImage 的依赖版本。

显然,上面的几种方案不太优雅,手动导入项目难以享受组件的更新优化,修改本地 repo 信息会因为 repo 列表的更新而复位。

避免依赖冲突的优雅方案

出现依赖冲突是必须要解决的问题,其它组件依赖的版本限制可以视为不变量,解决方案可以从组件的制作方面考虑。

要做到的目标是,既满足部分用户快速集成组件,又能让部分用户解决依赖冲突的前提下保证能享受组件将来的更新优化。

答案就是subspec,以下是 YBImageBrowser.podspec 部分代码(完整代码[3]):

s.subspec "Core" do |core|
core.source_files = "YBImageBrowser/**/*.{h,m}"
core.dependency 'SDWebImage', '>= 5.0.0'
end
s.subspec "NOSD" do |core|
core.source_files = "YBImageBrowser/**/*.{h,m}"
core.exclude_files = "YBImageBrowser/WebImageMediator/YBIBDefaultWebImageMediator.{h,m}"
end

由此,用户可以自由的选择是否需要依赖 SDWebImage,在 Podfile 里的观感大致是这样:

// 依赖 SDWebImage
pod 'YBImageBrowser'
// 不依赖 SDWebImage
pod 'YBImageBrowser/NOSD'

那么在 YBImageBrowser 代码中应该如何区分是否依赖了 SDWebImage 并且提供默认实现呢?

第一步是设计一个抽象接口(这个接口不依赖 SDWebImage):

@protocol YBIBWebImageMediator <NSObject>
// Download methode, caching methode, and so on.
@end

第二步是在YBImageBrowser.h中定义一个遵循该接口的属性:

/// 图片下载缓存相关的中介者(赋值可自定义)
@property (nonatomic, strong) id<YBIBWebImageMediator> webImageMediator;

第三步是实现一个默认的中介者(这个类依赖了 SDWebImage):

@interface YBIBDefaultWebImageMediator : NSObject <YBIBWebImageMediator>
@end
@implementation YBIBDefaultWebImageMediator
//通过 SDWebImage 的 API 实现 <YBIBWebImageMediator> 协议方法
@end

第四步是在内部代码中通过条件编译导入并初始化默认中介者:

#if __has_include("YBIBDefaultWebImageMediator.h")
#import "YBIBDefaultWebImageMediator.h"
#endif
...
#if __has_include("YBIBDefaultWebImageMediator.h")
_webImageMediator = [YBIBDefaultWebImageMediator new];
#endif

第五步在 YBImageBrowser.podspec 中也可以看到,在不依赖 SDWebImage 的集成方式时排除了两个文件:YBIBDefaultWebImageMediator.{h.m}。

由此便实现了目标:

• 用依赖 SDWebImage 的集成方式快速集成。

• 使用不依赖 SDWebImage 的集成方式避免各种情况下的依赖冲突,但注意这种情况需要自行实现一个遵循<YBIBWebImageMediator>协议的中介者。

以上便是避免依赖冲突的小技巧,希望读者朋友能提出更好的建议或意见😁。

参考

[1]https://github.com/indulgeIn/YBImageBrowser

[2]https://guides.cocoapods.org/syntax/podspec.html#dependency

[3]https://github.com/indulgeIn/YBImageBrowser/blob/master/YBImageBrowser.podspec

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对我们的支持。

(0)

相关推荐

  • iOS组件化开发实战记录

    1. 组件化需求来源 起初的这个项目,App只有一条产品线,代码逻辑相对比较清晰,后期随着公司业务的迅速发展,现在App里面承载了大概五六条产品线,每个产品线的流程有部分是一样的,也有部分是不一样的,这就需要做各种各样的判断及定制化需求.大概做了一年多后,出现了不同产品线提过来的需求,开发人员都需要在主工程中开发,但是开发人员开发的是不同的产品线,也得将整个工程跑起来,代码管理.并行开发效率.分支管理.上线时间明显有所限制.大概就在去年底,我们的领导提出了这个问题,希望作成组件化,将代码重构拆分

  • iOS组件依赖避免冲突的小技巧分享

    问题缘由 本文以 YBImageBrowser[1] 组件举例. YBImageBrowser 依赖了 SDWebImage,在使用 CocoaPods 集成到项目中时,可能会出现一些依赖冲突的问题,最近社区提了多个 Issues 并且在 Insights -> Traffic -> Popular content 中看到了此类问题很高的关注度,所以不得不着手解决. 严格的版本限制 一个开源组件的迭代过程中,保证上层接口的向下兼容就不错了.为了优化性能并且控制内存,YBImageBrowser

  • iOS中修改UISearchBar圆角的小技巧分享

    前言 在我们日常开发中,经常会遇到一些需求非要把 UISearchBar 默认的圆角矩形的圆角改大,顶端改成圆形的.虽然系统没有提供这个 API,不过还是有一个简单方法可以解决. 解决方法: 首先在 UIView 的 category 里加一个方法: UIView+Utils.m - (UIView*)subViewOfClassName:(NSString*)className { for (UIView* subView in self.subviews) { if ([NSStringFr

  • JavaScript编码小技巧分享

    三元操作符 如果使用if...else语句,那么这是一个很好节省代码的方式. const x = 20; let big; if (x > 10) { big = true; } else { big = false; } //这样写... const big = x > 10 ? true : false; Short-circuit Evaluation 分配一个变量值到另一个变量的时候,你可能想要确保变量不是null.undefined或空.你可以写一个有多个if的条件语句或者Short

  • 注释PHP和html混合代码的小技巧(分享)

    我们在写php的时候有时候会遇到想要注释一部分的代码,是由html和PHP混合组成的,这时候如果一行一行去分开注释有点麻烦.但是又不敢删掉,这时候我们可以充分利用if语句去进行注释的功能 <?php if (1==2):?> //代码片段 <?php endif;?> 以上就是小编为大家带来的注释PHP和html混合代码的小技巧(分享)全部内容了,希望大家多多支持我们~

  • Python中最大最小赋值小技巧(分享)

    码代码时,有时候需要根据比较大小分别赋值: import random seq = [random.randint(0, 1000) for _ in range(100)] #方法1: xmax, xmin = max(seq), min(seq) #方法2: xmax, *_, xmin = sorted(seq) 从上面这个来看,看不出来方法2的优势来,不过我们常用的是比较两个数的大小,并选取: dx, dy = random.sample(seq, 2) #方法1: dx, dy = m

  • Android获取、更改包名的小技巧分享(超实用)

    前言 小菜因为工作需要,经常需要一套代码修改很多次包名,虽然不是什么技术活,但是小菜的用的次数多了就有了一点点小技巧分享给大家,下面话不多说了,来一起看看详细的介绍吧. 方法如下: 1. 如果源码是在本机电脑中,首先找到源码多位置,例如:由包名 com.aaa.bbb 修改为包名 com.ccc.ddd,可以直接重命名,本地修改: 2. AndroidStudio 打开本项目: 3. 删除 .gradle: 4. 把 build.gradle 中 appicationId 替换为新的包名: 5.

  • pandas 使用均值填充缺失值列的小技巧分享

    pd.DataFrame中通常含有许多特征,有时候需要对每个含有缺失值的列,都用均值进行填充,代码实现可以这样: for column in list(df.columns[df.isnull().sum() > 0]): mean_val = df[column].mean() df[column].fillna(mean_val, inplace=True) # -------代码分解------- # 判断哪些列有缺失值,得到series对象 df.isnull().sum() > 0

  • 关于Python形参打包与解包小技巧分享

    Python中的函数调用与c++不同的是将this指针直接作为self当作第一个形参进行处理,从而将静态函数与实例方法的调用形式统一了起来.在实际编程过程中,可以通过传递函数的地址.函数的形参的方式将所有函数(包括静态函数.类实例函数)的调用用统一的方式表达出来,方便统一接口和抽象. 待传递的2个函数如下: class Operation: @staticmethod def close_buy(): """ :return: """ print

  • 使用python将大量数据导出到Excel中的小技巧分享

    (1) 问题描述:为了更好地展示数据,Excel格式的数据文件往往比文本文件更具有优势,但是具体到python中,该如何导出数据到Excel呢?如果碰到需要导出大量数据又该如何操作呢? 本文主要解决以上两个问题. (2)具体步骤如下: 1.第一步,安装openpyxl, 使用pip install openpyxl即可,但是在windows下安装的是2.2.6版本,但是centos自动安装的是4.1版本,(多谢海哥的提醒). 写的代码在windows下运行没问题,但centos上却报错了,说是e

  • Python中使用filter过滤列表的一个小技巧分享

    有的时候使用dir(Module),可以查看里面的方法,但是模块自带的属性"__"开头的也会显示,如下: >>> import random >>> dir(random) ['BPF', 'LOG4', 'NV_MAGICCONST', 'RECIP_BPF', 'Random', 'SG_MAGICCONST', 'SystemRandom', 'TWOPI', 'WichmannHill', '_Buil tinMethodType', '_M

随机推荐