Преобразование самозапускающихся объектов в 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];
}