异步请求的实现导致许多泄漏

时间:2012-04-23 08:01:11

标签: iphone objective-c ios memory-leaks asihttprequest

我继承了一个使用ASIHttpRequest进行所有网络通信的项目。我不清楚我们使用的是哪个特定版本。我只能说,从.h文件开始,特定文件上最早的创建日期是17/08/10(ASIDataDecompressor)。

我们正在使用完成和失败块。出于某种原因,通常会触发故障块,这应该只在服务器无法响应时才会发生。我们的日志看起来很清醒,我们还没有收到任何通知(Airbrake),在错误发生的时候出现了服务器问题,所以现在我正在向前推进,假设我们的服务器很好,它是应用程序罪魁祸首。

我决定通过Instruments(Leaks)运行应用程序并且惊讶地看到当我强制请求失败时,立即创建~27个泄漏。我不知道如何很好地绕过仪器,所以我现在还不确定如何处理这些信息。

我想我会发布我的代码,看看是否有任何明显的东西。

在viewDidLoad中,执行此代码

[[MyAPI sharedAPI] getAllHighlights:pageNumber:perPage onSuccess:^(NSString *receivedString,NSString *responseCode) {
        [self getResults:receivedString];
        if(![responseCode isEqualToString:@"Success"]) {
            [self hideProgressView];
            appDelegate.isDiscover_RefreshTime=YES;                
            [[MyAPI sharedAPI] showAlert:responseCode]; 
        } else {
            NSString *strLogEvent=@"Discover_Highlights_Loaded Page_";
            strLogEvent=[strLogEvent stringByAppendingFormat:@"%i",intPageNumber];
            [FlurryAnalytics logEvent:strLogEvent timed:YES];
        }
    } onFail:^(ASIFormDataRequest *request) {
        NSDictionary *parameters = [[MyAPI sharedAPI] prepareFailedRequestData:request file:@"Discover" method:_cmd];
        [FlurryAnalytics logEvent:@"Unable_to_Connect_to_Server" withParameters:parameters  timed:true];
        [self hideProgressView];
        appDelegate.isDiscover_RefreshTime=YES;

        [[AfarAPI sharedAPI] showAlert:@"Unable to Connect to Server."];
        [tblHighlightsGrid reloadData];
        [tblListHighlights reloadData];

}];

这些typedef已在API Singleton的顶部定义:

typedef void (^ASIBasicBlockWrapper)(NSString *responseString,NSString *responseCode);
typedef void (^ASIBasicBlockWrapperFail)(ASIFormDataRequest *request);

MyAPISingleton#getAllHighlights ...

- (void)getAllHighlights:(NSString *)pageNumber:(NSString *)perPage onSuccess:(ASIBasicBlockWrapper)cb1 onFail:(ASIBasicBlockWrapperFail)cb2{
    NSString *access_token= [[NSUserDefaults standardUserDefaults] objectForKey:@"access_token"];

    NSString *url = [baseURL stringByAppendingFormat:AFAR_GET_ALL_HIGHLIGHTS_ENDPOINT, pageNumber,perPage];
    if (access_token) { url = [url stringByAppendingFormat:ACCESS_TOKEN, access_token]; }

    __block ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:url]];
    [request setRequestMethod:@"GET"];
    [request setDelegate:self];  
    [self executeAsynchronousRequest:request onSuccess:cb1 onFail:cb2];
}

最后,MyAPI #secuteAsynchronousRequest:

- (void) executeAsynchronousRequest:(ASIFormDataRequest *)request onSuccess:(ASIBasicBlockWrapper)cb1 onFail:(ASIBasicBlockWrapperFail)cb2
{
    [request setCompletionBlock:^{
        int statusCode = [request responseStatusCode];
        NSString *statusMessage = [self statusErrorMessage:statusCode];
        cb1([request responseString],statusMessage);
    }];
    [request setFailedBlock:^{
        cb2(request);
    }];

    [request startAsynchronous];    
}

有什么突出的原因可以解释为什么会造成27次泄漏?

1 个答案:

答案 0 :(得分:0)

我想出来了。

ASIHttpRequest Documentation非常清楚您需要使用__block存储机制指定您的请求对象:

  

注意在声明请求时使用__block限定符,这很重要!它告诉块不保留请求,这对于防止保留周期很重要,因为请求将始终保留块。

在getAllHighlights()中,我正在这样做,但后来我将请求对象作为参数发送给另一个方法(executeAsyncRequest)。 __block存储类型只能在局部变量上声明,因此在方法签名中,request只是输入到普通的ASIFormDataRequest,因此好像它失去了__block状态。

诀窍是在一个块中使用之前施放(我不确定这在技术上是否准确)参数。

这是我的无泄漏执行executeAsyncRequest:

- (void) executeAsyncRequest:(ASIFormDataRequest *)request onSuccess:(ASIBasicBlockWrapper)cb1 onFail:(ASIBasicBlockWrapperFail)cb2
{
    // this is the important part. now we just need to make sure
    // to use blockSafeRequest _inside_ our blocks
    __block ASIFormDataRequest *blockSafeRequest = request;

    [request setCompletionBlock: ^{
        int statusCode = [blockSafeRequest responseStatusCode];
        NSString *statusMessage = [self statusErrorMessage:statusCode];
        cb1([blockSafeRequest responseString],statusMessage);
    }];

    [request setFailedBlock: ^{
        cb2(blockSafeRequest);
    }];

    [request startAsynchronous];    
}