ARC环境下异步网络请求引发自定义对象内存泄漏问题求助
ARC环境下异步网络请求导致CustomObject内存泄漏问题分析与解决
问题描述
在iOS ARC环境中,通过链式方法传递CustomObject存储HTML文件:
- 当文件存在本地时,流程正常,对象可被ARC正常释放;
- 当需要发起异步网络请求获取文件时,
CustomObject始终无法被ARC释放。
相关代码
CustomObject定义
@interface CustomObject : NSObject @property (strong) NSString *htmlPage1; @property (strong) NSString *htmlPage2; @property (strong) NSString *htmlPage3; @property (strong) NSMutableArray *array; @property (strong) DifferentCustomObject *c1; @end
对象创建与链式调用
- (void) loop: (int) n { CustomObject *co; int i; for (i = 0; i < n; ++i) { co = [[CustomObject alloc] init]; co.c1 = [[DifferentCustomObject alloc] init]; [self startFileFetchChain: co]; } }
链式请求核心代码(示例)
- (void) startFileFetchChain: (CustomObject *) co { __block NSURLSessionTask *task = nil; NSURL *pageURL; // 本地文件存在时的同步流程 if (localPageSource) { co.htmlPage1 = localPageSource; [self requestNextPageInTheChain: co]; } else { // 异步网络请求流程 task = [[NSURLSession sharedSession] dataTaskWithURL:pageURL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response; switch ([httpResponse statusCode]) { case 200: co.htmlPage1 = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; dispatch_async(dispatch_get_main_queue(),^{ [co.htmlPage1 writeToFile: localPath atomically:YES encoding:NSUTF8StringEncoding error:nil]; [self requestNextPageInTheChain: co]; }); break; default: [self endFileFetchChain: co]; break; } }]; [task resume]; } }
尝试释放对象的方法
- (void) endFileFetchChain: (CustomObject *) co { if (co.array) [co.array removeAllObjects]; if (co.c1) co.c1 = nil; co = nil; }
问题解答
1. 你的推测是否正确?
不完全准确。核心问题不是“主循环创建新对象导致原对象引用计数无法归零”,而是异步block对CustomObject的强引用未被正确处理:
- 在
NSURLSession的completion handler和dispatch_async的block中,直接引用了co,ARC会自动为co添加强引用,直到block执行完毕; - 如果链式调用中存在多层异步block,这些强引用会形成持续的引用链,即使调用
endFileFetchChain,也无法打破这条链(因为endFileFetchChain里的co = nil只是修改了方法参数的局部变量,对原对象的强引用没有影响)。
2. 如何重构代码解决内存泄漏?
方法一:使用弱引用打破block强引用
在异步任务前,对CustomObject创建弱引用,避免block持有强引用;同时在block内转为强引用,防止对象中途释放:
- (void) startFileFetchChain: (CustomObject *) co { __block NSURLSessionTask *task = nil; NSURL *pageURL; __weak typeof(CustomObject) weakCo = co; // 创建弱引用 if (localPageSource) { co.htmlPage1 = localPageSource; [self requestNextPageInTheChain: co]; } else { task = [[NSURLSession sharedSession] dataTaskWithURL:pageURL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response; typeof(CustomObject) strongCo = weakCo; // 转为强引用,保证操作完整性 if (!strongCo) return; switch ([httpResponse statusCode]) { case 200: strongCo.htmlPage1 = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; dispatch_async(dispatch_get_main_queue(),^{ typeof(CustomObject) queueStrongCo = weakCo; if (!queueStrongCo) return; [queueStrongCo.htmlPage1 writeToFile: localPath atomically:YES encoding:NSUTF8StringEncoding error:nil]; [self requestNextPageInTheChain: queueStrongCo]; }); break; default: [self endFileFetchChain: strongCo]; break; } }]; [task resume]; } }
方法二:优化endFileFetchChain方法
原方法中的co = nil无效,需清空对象内部所有强引用,减少引用计数:
- (void) endFileFetchChain: (CustomObject *) co { // 清空所有内部强引用 co.htmlPage1 = nil; co.htmlPage2 = nil; co.htmlPage3 = nil; [co.array removeAllObjects]; co.array = nil; co.c1 = nil; }
方法三:管理任务与对象的绑定生命周期
用NSMutableArray等容器持有所有未完成的NSURLSessionTask和对应的CustomObject,当任务完成或失败时,从容器中移除对应条目,确保外部引用被及时释放。
3. 自定义对象包含的其他对象会随父对象释放吗?
会。只要CustomObject被ARC正确释放(引用计数归零),它的所有strong属性(如c1、array等)会被ARC自动置为nil,这些子对象的引用计数会相应减少。如果子对象没有其他外部强引用,就会被ARC自动释放。
4. 是否需要为每个自定义对象编写自定义析构方法?
不需要。ARC会自动处理Objective-C对象的内存管理,只有当对象持有非ARC管理的资源(如Core Foundation对象、文件句柄、网络套接字等)时,才需要在dealloc方法中手动释放这些资源。对于纯Objective-C对象组成的属性,ARC会自动处理释放逻辑。
内容的提问来源于stack exchange,提问作者johnpurlia
相关产品推荐
相关产品推荐

