最近听了蛮多 runtime 平时完全用不到的这种言论,所以慢慢的写一点 runtime 在项目中的应用场景。其中之一就是,当更新的资源包的文件无法正确读取的时候,替换成一张占位图。
能看的实现方法有如下几种:
- 写一个类别方法,在类别中进行逻辑处理。
- 用原方法,但是进行动态方法交换。
第一个缺点其实很明显,当团队成员不断增加之后,没有办法确保每一个人都能按照你的规范来使用工具类别中的指定方法,除非特别想每天抽不止一点时间 Code Review。
那么第二个方案带来的则是一劳永逸,但是在使用的时候一定要评估影响。比如:组件化的时候经常会用到 bundle 来储存图片,并且使用 cocoapods 来进行组件管理,那么获取图片的逻辑就要考虑到你取 -imageNamed:
方法获得的,可能是原图片,也可能是占位图。那么就需要做相关的逻辑处理。
在例子中,我们只是简单展示了一下方法交换的使用方式。
// 这是例子,用的是类别实现。类别的话,我们丢项目里就可以使用了,并不需要再进行引入头文件。
#import "UIImage+PlaceHolderImage.h"
#import <objc/runtime.h>
@implementation UIImage (PlaceHolderImage)
+ (void)load
{
// 获取 UIImage 方法 -imageNamed: 的 Method
Method imageNameMethod = class_getClassMethod(self, @selector(imageNamed:));
// 获取 UIImage+PlaceHolderImage 方法 -replaced_imageNamed: 的 Method
Method replaced_imageNamedMethod = class_getClassMethod(self, @selector(replaced_imageNamed:));
// 将两个方法进行交换,现在如果调用 -imageNamed: 则调用的是下方 +replaced_imageNamed: 的实现
method_exchangeImplementations(imageNameMethod, replaced_imageNamedMethod);
}
+ (UIImage *)replaced_imageNamed:(NSString *)imageName
{
// 这里是递归调用吗?不是。因为现在调用 +replaced_imageNamed: 实现则是苹果框架内的 -imageNamed: 的实现。
UIImage *image = [UIImage replaced_imageNamed:imageName];
if (!image)
{
image = [UIImage replaced_imageNamed:@"placeholder_image"];
}
return image;
}
@end
一个简单的封装,如下:
#import <objc/runtime.h>
- (void)pd_exchangeSelector:(SEL)originSel toSelector:(SEL)replazSel
{
[[self class] _pd_exchangeSelector:originSel toSelector:replazSel];
}
+ (void)pd_exchangeSelector:(SEL)originSel toSelector:(SEL)replazSel
{
[self _pd_exchangeSelector:originSel toSelector:replazSel];
}
+ (void)_pd_exchangeSelector:(SEL)originSel toSelector:(SEL)replazSel
{
Method originMethod = class_getClassMethod(self, originSel);
Method replazMethod = class_getClassMethod(self, replazSel);
method_exchangeImplementations(originMethod, replazMethod);
}
实际项目中,建议使用成熟的 Aspects 库进行 AOP 编程,功能强大,稳定高效。
本想解析一下 Aspects 库的,一看网上很多很多,就偷个懒粘一个链接了。