NSURlConnection отмены причиной утечки памяти - PullRequest
0 голосов
/ 24 июля 2011

Это действительно странная ошибка.

Я использую код для загрузки файла с NSURLConnection, если загрузка закончится, у меня нет утечки.Но если я отменю загрузку, у меня 1Мо памяти не освободится.Я сделал тест с инструментом, и метод, идентифицированный для создания этой утечки,

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

2 голосов
/ 24 июля 2011

Как объявляется self.transientData свойство?
Поскольку вы инициализируете с помощью: self.transientData = [[NSMutableData alloc] init]; и если для свойства задано сохранение значения, вам нужно будет разблокировать его два раза.

Если это так, для установки свойства используйте: self.transientData = [[[NSMutableData alloc] init] autorelease]; или просто [NSMutableData data];.
Остальное мне кажется нормальным.

1 голос
/ 24 июля 2011

Утечка указывает на то, что ваша переменная создается в этой точке, так что утечка находится не там, где она начинается.Вам необходимо освободить 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. create instance и назначайтелокальной переменной
  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];
}

Преимущество в том, что вы не используете авто-релиз, поэтому вы более четко определяете, когда объект больше не требуется, что предпочтительнее, когда это возможно на устройствах с небольшой памятью..

Еще одно несоответствие, которое может принести пользу, приведя в порядок немного, - это то, как вы выпускаете свои ивары.Вы сделали это взаимозаменяемо в своем коде

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

Я бы использовал последнее в моем коде (не в dealloc) для переменных экземпляра, так как установщик synthesized вызовет для вас releaseи установите указатель на nil, что значительно безопаснее.

...