程序员

iOS 封装runtime库,一句话可实现方法交换

2020-08-03  本文已影响0人  Allan_野草

长话短说,直接上代码比较直观:

[CCRuntime exchangeMethod:[self class] name:@"viewWillAppear:" withBlock:^void(id _self, BOOL animated) {
    NSLog(@"已经被hook了");
    
    // 原来的方法实现会被交换至新的方法,方法名可通过下面获取
  
    // 回调原来viewWillAppear里面的代码
    [_self performSelector:NSSelectorFromString([CCRuntime exchangeOriginMethodUsingSelectorName:@"viewWillAppear:"]) withObject:@(animated)];
}];

至此,viewWillAppear已经成功被替换掉了。在回调viewWillAppear之前,会先执行打印代码。

如何实现?

我把原方法的实现替换成block,原方法的实现丢到一个新的方法。新的方法名可获取,有需要再回调即可。点击下载源码

其它介绍

除了方法交换之处,我还提供了其它API,提供更为便携地通过runtime操作方法、类、属性:

方法

一、为类动态添加一个方法,用block去构造方法实现:

[CCRuntime addMethodTo:[self class] name:@"getUserName:" withBlock:^int(id _self, int *userId){
    NSLog(@"userId是:%d", userId);
    return @"Allan";
} returnType:@"id" paramTypes:@[@"int"]];

二、为类动态添加一个方法,用已有方法去构造方法实现:

[CCRuntime addMethodTo:[self class] name:@"bar" from:[self class] name:@"foo"];

[self performSelector:NSSelectorFromString(@"bar")]; /* 打印:我是foo */

-(void)foo {
    NSLog(@"我是foo");
}

三、重置一个方法的实现,用block重置

[CCRuntime resetMethod:[self class] name:@"foo" withBlock:^{
    NSLog(@"我不是foo了呀");
}];
[self foo]; /* 打印:我不是foo了呀 */

四、交换一个方法,这个方法会以block作为方法实现:

[CCRuntime exchangeMethod:[self class] name:@"viewWillAppear:" withBlock:^void(id _self, BOOL animated) {
    NSLog(@"已经被hook了");
    
    // 原来的方法实现会被交换至新的方法,方法名可通过下面获取
  
    // 回调原来viewWillAppear里面的代码
    [_self performSelector:NSSelectorFromString([CCRuntime exchangeOriginMethodUsingSelectorName:@"viewWillAppear:"]) withObject:@(animated)];
}];

在之前,我们进行方法交换的一般做法是:多写一个方法,然后在初始化时用runtime库交换实现。

现在可以优雅地聚合代码和快速实现需求了。

属性

获取属性列表:

NSArray *props = [CCRuntime getPropertyListOnlyName:[self class]];

为对象属性添加动态属性:

UIViewController *vc = [UIViewController new];

/* 类似分类添加属性的做法,好处是不必再多写一个分类文件 */
[CCRuntime setObject:vc copyValue:@"广州" forKey:@"address"];

NSLog(@"address是:%@", [CCRuntime object:vc valueForKey:@"address"]);

动态创建一个类:

Class cls = [CCRuntime buildClass:[UIViewController class] name:@"TestViewController" ivarAndMethodBulider:^(Class  _Nonnull __unsafe_unretained cls) {
    // 可在block里面添加成员变量&方法
    [CCRuntime addIvar:cls name:@"name"];
    [CCRuntime addMethodTo:cls name:@"test" withBlock:^{
        NSLog(@"i am test控制器的方法");
    } returnType:@"void" paramTypes:@[]];
}];


UIViewController *vc = (UIViewController *)[CCRuntime getClassInstance:cls];

// 1.测试下成员变量
[CCRuntime setIvarValue:vc name:@"name" value:@"i am test控制器"];
NSLog(@"%@", [CCRuntime getIvarValue:vc name:@"name"]);

// 2.测试下方法
[vc performSelector:NSSelectorFromString(@"test")];

// 3.尝试下present刚才动态创建的类
vc.view.backgroundColor = [UIColor cyanColor];
[self presentViewController:vc animated:YES completion:nil];
上一篇下一篇

猜你喜欢

热点阅读