ARC 异步块最佳实践
ARC Async Block Best Practices
我的应用程序通过 HTTP
请求发送大量消息。我围绕一个 HTTP 请求编写了一个简单的包装器,它有一个主要方法来触发一个请求和 return 任何 NSData
作为结果。
看起来像这样:
- (void)sendRequest:(NSURLRequest *)request {
[[[NSURLSession sharedSession] dataTaskWithRequest:request
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
NSLog(@"---->> request returned");
if (_delegate == nil)
return;
if (error == nil) {
// forward along
[_delegate requestReturnedResult:data withResponse:response];
}
else {
// error occurred
[_delegate requestReturnedError:error withResponse:response];
}
}] resume];
}
这个HttpRequestHelper
对象是我的一个消息抽象对象OutboundMessage
的ivar
,将数据打包发送出去
我认为有一个 MessageSender
对象可以获取一些数据,创建一个 OutboundMessage
对象,然后将其发送出去。
这个抽象工作得很好,除了我认为我遇到了 ARC 的问题。
当有人打电话给[MessageSender send:]
时,我alloc
一个OutboundMessage
然后发送出去。
我通过 NSLog
看到的是我的 OutboundMessage
被清理了(大概当 send
退出时,我看到 HttpRequestHelper
被清理了。然后 我在 dataTaskWithRequest
的完成处理程序中看到了 NSLog
。
这向我表明,在 HTTP 请求完成之前,我的请求助手周围的所有内容都已清理干净。然后你可以想象,很快我尝试通过 delegate
冒泡我的回复,我得到 exc_bad_access
.
我可以想出一些有创意的方法来防止重新分配并防止这种情况发生,但我想知道的是从概念上讲,处理这种触发消息模式的最佳做法是什么,其中封装对象仅在请求已完成。
谢谢。
更新
每个包装器对象都使用 initWithDelegate:self
样式模式,我注意到这些委托是这样保存的:
@property (nonatomic, assign) id<HttpRequesterDelegate> delegate;
我将它们更改为 strong
,它似乎更正了它,但我不确定我现在是否正在泄漏内存,或者是否有更优化的方法。
首先,由NSURLSession - dataTaskWithRequest:completionHandler:
方法创建的NSURLSessionDataTask
对象保留了completionHandler
块对象。在数据任务完成时执行块之前,块对象不会被释放。
接下来,块自动捕获(保留)局部变量。代码中的下面一行
if (_delegate == nil)
与
相同
if (self.delegate == nil)
如果delegate
属性是strong
@property (nonatomic, strong) id<HttpRequesterDelegate> delegate;
然后self
被块保留。在这种情况下,self
是 HttpRequestHelper
对象。所以 HttpRequestHelper
对象和 delegate
对象只要数据任务完成就一直存在。很安全。
但是delegate
属性是assign
@property (nonatomic, assign) id<HttpRequesterDelegate> delegate;
与__unsafe_unretained
相同。 Blocks 根本不保留 __unsafe_unretained
对象。因此self
没有被保留,在数据任务完成前被释放
已添加
在这种情况下,您可以使用局部变量来保留 delegate
对象。因为 completionHandler 仅使用 delegate
对象,而不是 HttpRequestHelper
对象本身。
id<HttpRequesterDelegate> delegate = _delegate;
[[[NSURLSession sharedSession] dataTaskWithRequest:request
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
NSLog(@"---->> request returned");
/*
* Use `delegate` instead of `_delegate`
*/
if (delegate == nil)
return;
...
这样的话,delegate
属性的属性是strong还是assign,完全没有关系。
我的应用程序通过 HTTP
请求发送大量消息。我围绕一个 HTTP 请求编写了一个简单的包装器,它有一个主要方法来触发一个请求和 return 任何 NSData
作为结果。
看起来像这样:
- (void)sendRequest:(NSURLRequest *)request {
[[[NSURLSession sharedSession] dataTaskWithRequest:request
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
NSLog(@"---->> request returned");
if (_delegate == nil)
return;
if (error == nil) {
// forward along
[_delegate requestReturnedResult:data withResponse:response];
}
else {
// error occurred
[_delegate requestReturnedError:error withResponse:response];
}
}] resume];
}
这个HttpRequestHelper
对象是我的一个消息抽象对象OutboundMessage
的ivar
,将数据打包发送出去
我认为有一个 MessageSender
对象可以获取一些数据,创建一个 OutboundMessage
对象,然后将其发送出去。
这个抽象工作得很好,除了我认为我遇到了 ARC 的问题。
当有人打电话给[MessageSender send:]
时,我alloc
一个OutboundMessage
然后发送出去。
我通过 NSLog
看到的是我的 OutboundMessage
被清理了(大概当 send
退出时,我看到 HttpRequestHelper
被清理了。然后 我在 dataTaskWithRequest
的完成处理程序中看到了 NSLog
。
这向我表明,在 HTTP 请求完成之前,我的请求助手周围的所有内容都已清理干净。然后你可以想象,很快我尝试通过 delegate
冒泡我的回复,我得到 exc_bad_access
.
我可以想出一些有创意的方法来防止重新分配并防止这种情况发生,但我想知道的是从概念上讲,处理这种触发消息模式的最佳做法是什么,其中封装对象仅在请求已完成。
谢谢。
更新
每个包装器对象都使用 initWithDelegate:self
样式模式,我注意到这些委托是这样保存的:
@property (nonatomic, assign) id<HttpRequesterDelegate> delegate;
我将它们更改为 strong
,它似乎更正了它,但我不确定我现在是否正在泄漏内存,或者是否有更优化的方法。
首先,由NSURLSession - dataTaskWithRequest:completionHandler:
方法创建的NSURLSessionDataTask
对象保留了completionHandler
块对象。在数据任务完成时执行块之前,块对象不会被释放。
接下来,块自动捕获(保留)局部变量。代码中的下面一行
if (_delegate == nil)
与
相同if (self.delegate == nil)
如果delegate
属性是strong
@property (nonatomic, strong) id<HttpRequesterDelegate> delegate;
然后self
被块保留。在这种情况下,self
是 HttpRequestHelper
对象。所以 HttpRequestHelper
对象和 delegate
对象只要数据任务完成就一直存在。很安全。
但是delegate
属性是assign
@property (nonatomic, assign) id<HttpRequesterDelegate> delegate;
与__unsafe_unretained
相同。 Blocks 根本不保留 __unsafe_unretained
对象。因此self
没有被保留,在数据任务完成前被释放
已添加
在这种情况下,您可以使用局部变量来保留 delegate
对象。因为 completionHandler 仅使用 delegate
对象,而不是 HttpRequestHelper
对象本身。
id<HttpRequesterDelegate> delegate = _delegate;
[[[NSURLSession sharedSession] dataTaskWithRequest:request
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
NSLog(@"---->> request returned");
/*
* Use `delegate` instead of `_delegate`
*/
if (delegate == nil)
return;
...
这样的话,delegate
属性的属性是strong还是assign,完全没有关系。