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