NSURlConnection取消导致内存泄漏

发布于 2024-11-25 18:55:05 字数 2709 浏览 1 评论 0原文

这真是一个奇怪的错误。

我使用代码通过 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];
}

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

多谢。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

我们只是彼此的过ke 2024-12-02 18:55:05

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

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

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.

陈年往事 2024-12-02 18:55:05

泄漏表明您的变量在该点被实例化,因此这不是泄漏实际发生的地方,而是泄漏开始的地方。您需要像这样在取消方法中释放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 这要安全得多。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文