Преобразование самозапускающихся объектов в ARC

ОК, поэтому Apple принес нам ARC, что здорово. После рефакторинга моего приложения в ARC почти все работает отлично, и теперь намного легче разрабатывать и поддерживать.

Есть только одна проблема, которую я до сих пор не могу понять.

Моя программа управления заданиями показывает разные детализированные предложения, заказы и т.д. в их собственных окнах. Поэтому у меня есть специальный класс, в котором WindowControllers получает выделение и инициируется с помощью initWithWindowNibName, а затем окно отображается с showWindow:

DetailWindowController *proposalWindowController = [[DetailWindowController alloc] initWithWindowNibName:@"ThePorposalWindow"];
[proposalWindowController showWindow:nil];

До ARC экземпляр WindowController сделал выпуск, как показано в documentation:

- (void)windowWillClose:(NSNotification *)notification
{
   [self autorelease];
}

Но теперь с ARC это больше невозможно, а что еще хуже, в моем специальном классе, где WindowController назначается и инициируется, тот же windowController освобождается ARC, потому что нет указателя на windowController.

Моя идея состояла в том, чтобы скопировать windowController в управляемый массив:

[proposalWindowArray addObject:proposalWindowController];
[[proposalWindowArray lastObject] showWindow:nil];

А в окнеКонтроллеры делегировать метод windowWillClose я отправляю уведомление своему специальному классу:

- (void)windowWillClose:(NSNotification *)notification
{
    [[NSNotificationCenter defaultCenter] postNotificationName:@"ProposalWindowWillClose" object:[[self window] windowController] userInfo:nil];
}

В моем специальном классе я слушаю уведомление и удаляю объект из массива:

- (void) proposalWindowWasClosed: (NSNotification *) notification
{
    [proposalWindowArray removeObjectIdenticalTo:[notification object]];
}

Это работает, но я до сих пор не считаю, что это правильный путь.

Есть ли у кого-то такая же проблема или подсказка, чтобы сделать ее лучше?

Ответ 1

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

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

Создайте личное свойство strong. Присвойте ему self. Это создаст цикл сохранения, который будет поддерживать вас до тех пор, пока вы не установите свойство nil.

Ответ 2

Без хаков нет элегантного способа сохранить сохраненный объект, отличный от ссылки на него в каком-либо другом объекте. Например, вы можете сохранить статический NSMutableArray/NSMutableSet, добавить туда свой контроллер и удалить его в windowsWillClose:. Это будет меньше, чем публикация уведомления. Чтобы сделать это многоразовым, создайте синглтон WindowControllerRegistry с массивом, в который вы добавите такие контроллеры, которые будут автоматически прослушивать NSWindowWillCloseNotification, и удаляйте их из своего массива, тем самым освобождая право собственности.

В качестве быстрого обхода вы можете выполнять вызовы retain/autorelease из файла без ARC:

my_retain(self);
my_autorelease(self);

// ArcDisabled.mm
void my_retain(id obj) { [obj retain]; }
void my_autorelease(id obj) { [obj autorelease]; }

Ответ 3

Я думаю, что ваш альтернативный подход должен быть правильным, но я не думаю, что вам нужно второе уведомление. Вы должны уметь:

- (void)windowWillClose:(NSNotification *)notification
{
    [proposalWindowArray removeObjectIdenticalTo:self];
}

Предполагая, что "offerWindowArray" является статическим NSMutableArray.

Ответ 4

У меня была такая же проблема, когда я переключился на ARC. Ваше решение работает, но вы делаете его слишком сложным. Вы можете по существу делать то, что делали раньше, имея сам выпуск окна, когда он закрывается, но совместимым с ARC образом.

Решение состоит в том, чтобы просто создать свойство вашего класса внутри самого класса. Для вашего примера, в DetailWindowController, вы должны добавить следующее свойство:

@property (strong)  DetailWindowController      *theWindowController;

Затем, когда вы создаете окно с указанным выше кодом, добавьте одну строку следующим образом:

DetailWindowController *proposalWindowController = [[DetailWindowController alloc] initWithWindowNibName:@"ThePorposalWindow"];
[preferenceController setTheWindowController:proposalWindowController];
[proposalWindowController showWindow:nil];

Затем, наконец, чтобы ARC выпустило окно, когда оно закрыто, как вы это делали с авторекламой pre-ARC, в классе DetailWindowController просто выполните:

- (void)windowWillClose:(NSNotification *)notification
{
    // Let ARC tear this down and clean it up
    [self setTheWindowController:nil];
}