Runtime进阶一:Method Swizzling

Runtime进阶一:Method Swizzling

本文详解通过runtime来替换两个方法的实现,对runtime不熟悉的小白可以先跳转以下链接了解一下

百度百科:Runtime

我看到的一篇介绍基础知识还算简洁明了的文章,点此跳转

在OC中Runtime的实际应用场景比较常用的大概有以下这么几种:

一:Method Swizzling (替换两个方法的实现)
二:实现给分类添加属性
三:实现字典和模型的自动转换
四:当属性过多时,通过runtime便捷归解档
五:JSPatch替换已有的OC方法实现

本文详解第一条具体方式,后面几点会在接下来有空的时候慢慢补充,想看的或者互相交流的可以关注我的博客。

Method Swizzling

常规写法
    + (void)load {
    //由于 swizzling 改变了全局的状态,所以需要确保在运行时,我们采用的预防措施是可用的。原子操作就是这样一个用于确保代码只会被执行一次的预防措施,就算是在不同的线程中也能确保代码只执行一次。Grand Central Dispatch 的 dispatch_once 满足了这些需求,所以,Method Swizzling 应该在 dispatch_once 中完成
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        @autoreleasepool {//添加到自动释放池,优化内存
            //这里就是随便举了个例子替换button点击后发送消息的方法
            Method systemMethod = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:));
            SEL sysSEL = @selector(sendAction:to:forEvent:);

            Method customMethod = class_getInstanceMethod(self, @selector(custom_sendAction:to:forEvent:));
            SEL customSEL = @selector(custom_sendAction:to:forEvent:);

            //添加方法 语法:BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types) 若添加成功则返回No
            // cls:被添加方法的类  name:被添加方法方法名  imp:被添加方法的实现函数  types:被添加方法的实现函数的返回值类型和参数类型的字符串
            BOOL didAddMethod = class_addMethod(self, sysSEL, method_getImplementation(customMethod), method_getTypeEncoding(customMethod));

            //如果系统中该方法已经存在了,则替换系统的方法  语法:IMP class_replaceMethod(Class cls, SEL name, IMP imp,const char *types)
            if (didAddMethod) {
                class_replaceMethod(self, customSEL, method_getImplementation(systemMethod), method_getTypeEncoding(systemMethod));
            }else{
                method_exchangeImplementations(systemMethod, customMethod);

            }
        }
    });
}
- (void)custom_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event{
}
但是这么写的话,同一个类里有多个方法需要替换就很不方便。封装一下之后的写法如下。懒得看的可以直接下载demo。

demo链接

首先是封装一个公共的替换两个方法的类
#import "NSObject+ZYTSwizz.h"

@implementation NSObject (ZYTSwizz)
/**
 sysSelector:原方法
 mySelector:将要替换的方法
 */
- (void)swizzMethodWithSysSelector:(SEL)sysSelector swizzMySelector:(SEL)mySelector {
    //调用此方法的类
    Class class = [self class];

    Method systemMethod = class_getInstanceMethod(class, sysSelector);
    Method myMethod = class_getInstanceMethod(class, mySelector);
    //didAddMethod 为NO,表示添加成功
    BOOL didAddMethod = class_addMethod(class, sysSelector, method_getImplementation(myMethod), method_getTypeEncoding(myMethod));
    if (didAddMethod) {
        class_replaceMethod(class, mySelector, method_getImplementation(systemMethod), method_getTypeEncoding(systemMethod));
    }else {
        method_exchangeImplementations(systemMethod, myMethod);
    }
}
然后这样调用
#import "NSMutableDictionary+ZYTCategory.h"
#import "NSObject+ZYTSwizz.h"

@implementation NSMutableDictionary (ZYTCategory)

+ (void)load {
    [super load];
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        @autoreleasepool {
            //类名后面跟的M表示可变字典,跟I表示不可变字典,跟0表示刚实例化还没有赋值
            [objc_getClass("__NSDictionaryM") swizzMethodWithSysSelector:@selector(setObject:forKey:) swizzMySelector:@selector(safe_setObject:forKey:)];

        }
    });
}
/** 防止当object或key为空时crash */
- (void)safe_setObject:(id)obj forKey:(NSString *)key {
    if (obj == nil || key == nil) {
        return;
    }
    [self safe_setObject:obj forKey:key];

}
以上就是通过Runtime来勾住某个方法达到替换某个方法实现的目的。没看懂的可以下载demo来仔细看看。再附一遍demo地址

demo链接

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值