美文网首页程序员
iOS 封装runtime库,一句话可实现方法交换

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

作者: Allan_野草 | 来源:发表于2020-08-03 11:17 被阅读0次

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

    [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];
    

    相关文章

      网友评论

        本文标题:iOS 封装runtime库,一句话可实现方法交换

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