Как обеспечить сохранение контроллера до тех пор, пока он не завершит обработку при преобразовании в ARC? - PullRequest
4 голосов
/ 24 октября 2011

Я использую следующий шаблон в своем приложении и перехожу на ARC. По сути, объект сохраняет экземпляр контроллера и освобождает этот контроллер, когда он получает уведомление по протоколу делегата о завершении. Я не использую iVar / свойство b / c startProcess может вызываться N раз для обработки N вещей.

Пример ниже:

// start a process in a controller
- (void)startProcess
{
    MyController *controller = [[MyController alloc] init];
    // set the delegate, the delegate is defined as (nonatomic, assign)
    controller.delegate = self;
    [controller start];
}

// when the delegate is notified, release the controller
- (void)myControllerDidFinish:(MyController):controller
{
    // do something with results
    [controller release];
}

Когда вышеуказанная реализация преобразована в ARC, контроллер больше не сохраняется после завершения startProcess, поэтому обработка не происходит и сообщение делегата никогда не принимается.

ВОПРОС: При преобразовании моего проекта для использования ARC, как будет изменена вышеописанная реализация для корректной работы без создания iVars в объекте, создающем экземпляр контроллера? В документации Apple есть аналогичный пример перехода на ARC, но он включает использование блоков. Я бы не стал заменять протокол делегата блоками завершения.

EDIT: добавлен комментарий в коде относительно определения делегата

РЕДАКТИРОВАТЬ: разъяснил первый параграф, чтобы объяснить, почему iVar / свойство для хранения контроллера не будет работать

Ответы [ 3 ]

6 голосов
/ 24 октября 2011

Почему бы просто не создать NSMutableArray переменную экземпляра pendingControllers и добавить туда свой контроллер?Поскольку массивы сохраняют свои элементы, ваш код будет выглядеть следующим образом:

// start a process in a controller
- (void)startProcess
{
    MyController *controller = [[MyController alloc] init];
    // set the delegate, the delegate is defined as (nonatomic, assign)
    controller.delegate = self;
    [controller start];

    if (pendingControllers == nil) {
        pendingControllers = [[NSMutableArray alloc] init];
    }
    [pendingControllers addObject:controller];
    [controller release];
}

// when the delegate is notified, release the controller
- (void)myControllerDidFinish:(MyController):controller
{
    // do something with results
    [pendingControllers removeObject:controller];
    if ([pendingControllers count] == 0) {
        // if ARC is enabled, remove the call to -release.
        [pendingControllers release], pendingControllers = nil;
    }
}

Это позволяет избежать проблемы.Блоки завершения являются правильным ответом, и именно они используют Apple в будущем, но пока этот метод будет работать.

3 голосов
/ 24 октября 2011

Обычно ответственность за сохранность самого себя лежит на контроллере. Если ваш контроллер запускает задачу в фоновом потоке, то она должна автоматически сохраняться экземпляром NSThread. Если данные выбираются по сети с использованием NSURLConnection, контроллер следует сохранить как delegate.

Если вы не выполняете такую ​​задачу, вы можете использовать синтетические циклические удержания, чтобы сохранить контроллер во время выполнения задачи. Это можно сделать, создав объект, я назову его ObjectRetainer, у которого просто есть свойство __strong id. Когда контроллер начинает свою задачу, он должен иметь переменную экземпляра __strong ObjectRetainer, для которой устанавливается новый ObjectRetainer, который сохраняет контроллер. Таким образом, контроллер сохраняет ObjectRetainer, который удерживает контроллер, таким образом предотвращая освобождение любого из них.

Когда контроллер завершает свою задачу и вызывает все необходимые методы делегата, он должен установить для переменной ObjectRetainer instance значение nil. Это освободит ObjectRetainer, что, в свою очередь, освободит контроллер.

Интерфейс ObjectRetainer может выглядеть примерно так:

@interface ObjectRetainer : NSObject {
    __strong id object;
}
@property (nonatomic, strong) __strong id object;
@end

Вы должны объявить ivar в заголовке контроллера: __strong ObjectRetainer _retainer. Затем в методе start контроллера:

- (void)start {
    ...
    _retainer = [[ObjectRetainer alloc] init];
    _retainer.object = self;
}

Когда контроллер будет готов, просто установите _retainer на ноль:

- (void)performBackgroundTask {
    ....
    [delegate myControllerDidFinish:self];
    _retainer = nil;
}
0 голосов
/ 05 февраля 2012

проще: сделайте контроллер членом данных ... @synthesized метод сделает волшебство.

...