iOS runtime之swizzling
2017-08-15 本文已影响19人
秦萍健
问题一:
接手一个新的ios项目, 如何在复杂的项目结构中,快速定位当前显示的是哪个Controller, 直接打印此Controller的类名呢?
解决方法:
给UIViewController新增一个Category, 在Category的load方法里, 使用runtime的swizzling机制, 实现项目原来的viewWillAppear方法与自己新增的方法进行互换.
(如果项目中有基类(baseViewController), 可以直接在基类中打印)
代码实现:
#import "UIViewController+Swizzling.h"
#import <objc/runtime.h>
@implementation UIViewController (Swizzling)
//系统自带的加载方法
+(void)load{
//我们只有在开发的时候才需要查看哪个viewController将出现
//所以在release模式下就没必要进行方法的交换
#ifdef DEBUG
//原本的viewWillAppear方法
Method viewWillAppear = class_getInstanceMethod(self, @selector(viewWillAppear:));
//需要替换成 能够输出日志的viewWillAppear
Method logViewWillAppear = class_getInstanceMethod(self, @selector(logViewWillAppear:));
//两方法进行交换
method_exchangeImplementations(viewWillAppear, logViewWillAppear);
#endif
}
//实现自己的方法
-(void)logViewWillAppear:(BOOL)animated{
//我的目的:
NSString *className = NSStringFromClass([self class]);
NSLog(@"当前控制器是:%@",className);
//交换了方法以后, 记得原本的viewWillAppear方法也要走一遍,不然你就直接改了原来项目的代码了
//so:调用原本的viewWillAppear:(load时已经交换了,所以如下调用)
[self logViewWillAppear:animated];
}
问题二:
我们知道, 系统的NSMutableArray的addObject方法, 不可以用来添加nil对象, 否则会引起crash. 那么, 我们能使用swizzling对它修改, 使他即使新增了nil对象, 也不引起crash, 而且也不会"真正的"加入数组里.
代码实现:
#import "NSMutableArray+Swizzling.h"
#import <objc/runtime.h>
@implementation NSMutableArray (Swizzling)
+(void)load{
Method system = class_getInstanceMethod(NSClassFromString(@"__NSArrayM"), @selector(addObject:));
Method mine = class_getInstanceMethod(NSClassFromString(@"__NSArrayM"), @selector(addMineObj:));
//交换:
method_exchangeImplementations(system, mine);
}
//实现自己的方法
-(void)addMineObj:(id)obj{
if (obj != nil) {
//因为已经交换了, 所以实质是运行system方法
[self addMineObj:obj];
}
//如果为nil, 实质并没有加进去.
}