开发者

NSURlConnection cancel cause memory leak

开发者 https://www.devze.com 2023-03-22 08:30 出处:网络
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.

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. But if I cancel the download, I have 1Mo memory not release. 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 ?
Because you initialize with : 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]; or simply [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

- (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. 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. This is later released only once therefore you have a leak as the NSMutableData will still be hanging around.

Further down in your code you use the pattern

  1. create instance and assign to local variable
  2. assign instance to ivar
  3. release local variable instance

This is the pattern I would use when I am not in an init method. 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. 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.

0

精彩评论

暂无评论...
验证码 换一张
取 消