方法替换method swizzling
method swizzling是runtime中的黑魔法,可以动态的将方法进行替换。
简单实现
通过method_exchangeImplementations的方法,将获取到的两个方法进行替换。
简单实现了UIView设置背景颜色的方法替换,当背景颜色设置为黑色的时候,处理成设置为红色。
方法替换,要在自己写的方法中再次调用自己的方法。方法已经完成了替换,此时调用自己写的方法则其实是调用的系统本身的方法。
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIView (Swizzing)
@end
NS_ASSUME_NONNULL_END
#import "UIView+Swizzing.h"
#import <objc/runtime.h>
@implementation UIView (Swizzing)
//load加载的时候会调用
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method *m1 = class_getInstanceMethod([self class], @selector(setBackgroundColor:));
Method *m2 = class_getInstanceMethod([self class], @selector(lwn_setBackgroundColor:));
method_exchangeImplementations(m1, m2);
});
}
- (void)lwn_setBackgroundColor:(UIColor *)color
{
if (color == [UIColor blackColor]) {
[self lwn_setBackgroundColor:[UIColor redColor]];
}
}
小扩展
通过方法替换,在调用系统的方法的时候去实现更多的操作。
例子:又一个tableview,当数据为0条的时候,加载一个没有数据的提示图进行界面的填充,此时可以通过方法替换,重写reloadData的方法,当没有数据的时候,动态创建一个提示图view,并且加载到界面上。
使用到了method_exchangeImplementations,以及分类中动态添加属性,并实现set,get方法。
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UITableView (Swizzing)
@property (nonatomic, strong) UIView *fillView;
@end
NS_ASSUME_NONNULL_END
#import "UITableView+Swizzing.h"
#import <objc/runtime.h>
//静态变量关键字
static const char LwnFillView;
@implementation UITableView (Swizzing)
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method *m1 = class_getInstanceMethod(self, @selector(reloadData));
Method *m2 = class_getInstanceMethod(self, @selector(lwn_reloadData));
method_exchangeImplementations(m1, m2);
});
}
- (void)lwn_reloadData
{
[self lwn_reloadData];
[self fillDefalutView];
}
- (void)fillDefalutView
{
id<UITableViewDataSource> dataSource = self.dataSource;
NSInteger section = ([dataSource respondsToSelector:@selector(numberOfSectionsInTableView:)] ?[dataSource numberOfSectionsInTableView:self] : 1);
int rows = 0;
for (int i = 0; i<section; i++) {
rows = [dataSource tableView:self numberOfRowsInSection:section];
}
if (!rows) {
self.fillView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
self.fillView.backgroundColor = [UIColor blueColor];
[self addSubview:self.fillView];
}else {
self.fillView.hidden = YES;
}
}
//给分类添加属性
- (void)setFillView:(UIView *)fillView
{
objc_setAssociatedObject(self, &LwnFillView, fillView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIView *)fillView
{
return objc_getAssociatedObject(self, &LwnFillView);
}
注意
方法替换要在+ (void)load中进行复写,并实现方法的替换。
这个点主要是+ (void)load和+ (void)initialize的区别。
对于这两个方法:
两个方法对比:
1.+load 不支持继承,类没有实现就不会调用;而 +initialize 支持,类没有实现就调用父类的。
2.+load 在类载入的时候由 runtime 自动调用;而 +initialize 是惰性调用,使用类的时候才调用。
3.+load 只调用一次,而 +initialze 方法,虽然对每个类来说也只会自动调用一次(无论生成多少实例对象,该方法只调用一次),但情况比较复杂:1.使用子类的时候如果没有实现该方法,会调用父类的;2. 子类里显式调用父类的方法。所以强烈建议在 +load 里使用 method swizzling。
网友评论