【问题标题】:NSURlConnection cancel cause memory leakNSURlConnection 取消导致内存泄漏
【发布时间】:2011-07-24 14:20:52
【问题描述】:

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

我正在使用代码下载带有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];
}

你能帮我找出问题吗?

非常感谢。

【问题讨论】:

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


    【解决方案1】:

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

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

    【讨论】:

    • 在 dealloc 或取消函数中释放 self.transientData 应该可以解决您的问题 ...
    • @Girish Kolari - 没错,但为什么要这样做,而不是正确地进行第一次分配?
    • 当self.transientData没有release或者self.transientData=nil时不会释放;在交易中
    • 感谢您的帮助。在这种情况下,我在 dealloc 中编写了我的发布代码,但在 willTurnIntoFault 中,因为该对象由 CoreData 管理。
    【解决方案2】:

    泄漏表明您的变量在该点被实例化,因此这不是泄漏的实际位置,而是它开始的地方。您需要像这样在取消方法中释放瞬态数据

    - (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. 释放局部变量实例

    这是我不在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 设置器将为您调用release 并将指针设置为nil,这相当安全。

    【讨论】:

    • 感谢您的帮助,现在我明白如何使用 ivars 获得更安全的代码了。
    猜你喜欢
    • 2013-11-11
    • 2014-08-13
    • 2023-04-08
    • 1970-01-01
    • 1970-01-01
    • 2014-03-24
    • 2016-06-02
    • 2015-07-06
    • 2014-06-07
    相关资源
    最近更新 更多