NSOperation集成NSURLSessionDownloadTask取消操作时崩溃问题
崩溃根因
- 代码中使用
__unsafe_unretained修饰weakSelf,该修饰符修饰的对象被释放后指针不会自动置为nil,会保留原内存地址成为野指针,回调触发后访问野指针直接抛出EXC_BAD_ACCESS错误。 NSURLSession的任务回调会被session实例持有,你使用的sharedSession是全局单例,生命周期和应用一致,所以即便DownloadOperation对象被释放,任务完成/取消时回调仍然会触发,触发后访问野指针就会崩溃。
修复步骤
1. 替换内存修饰符,增加空判断
将initWithSession方法中的__unsafe_unretained替换为__weak,并在回调内部先尝试强持有对象,判断非空再执行后续逻辑:
- (instancetype)initWithSession:(NSURLSession *)session downloadTaskURL:(NSURL *)downloadTaskURL completionHandler:(nullable void (^)(NSURL * _Nullable, NSURLResponse * _Nullable, NSError * _Nullable))completionHandler { self = [super init]; if (self) { // 替换为__weak,对象释放后自动置nil __weak DownloadOperation *weakSelf = self; _task = [[NSURLSession sharedSession] downloadTaskWithURL:downloadTaskURL completionHandler:^(NSURL * _Nullable localURL, NSURLResponse * _Nullable response, NSError * _Nullable error) { // 内部强持有,避免执行过程中对象被释放 __strong DownloadOperation *strongSelf = weakSelf; // 对象已经释放,直接返回避免野指针访问,可选继续执行外部传入的completionHandler if (completionHandler) { completionHandler(localURL, response, error); } if (!strongSelf) { return; } strongSelf.state = kFinished; }]; } return self; }
2. 可选优化(适配操作依赖)
当前你重写了isReady方法直接返回自定义状态,会丢失NSOperation默认的依赖支持,如果你的业务需要用到操作依赖,可以修改isReady的实现:
- (BOOL)isReady { return [super isReady] && (self.state == kReady); }
内容的提问来源于stack exchange,提问作者Petter Sjölund
相关产品推荐
相关产品推荐

