一、引言
Objective-C是的运行时特性在iOS开发中应用广泛,通过runtime方法,开发者可以在运行时动态为类添加方法,修改类的方法,系统的class\_addMethod()方法和class\_replaceMethod()方法可以十分简单的添加和修改方法,然而,直接使用这两个函数有时并不安全,其主要问题有如下几点:
1.在进行动态函数修改的时候,有可能其他线程也在做同样的操作。
2.在继承中,子类执行父类替换的方法会出现问题。
3.函数的替换必须依靠_cmd参数。
4.可能会出现命名冲突。
有关Objective-C运行时的相关内容可在如下博客中查看:http://my.oschina.net/u/2340880/blog/489072。
RSSwizzle框架可以解决上面所有问题,在要求比较高的项目中如果需要使用到运行时函数替换的需求,可以直接使用这个框架。git地址如下:
https://github.com/rabovik/RSSwizzle。
RSSwizzle中提供了两种使用方式,一种是通过调用类方法来实现函数的替换,另一种是使用RSSwizzle定义的宏来进行函数的替换。使用类方法的方式示例如下:
1 2 3 4 5 6 7 8 9 10 11 12
| - (void)viewDidLoad { [super viewDidLoad]; [RSSwizzle swizzleClassMethod:NSSelectorFromString(@"log") inClass:NSClassFromString(@"ViewController") newImpFactory:^id(RSSwizzleInfo *swizzleInfo) { return ^(__unsafe_unretained id self){ NSLog(@"Class log Swizzle"); }; }]; } +(void)log{ NSLog(@"Class log"); }
|
这个函数用来替换类方法,第1个参数为要替换的函数选择器,第2个参数为要替换此函数的类,block参数中需要返回一个方法函数,这个函数为要替换成的函数,要和原函数类型相同。在类中的函数默认都会有一个名为self的id参数。进行实例函数的替换实例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| - (void)viewDidLoad { [super viewDidLoad];
[RSSwizzle swizzleInstanceMethod:NSSelectorFromString(@"touchesBegan:withEvent:") inClass:NSClassFromString(@"ViewController") newImpFactory:^id(RSSwizzleInfo *swizzleInfo) { return ^(__unsafe_unretained id self,NSSet* touches,UIEvent* event){ NSLog(@"text Swizzle"); }; } mode:RSSwizzleModeAlways key:@"key"]; } -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ NSLog(@"test"); [ViewController log]; }
|
替换模式的枚举RSSwizzleMode意义如下:
1 2 3 4 5 6 7 8
| typedef NS_ENUM(NSUInteger, RSSwizzleMode) { RSSwizzleModeAlways = 0, RSSwizzleModeOncePerClass = 1, RSSwizzleModeOncePerClassAndSuperclasses = 2 };
|
使用宏的模式进行方法替换操作的代码更加简单,示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
RSSwizzleClassMethod(NSClassFromString(@"ViewController"), NSSelectorFromString(@"log"), RSSWReturnType(void), RSSWArguments(), RSSWReplacement( { RSSWCallOriginal(); NSLog(@"Class log Swillze"); }));
RSSwizzleInstanceMethod(NSClassFromString(@"ViewController"),NSSelectorFromString(@"touchesBegan:withEvent:"), RSSWReturnType(void), RSSWArguments(NSSet* touchs,UIEvent * event), RSSWReplacement({ NSLog(@"test Swizzle"); }), RSSwizzleModeAlways, @"key");
|
在宏内,可以直接调用RSSWCallOriginal()来执行替换前的原始函数,十分方便。
专注技术,热爱生活,交流技术,也做朋友。
——珲少 QQ群:203317592