必要性:
当某个页面销毁后如果依然存在未结束的网络请求,那么不仅会浪费流量而且会占用一部分内存得不到释放直到请求结束。
时机:
当不需要处理请求返回结果的时候就可以在某个时机来 cancel 掉这些无用的请求,而这个时机就是 dealloc 的时候。
原理:
- 1.我目前项目的设计模式是 MVVM,请求类为单例。当开始一个请求时, 请求类会将该 task 以通知形式传给调用网络请求方法的控制器, 控制器以数组形式管理 tasks。
- (void)addAutoCancelRequestSessionTaskNoti {
@weakify(self);
// 接收执行请求的 sessionTask
[[NSNotificationCenter defaultCenter] addObserverForName:kRequestSessionTaskNotificationName object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
@strongify(self);
if (!self) return;
NSDictionary *userInfo = note.userInfo;
if (userInfo == nil || ![userInfo allKeys].count) return;
if (![[userInfo valueForKey:kRequestSessionTaskControllerNameKey] isEqualToString:NSStringFromClass([self class])]) return;
NSURLSessionTask *task = (NSURLSessionTask *)[userInfo valueForKey:kRequestSessionTaskKey];
[self addAutoCancelSessionTask:task];
}];
// 接收执行的请求成功/失败的 sessionTask
[[NSNotificationCenter defaultCenter] addObserverForName:kAutoCancelSessionTaskNotificationName object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
@strongify(self);
if (!self) return;
NSDictionary *userInfo = note.userInfo;
if (userInfo == nil || ![userInfo allKeys].count) return;
if (![[userInfo valueForKey:kRequestSessionTaskControllerNameKey] isEqualToString:NSStringFromClass([self class])]) return;
NSURLSessionTask *task = (NSURLSessionTask *)[userInfo valueForKey:kRequestSessionTaskKey];
[self removeAutoCancelSessionTask:task];
}];
}
- 2.当结束请求后, 请求类同样会通知控制器来移除该 task。
- 3.当该控制器 delloc 后, 如依然存在未完成 task, 则会将当前 tasks cancel。
/// 取消当前页所有未完成请求
- (void)autoCancelCurrentRequestTasks {
if (!self.autoCancelSessionTasks.count) return;
[NETWORK cancelRequestWithTasks:[[NSArray alloc] initWithArray:self.autoCancelSessionTasks copyItems:YES]];
[self.autoCancelSessionTasks removeAllObjects];
}
- 4.查找网络请求控制器无侵入,通过堆栈信息获取网络请求控制器。
/// 通过堆栈信息获取网络请求控制器
- (UIViewController *)searchNetworkRequestVc {
NSArray<NSString *> *stackSymbols = [NSThread callStackSymbols];
for (NSString *symbol in stackSymbols) {
NSArray<NSString *> *array = [symbol componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"[]"]];
if (array.count < 2) continue;
NSString *selString = array[1];
if (selString.length) {
NSArray *tempArr = [selString componentsSeparatedByString:@" "];
if (tempArr[0]) {
Class c = NSClassFromString(tempArr[0]);
if ([c isSubclassOfClass:[UIViewController class]]) {
return (UIViewController *)c;
}
}
}
}
return nil;
}
不足:
- 1.假设名称为 XXViewController 的控制器当前存在多个,则会造成通知错乱;
- 2.因为 dealloc 不能被 hook,所以必须有一个父控制器来处理 dealloc 后的 cancel 事件。
参考链接:
https://www.jianshu.com/p/20f6172524d6
第三方:
最后附上
- 代码地址:AutoCancelRequest
网友评论