NSURlConnection取消导致内存泄漏

时间:2011-07-24 14:20:52

标签: ios objective-c memory-management memory-leaks nsurlconnection

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

如果下载完成,我使用代码下载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];
}

你能帮我找出问题吗?

非常感谢。

2 个答案:

答案 0 :(得分:2)

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

如果是,请设置属性使用:self.transientData = [[[NSMutableData alloc] init] autorelease];或仅[NSMutableData data]; 剩下的对我来说似乎没问题。

答案 1 :(得分:1)

泄漏表明您的变量在该点被实例化,因此泄漏实际上并不是它开始的位置。您需要在取消方法中释放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]来电正在创建NSMutableData的实例,保留计数为+1。然后,如果您使用retain的属性(最好),则self.transientData =将另外保留为保留计数添加另外+1。这只是后来才发布一次,因为NSMutableData仍然会闲置,因此你会有泄漏。

在代码中进一步使用模式

  1. 创建实例并分配给本地变量
  2. 将实例分配给ivar
  3. 发布本地变量实例
  4. 这是我不在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并将指针设置为{{1这是相当安全的。