iOS底层系列之<24>--Runtime(十一)runtime 方法的API

1、方法替换class_replaceMethod

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self test7];
}

void myRun() {
    NSLog(@"--myRun");
}

- (void)test7 {
    Person *person = [[Person alloc] init];
    class_replaceMethod([Person class], @selector(run), (IMP)myRun, "v@:");
    [person run];
}

可以使用block

 class_replaceMethod([Person class], @selector(run), imp_implementationWithBlock(^{
        NSLog(@"block--imp");
    }), "v@:");

2、方法交换method_exchangeImplementations

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self test7];
}


- (void)test7 {
    Person *person = [[Person alloc] init];
    Method runMethod = class_getInstanceMethod([Person class], @selector(run));
    Method testMethod = class_getInstanceMethod([Person class], @selector(test));
    method_exchangeImplementations(runMethod, testMethod);
    [person run];
}

3、拦截按钮点击事件应用

#import "UIControl+Extension.h"
#import <objc/runtime.h>

@implementation UIControl (Extension)

+ (void)load {
    NSLog(@"%s",__func__);
    Method method1 = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:));
    Method method2 = class_getInstanceMethod(self, @selector(jh_sendAction:to:forEvent:));
    method_exchangeImplementations(method1, method2);
}

- (void)jh_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event {
    // 注意,这里不会产生递归调用,因为上面已经交换了selector的方法实现了
    [self jh_sendAction:action to:target forEvent:event];
    
    if ([self isKindOfClass:[UIButton class]]) {
        NSLog(@"拦截到按钮点击");
    }
}

@end

4、数组防止添加空对象

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSString *str = nil;
    NSMutableArray *arr = [NSMutableArray array];
    [arr addObject:@"a"];
    [arr addObject:@"b"];
    [arr addObject:str];
    NSLog(@"arr-%@",arr);
}

报错:
*** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil’

  • 思路:尝试通过runtime修改方法
+ (void)load {
    NSLog(@"%s",__func__);
    Method method1 = class_getInstanceMethod(self, @selector(addObject:));
    Method method2 = class_getInstanceMethod(self, @selector(jh_addObject:));
    method_exchangeImplementations(method1, method2);
}

- (void)jh_addObject:(id)anObject {
    if (anObject == nil) {
        NSLog(@"添加为空的");
    } else {
        [self jh_addObject:anObject];
    }
    
}

看起来好像没问题,但是时间上还是会报错

*** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil’

  • 1、思考:
  1. 为什么还是会报错呢,通过打印可以看到,是有进入load方法的
  2. 注意到:[__NSArrayM insertObject:atIndex:] ,这个类是 __NSArrayM
  3. 实际上NSString、 NSDictonary、NSArray、NSMutableArray等,都是属于__NSArrayM的类簇
  4. 改进如下:
+ (void)load {
    NSLog(@"%s",__func__);
    Class cls = NSClassFromString(@"__NSArrayM");
    Method method1 = class_getInstanceMethod(cls, @selector(addObject:));
    Method method2 = class_getInstanceMethod(cls, @selector(jh_addObject:));
    method_exchangeImplementations(method1, method2);
}

打印结果:
2022-01-18 11:43:05.169324+0800 super-interview[38418:4264979] +[NSMutableArray(Extension) load]
2022-01-18 11:43:05.294043+0800 super-interview[38418:4264979] 添加为空的
2022-01-18 11:43:05.294251+0800 super-interview[38418:4264979] arr-(
a,
b
)

结论:交换成功

  • 2、进一步思考

[__NSArrayM insertObject:atIndex:] 本来是调用add方法,却报错来到了insert方法
容易猜想到内部实现,最终是调用insert方法
为了统一处理add 和 insert方法,直接替换insert方法不是更好吗
进步一改进:

+ (void)load {
    NSLog(@"%s",__func__);
    Class cls = NSClassFromString(@"__NSArrayM");
    
    Method method1 = class_getInstanceMethod(cls, @selector(insertObject:atIndex:));
    Method method2 = class_getInstanceMethod(cls, @selector(jh_insertObject:atIndex:));
    method_exchangeImplementations(method1, method2);
}

- (void)jh_insertObject:(id)anObject atIndex:(NSUInteger)index {
    if (anObject == nil) {
        NSLog(@"添加为空的");
    } else {
        [self jh_insertObject:anObject atIndex:index];
    }
}

打印结果:
2022-01-18 11:46:24.681854+0800 super-interview[38561:4268440] +[NSMutableArray(Extension) load]
2022-01-18 11:46:24.799525+0800 super-interview[38561:4268440] 添加为空的
2022-01-18 11:46:24.799678+0800 super-interview[38561:4268440] arr-(
a,
b
)

  • 结论:有时候看到的类不一定是类本身,可以通过报错打印结果,看实际上是什么类,然后调用runtime的API的时候,传参class的时候,就传那个类
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值