2

注意:这个类似的 SO question讨论了如何构建相同的类,但没有解决我与使用该类相关的内存管理问题。

我想使用sendAsynchronousRequest:queue:completionHandler:,但我需要支持 iOS 4.2。因此,我创建了一个名为的自定义类JCURLRequest,它使用NSURLConnection并生成了一个不错的接口:

- (void)sendRequest:(NSURLRequest *)request 
    responseHandler:(JCURLResponseHandler)responseHandler;

我有一个关于使用这个类的问题(我在 ARC 内存管理中):

  • 当我创建我的 JCURLRequest 对象时,我需要保留对该对象的引用吗?还是我可以“开火然后忘记它”?

注意:我了解 ARC 的基础知识 - 如果您有一个指向对象的指针,它将保持活动状态,如果没有更多指向对象的指针,它将在下一个自动释放池中释放。

因此,我想知道 - 我可以像(1)那样称呼它还是我需要使用(2)

(1)

JCURLRequest *jcURLRequest = [[JCURLRequest alloc] init];
[jcURLRequest sendRequest:myRequest
          responseHandler:^(NSData *data, NSError *error) { ... }];
// Assuming I don't maintain a reference to jcURLRequest after this

(2)

// Assume @property (strong) JCURLRequest *jcURLRequest;
//        @synthesize jcURLRequest = _jcURLRequest;
self.jcURLRequest = [[JCURLRequest alloc] init];   
[self.jcURLRequest sendRequest:myRequest
          responseHandler:^(NSData *data, NSError *error) { ... }];

NSURLConnection使用异步回调,所以我的想法是我必须使用(2)。这是因为 - 当委托回调“回调”时,jcURLRequest 实例可能已经在自动释放池中被清理了。

不过我很困惑,因为我已经用 (1) 进行了测试,它“似乎”工作正常。但是,我的想法是,它的工作可能只是巧合 - 即。确实没有更多指向 jcURLRequest 对象的有效指针,但 iOS 只是还没有解决它。

以下是完整的JCURLRequest课程供参考

//  JCURLRequest.h
#import <Foundation/Foundation.h>

typedef void (^JCURLResponseHandler) (NSData *data, NSError *error);

@interface JCURLRequest : NSObject
- (void)sendRequest:(NSURLRequest *)request responseHandler:(JCURLResponseHandler)responseHandler;
@end



//  JCURLRequest.m
#import "JCURLRequest.h"

@interface JCURLRequest ()
{
    JCURLResponseHandler responseHandler;
}
@property (strong, nonatomic) NSMutableData *responseData;
@end

@implementation JCURLRequest
@synthesize responseData = _responseData;


#pragma mark - Public API

- (void)sendRequest:(NSURLRequest *)request responseHandler:(JCURLResponseHandler)handler
{
    responseHandler = [handler copy];
    dispatch_async(dispatch_get_main_queue(), ^{
        __unused NSURLConnection *connectionNotNeeded = [[NSURLConnection alloc] initWithRequest:request delegate:self];
    });
}


#pragma mark - Private API

- (NSMutableData *)responseData
{
    if (!_responseData) 
    {
        _responseData = _responseData = [[NSMutableData alloc] initWithLength:0];
    }

    return _responseData;
}


#pragma mark - URL Connection Methods

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response 
{
    [self.responseData setLength:0];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data 
{
    [self.responseData appendData:data];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error 
{
    responseHandler(nil, error);
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection 
{    
    responseHandler([NSData dataWithData:self.responseData], nil);
}

@end
4

2 回答 2

2

我建议保留JCURLRequest安全和正确。以下面的代码为例:

@interface AsyncObject : NSObject
{
    BOOL ivar;
}
-(void)sendRequest:(void(^)()) callback;
@end

@implementation AsyncObject
-(void)sendRequest:(void (^)())callback
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"Starting Long Async Task");
        sleep(15);
        //ivar = YES;
        NSLog(@"Calling Callback");
        callback();
    });
}
-(void)dealloc
{
    NSLog(@"Deallocated!");
}
@end

    ...
    [[[AsyncObject alloc] init] sendRequest:^{ NSLog(@"Called Back"); }];

如果你留下ivar注释掉,打印出来的第一件事就是Deallocated!这意味着AsyncObject在请求完成之前就被释放了。现在,如果您取消注释ivar,那么Deallocated!现在应该是最后一个,因为该块正确保留了正确的值。但是,如果调度使用_weakself.

__weak AsyncObject *self_ = self;
    ... //Inside gcd async method
    self_->ivar = YES; //This will break
于 2012-04-23T20:31:19.887 回答
2

NSURLConnection 在连接处于活动状态时保留它的委托,以及它自己。因此,您的解决方案有效并非巧合。

我找不到任何官方消息来源,但已在 SO 上非正式地确认:

于 2012-04-23T21:09:35.900 回答