Освобождение делегирующего объекта в его методе обратного вызова делегата. - PullRequest
17 голосов
/ 31 октября 2010

Я пытаюсь выяснить, какова рекомендуемая практика для следующей ситуации. Некоторые объекты, такие как CLLocationManager или MKReverseGeocoder, асинхронно отправляют свои результаты методу обратного вызова делегата. Можно ли выпустить этот экземпляр CLLocationManager или MKReverseGeocoder (или любой другой класс) в методе обратного вызова? Дело в том, что вам больше не нужен этот объект, поэтому вы говорите ему прекратить отправку обновлений, установите его делегат равным nil и освободите объект.

Псевдокод:

@interface SomeClass <CLLocationManagerDelegate>
...
@end

@implementation SomeClass

...

- (void)someMethod
{
    CLLocationManager* locManager = [[CLLocationManager alloc] init];
    locManager.delegate = self;
    [locManager startUpdatingLocation];
}

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
    // Do something with the location
    // ...

    [manager stopUpdatingLocation];
    manager.delegate = nil;
    [manager release];
}

@end

Мне интересно, считается ли этот шаблон использования всегда нормальным, считается ли он никогда не нормальным или зависит от класса?

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

Я искал и не могу найти окончательного ответа на этот вопрос. У кого-нибудь есть авторитетный источник, который может ответить на этот вопрос?

Ответы [ 4 ]

5 голосов
/ 01 ноября 2010

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

5 голосов
/ 01 ноября 2010

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

Это определенно очень плохая идея для освобождения - таким образом освободить объект от его делегата.Подумайте только о том, как объекты (например, CLLocationManager) вызывают своих делегатов - они просто вызывают их в середине какого-то метода.Когда вызов делегата завершен, выполнение кода возвращается к методу объекта, который уже был освобожден.БАМ!

Давайте на минутку забудем о том, что это плохая идея.Я вижу два варианта, как исправить так легко.Во-первых, autorelease вместо release дает объекту немного больше времени для спама - он, по крайней мере, выживет, возвращаясь от делегата.Этого должно быть достаточно для большинства случаев, по крайней мере, если автор API хорошо выполнил свою работу и инкапсулировал логику за основным классом API (в случае CLLocationManager может ожидаться отключение GPS ...).Второй вариант - отложить выпуск (на ум приходит performSelector:withObject:afterDelay:), но это скорее обходной путь для плохо реализованных API.

Так что, если выпускать это не очень хорошая идея, тогда что?

Ну, что вы на самом деле получаете, выпустив CLLocationManager?Освобождение этих нескольких байтов памяти не спасет ваше приложение от завершения, когда в системе недостаточно памяти.В любом случае, действительно ли вам нужен текущий адрес только один раз?

Я предлагаю вам инкапсулировать задачи, связанные с CLLocationManager, в отдельный класс, возможно, даже в одиночный - этот класс станет его делегатом, и он будетпозаботьтесь о том, чтобы связаться с CLLocationManager и проинформировать ваше приложение о результатах (возможно, отправив NSNotification).CLLocationManager будет освобожден из dealloc этого класса, и никогда в результате обратного вызова делегата.stopUpdatingLocation должно хватить, освобождая несколько байтов памяти - ну, вы можете сделать это, когда ваше приложение переходит в фоновый режим, но пока ваше приложение работает, освобождение этих нескольких байтов не приводит к значительному улучшению потребления памяти.

** Добавление **

Для делегата естественно и правильно иметь право собственности на объект, для которого он выступает в качестве делегата.Но делегат не должен освобождать объект в результате обратного вызова.Однако есть одно исключение, и это обратный вызов, сообщающий, что обработка завершена.В качестве примера для этого можно привести NSURLConnection connectionDidFinishLoading:, в котором говорится в документации «Делегат больше не будет получать сообщений».У вас может быть класс, загружающий группу файлов, каждый из которых имеет свой NSURLConnection (с вашим классом в качестве делегата), выделяя и освобождая их по мере загрузки файлов. Поведение

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

3 голосов
/ 01 ноября 2010

Точно так же, как Михал сказал, что нет абсолютно никакой разумной причины выпустить объект менеджера для экономии памяти. Кроме того, как сказал JeremyP, было бы совершенно неверно по шаблону и дизайну выпускать объект внутри другой функции, которая получает только этот объект. Это противоречит всем правилам.

Однако, что было бы правильно, это просто остановить обновления и установить делегат менеджеров равным нулю, как вы уже делаете. Поэтому единственное, что вам нужно удалить - это строка [менеджер выпуска].

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

1 голос
/ 01 ноября 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...