美文网首页
Runtime(五)拦截系统方法method swizzling

Runtime(五)拦截系统方法method swizzling

作者: 炒河粉儿 | 来源:发表于2019-08-16 10:54 被阅读0次

方法替换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。

相关文章

网友评论

      本文标题:Runtime(五)拦截系统方法method swizzling

      本文链接:https://www.haomeiwen.com/subject/ellfsctx.html