繁体   English   中英

NSURlConnection取消导致内存泄漏

[英]NSURlConnection cancel cause memory leak

这是一个非常奇怪的错误。

如果下载完成,我使用代码通过NSURLConnection下载文件,但没有泄漏。 但是,如果取消下载,则无法释放1Mo内存。 我已经用仪器进行了测试,发现造成这种泄漏的方法是

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data

真的很奇怪

这是我用于创建,管理和取消下载的代码

注意:此下载是核心数据管理对象的一部分,但我认为核心数据不负责我的泄漏。

- (void)loadURL:(NSURL *)url
{

    if (currentLoadingDatas) { // just bool for prevent multiple download

        return;
    }
    currentLoadingDatas = YES;


    NSURLRequest *request = [[NSURLRequest alloc] 
                         initWithURL: url
                         cachePolicy:             NSURLRequestReloadIgnoringLocalAndRemoteCacheData
                         timeoutInterval: 60
                         ];
    connectionDatas = [[NSURLConnection alloc] initWithRequest:request delegate:self];
    [request release];

}

#pragma mark NSURLConnection Delegates
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{

    if (!self.transientData) {

        self.transientData = [[NSMutableData alloc] init];
    }
    [self.transientData setLength:0];



}

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


}



- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{

    [self willChangeValueForKey:@"datas"];
[self setValue:self.transientData forKey:@"datas"];
    [self didChangeValueForKey:@"datas"];

    [self.transientData release];


    NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:nil];
    [NSURLCache setSharedURLCache:sharedCache];
    [sharedCache release];



}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {


    NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:nil];
    [NSURLCache setSharedURLCache:sharedCache];
    [sharedCache release];


}

- (NSCachedURLResponse *)connection:(NSURLConnection *)connection     willCacheResponse:(NSCachedURLResponse *)cachedResponse
{
    return nil;
}

-(void)cancelDownload
{
    [self.connectionDatas cancel];


}

// fired by coreData 
-(void)willTurnIntoFault
{


    self.transientData = nil;
     [self cancelDownload];

    [super willTurnIntoFault];
}

// fired by coreData 
-(void)didTurnIntoFault
{

    [connectionDatas release];

NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:nil];
    [NSURLCache setSharedURLCache:sharedCache];
    [sharedCache release];

    [self.transientData release];
    [super didTurnIntoFault];
}

您能帮我找出问题所在吗?

非常感谢。

self.transientData属性如何声明?
因为您使用以下self.transientData = [[NSMutableData alloc] init];初始化: self.transientData = [[NSMutableData alloc] init]; 并且如果该属性设置为保留该值,则需要释放两次。

如果是这样,要设置属性,请使用: self.transientData = [[[NSMutableData alloc] init] autorelease]; 或只是[NSMutableData data];
其余的对我来说似乎还可以。

泄漏表明您的变量在该点被实例化,因此泄漏实际上不是泄漏所在的位置。 您需要像这样在您的cancel方法中释放transientData

- (void)cancelDownload
{
  [self.connectionDatas cancel];
  self.transientData = nil;
}

您的编码风格存在一些不一致的问题,可能使您更难以从心理上追踪正在发生的事情。 如果您坚持自己的标准,那么遵循流程应该会更容易。

这可能是发生另一个泄漏的地方

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    if (!self.transientData) {
        self.transientData = [[NSMutableData alloc] init]; // leaky line
    }
    [self.transientData setLength:0];
}

[[NSMutableData alloc] init]调用正在创建保留计数为+1的NSMutableData实例。 然后,如果您使用带有retain属性(最佳),则self.transientData =将进行另一个保留,这将为保留计数增加另一个+1。 此版本以后仅发布一次,因此您会泄漏,因为NSMutableData仍然会徘徊。

在代码中进一步使用模式

  1. 创建实例并分配给局部变量
  2. 将实例分配给ivar
  3. 释放局部变量实例

这是我不在init方法中时将使用的模式。 因此,之前的方法应更改为:

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    if (!self.transientData) {
        NSMutableData *tmpTransientData = [[NSMutableData alloc] init];
        self.transientData = tmpTransientData;
        [tmpTransientData release];
    }
    [self.transientData setLength:0];
}

这具有不使用自动释放的优点,因此您可以更明确地确定何时不再需要该对象,如果可能,则在内存较小的设备上首选该对象。

整理一下可能会有益的另一个不一致之处是您释放ivars的方式。 您已经在代码中互换执行了此操作

[self.transientData release];
self.transientData = nil;

我将在我的代码中使用后者(而不是在dealloc )作为实例变量,因为synthesized setter将为您调用release并将指针设置为nil ,这要安全得多。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM