Method Swizzling 可以被称为方法替换, 可以干出一些神奇的事情, 例如 Monkey 之前分享的 JSPatch 相关.
这里分享的是用于页面加载时间的统计.
起因, 早先看到一些 SDK 提供无需新增代码, 只要加入 SDK 就可以监控到 App 的页面加载时间, 网络请求失败率等情况. 这种黑科技一下就勾起我的好奇心, 开始了研究相关信息. 这些 SDK 都是商业 SDK, 无法看到开源代码, 只能自己琢磨. 猜测是某种 hook 技术, 然后用这个一点点搜索, 查到了 swizzle. 虽然无法确定这个就是他们的实际解决方案, 但这个应该是最接近的方案了.
init-初始化程序
loadView-只初始化 view
viewDidLoad-加载视图
viewWillAppear-UIViewController 对象的视图即将加入窗口时调用;
viewDidApper-UIViewController 对象的视图已经加入到窗口时调用;
viewWillDisappear-UIViewController 对象的视图即将消失、被覆盖或是隐藏时调用;
viewDidDisappear-UIViewController 对象的视图已经消失、被覆盖或是隐藏时调用;
viewVillUnload-当内存过低时,需要释放一些不需要使用的视图时,即将释放时调用;
viewDidUnload-当内存过低,释放一些不需要的视图时调用。
我们统计页面加载时间, 可以认为就是记录这几个函数的加载时间点,然后求差值. method swizzling 可以对 UIViewController 类进行扩展.加入我们的统计打点. 这样加入一次,所有的 UIViewController 子类也都可以统计加载时间了.
#import "UIViewController+Logging.h"
#import <objc/runtime.h>
static inline void swizzle_methods(Class class, SEL originalSelector, SEL swizzledSelector) {
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
BOOL didAddMethod =
class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
@implementation UIViewController (Logging)
+(void)load{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
SEL originalSelector = @selector(loadView);
SEL swizzledSelector = @selector(log_loadView);
swizzle_methods([self class], originalSelector, swizzledSelector);
SEL originalSelectorFinished = @selector(viewDidAppear:);
SEL swizzledSelectorFinished = @selector(log_viewDidAppear:);
swizzle_methods([self class], originalSelectorFinished, swizzledSelectorFinished);
});
}
#pragma mark - Method Swizzling
-(void)log_loadView{
[self log_loadView];
NSLog(@"test loadView: %@", [self class]);
}
-(void)log_viewDidAppear:(BOOL)animated{
[self log_viewDidAppear:animated];
NSLog(@"test viewDidAppear: %@", [self class]);
}
@end
每次 UIViewController 执行 loadView 方法时, 实际执行的是 log_loadView. (执行 log_loadView 时, 执行的是 loadView.) 就这样,每个 UIViewController 子类都会自带统计打点功能.
Method originalMethod = class_getInstanceMethod(class, originalSelector)
, originalMethod 会指向 0x0, 所以这方面如果有啥方法可以指导一下ios 逆向工程 - 内部钩子 (Method Swizzling) http://my.oschina.net/iq19900204/blog/411372
iOS ViewController 生命周期 http://www.cnblogs.com/chenyg32/p/3873679.html
Objective-C Runtime 运行时之四:Method Swizzling http://southpeak.github.io/blog/2014/11/06/objective-c-runtime-yun-xing-shi-zhi-si-:method-swizzling/