iOS10 NSURLSession的delegate NSURLSessionTaskDelegate增加了
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
在NSURLSessionTaskMetrics里的NSURLSessionTaskTransactionMetrics记录着一次请求的时间,如图:
要记录每个请求的各种环节时间,只需要实现这个delegate方法就行了。但是实际项目里不会只有一个NSURLSession,而且大部分时候是封装过的网络请求方法隐藏了NSURLSession。
通过逆向发现NSURLSession有个未暴露的方法:
- (void)delegate_task:(NSURLSessionTask *) didFinishCollectingMetrics:(NSURLSessionTaskMetrics *) completion:(id)
在这方法中调用了上面的delegate方法,因此可以hook NSURLSession类的这个方法完成对所有NSURLSession的请求的收集。
这个方法被调用的前提是实现上面的delegate方法,这样就有了“鸡”“蛋”问题。
static
void aURLSessionTaskDidFinishCollectingMetrics(id self, SEL cmd, NSURLSession *session, NSURLSessionTask *task, NSURLSessionTaskMetrics *metrics) {}
@interface TortoiseDelegate : NSObject<NSURLSessionDelegate>
@end
@implementation TortoiseDelegate
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics {
aURLSessionTaskDidFinishCollectingMetrics(self, _cmd, session, task, metrics);
}
@end
[NSURLSession aspect_hookSelector:@selector(sessionWithConfiguration:delegate:delegateQueue:)
withOptions:AspectPositionAfter
usingBlock:^(id<AspectInfo> info) {
id<NSURLSessionTaskDelegate> delegate = [info arguments][1];
if (!delegate) {
delegate = [[TortoiseDelegate alloc] init];
} else {
if (![delegate respondsToSelector:@selector(URLSession:task:didFinishCollectingMetrics:)]) {
__unused auto result = class_addMethod(object_getClass(delegate), @selector(URLSession:task:didFinishCollectingMetrics:), (IMP)aURLSessionTaskDidFinishCollectingMetrics, "v@:@@@");
assert(result);
}
}
}
error:nil];
这样就可以监测App里的所有NSURLSession请求了。