简体   繁体   English

NSURlConnection取消导致内存泄漏

[英]NSURlConnection cancel cause memory leak

It's a really strange bug. 这是一个非常奇怪的错误。

I'm use a code for download a file with NSURLConnection if the download finish , I have no leak. 如果下载完成,我使用代码通过NSURLConnection下载文件,但没有泄漏。 But if I cancel the download, I have 1Mo memory not release. 但是,如果取消下载,则无法释放1Mo内存。 I have make test with instrument, and the methode identified to create this leak is 我已经用仪器进行了测试,发现造成这种泄漏的方法是

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

is really strange 真的很奇怪

this is my code for create, manage and cancel download 这是我用于创建,管理和取消下载的代码

Note: this download is a part of core data managed object, but I think the core data is not responsible of my leak. 注意:此下载是核心数据管理对象的一部分,但我认为核心数据不负责我的泄漏。

- (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];
}

Can you help me for identify the problem ? 您能帮我找出问题所在吗?

Thanks a lot. 非常感谢。

How is self.transientData property declared ? self.transientData属性如何声明?
Because you initialize with : self.transientData = [[NSMutableData alloc] init]; 因为您使用以下self.transientData = [[NSMutableData alloc] init];初始化: self.transientData = [[NSMutableData alloc] init]; and if the property is set to retain the value, you will need to release it two times. 并且如果该属性设置为保留该值,则需要释放两次。

If so, to set the property use : self.transientData = [[[NSMutableData alloc] init] autorelease]; 如果是这样,要设置属性,请使用: self.transientData = [[[NSMutableData alloc] init] autorelease]; or simply [NSMutableData data]; 或只是[NSMutableData data]; .
The rest seems ok to me. 其余的对我来说似乎还可以。

The leak is indicating that your variable is instantiated at that point so that's not where the leak actually is that is where it starts. 泄漏表明您的变量在该点被实例化,因此泄漏实际上不是泄漏所在的位置。 You need to release the transientData in your cancel method like this 您需要像这样在您的cancel方法中释放transientData

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

There are a few issues with inconsistency in your coding style that may make it harder for you to mentally track what is going. 您的编码风格存在一些不一致的问题,可能使您更难以从心理上追踪正在发生的事情。 If you stick to your own standards it should be easier to follow the flow. 如果您坚持自己的标准,那么遵循流程应该会更容易。

This is potentially where another leak could occur 这可能是发生另一个泄漏的地方

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

The [[NSMutableData alloc] init] call is creating an instance of NSMutableData with a retain count of +1. [[NSMutableData alloc] init]调用正在创建保留计数为+1的NSMutableData实例。 Then if you are using properties (which are best) with retain then the self.transientData = will take another retain adding another +1 to the retain count. 然后,如果您使用带有retain属性(最佳),则self.transientData =将进行另一个保留,这将为保留计数增加另一个+1。 This is later released only once therefore you have a leak as the NSMutableData will still be hanging around. 此版本以后仅发布一次,因此您会泄漏,因为NSMutableData仍然会徘徊。

Further down in your code you use the pattern 在代码中进一步使用模式

  1. create instance and assign to local variable 创建实例并分配给局部变量
  2. assign instance to ivar 将实例分配给ivar
  3. release local variable instance 释放局部变量实例

This is the pattern I would use when I am not in an init method. 这是我不在init方法中时将使用的模式。 Therefore the method before should be changed to: 因此,之前的方法应更改为:

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

This has the benefit of not using an autorelease so you are more explicit about when the object is no longer required which is preferred when possible on devices with small memory. 这具有不使用自动释放的优点,因此您可以更明确地确定何时不再需要该对象,如果可能,则在内存较小的设备上首选该对象。

Another inconsistency which might benefit being tidied up a little is how you release your ivars. 整理一下可能会有益的另一个不一致之处是您释放ivars的方式。 You have interchangeably done this in your code 您已经在代码中互换执行了此操作

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

I would use the latter in my code (that is not in dealloc ) for instance variables as the synthesized setter will call release for you and set the pointer to nil which is considerably safer. 我将在我的代码中使用后者(而不是在dealloc )作为实例变量,因为synthesized setter将为您调用release并将指针设置为nil ,这要安全得多。

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

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