Кто владеет NSWindowController, в стандартной практике?
Я ищу дальнейшие разъяснения после просмотра Что несет ответственность за выпуск объектов NSWindowController?
Я пишу простое приложение для управления запасами для своих племянников. У меня есть табличное представление, отображающее содержимое их "библиотеки" и т.д. Чтобы добавить новый элемент в библиотеку, они нажимают кнопку "+". Эта кнопка открывает новое окно с запросом деталей элемента и проверяет ввод, когда они нажимают "OK".
Все это работает отлично. Однако у меня есть вопрос об управлении памятью. Чтобы создать новое окно, я использую следующий код:
- (IBAction)addNewItem:(id)sender {
LibraryItemEditorController *editorController =
[[LibraryItemEditorController alloc]
initWithWindowNibName:@"LibraryItemEditor"];
[editorController showWindow:nil];
// editorController is "leaked" here, it seems.
}
Я не могу освободить (и не авторекламу) editorController
в конце addNewItem:
, потому что ничего не ссылается на editorController
; если я его отпущу, окно сразу исчезнет. Тем не менее, я хочу, чтобы оконный контроллер был освобожден после закрытия его окна. В Apple Руководство по программированию окон, я прочитал следующее:
Если вы хотите закрыть окно, чтобы сделать как оконный, так и оконный контроллер уйти, когда он не является частью документ, ваш подкласс NSWindowController
может наблюдать NSWindowWillCloseNotification
или, как делегат окна, выполните windowWillClose:
и включить следующая строка кода в вашем реализация:
[self autorelease];
Я использовал [self autorelease]
в методе windowWillClose:
контроллера окна. Это работает и не утечки памяти. Однако он просто чувствует себя некрасиво; addNewItem:
выглядит как утечка памяти, и статический анализ тоже так думает. Я знаю, что он действительно заботился о windowDidClose:
, но он просто чувствует себя не так. Кроме того, оконный контроллер теперь освобождает себя, не сохранив при этом себя. Все это противоречит правилам управления памятью, которые я узнал.
Моя другая опция - поставить ivar на родительский контроллер (либо NSWindowController
, либо NSMutableSet
of NSWindowController
s), а затем посмотреть на NSWindowWillCloseNotification
в родительском контроллере и отпустить его в ответ. Это чище, и, вероятно, я это сделаю. Тем не менее, это еще и большая работа, что приводит меня к моим вопросам.
Наблюдает за стандартным способом NSWindowDidCloseNotification
? Каков стандартный способ управления NSWindowControllers
, который создается и уничтожается по требованию? Является ли метод [self autorelease]
традиционно рекомендованным, и только теперь, когда у нас есть статический анализ, это проблема?
Ответы
Ответ 1
Похоже, ваше окно модально, и в этом случае:
[NSApp runModalForWindow:[editorController window]];
[editorController release];
Здесь один шаблон для немодальных окон:
@implementation QLPrefWindowController
+ (id) sharedInstance
{
if (!_sharedInstance)
{
_sharedInstance = [[QLPrefWindowController alloc] init];
}
return _sharedInstance;
}
- (void)windowWillClose:(NSNotification *)notification
{
if ([notification object] == [self window] && self == _sharedInstance)
{
_sharedInstance = nil;
[self release];
}
}
Тогда любой, кто хочет получить доступ или отображает окно, может сделать это с помощью метода класса +sharedInstance
. Если окно уже не видно, оно создается, иначе они получают окно, отображаемое в настоящее время.
Ответ 2
Решение для немодальных ситуаций, описанных выше, неверно, потому что методы класса не могут обращаться к iVars. Я решил эту проблему, создав метод класса (в моем подклассе NSWindowController под названием LPWindowController), который выглядит так:
+ (id) autoreleasingInstanceShowingWindow
{
LPWindowController *obj = [[LPWindowController alloc] initWithWindowNibName:@"myWindow"];
[obj showWindow:[NSApp delegate]];
return obj;
}
Вышеуказанный метод возвращает экземпляр LPWindowController со значением удержания 1. Он также показывает окно контроллера. Это важно, потому что в противном случае нам придется полагаться на вызывающего абонента, чтобы вызвать "showWindow:", чтобы отобразить окно LPWindowController. Если вызывающий абонент когда-либо не смог этого сделать (что, вероятно, было бы ошибкой), контроллер никогда не будет выпущен. Заставляя окно отображать, когда мы выделяем/запускаем контроллер, мы избегаем этой ошибки.
Затем, в IB, мы устанавливаем делегат окна в класс LPWindowController и добавляем его в этот класс:
- (void) windowWillClose:(NSNotification *)notification
{
[self autorelease];
}
И, наконец, когда нам нужно создать и показать наше окно, мы просто используем метод ниже вместо вызова "alloc/initWithWindowNibName:" на LPWindowController.
LPWindowController *cont = [LPWindowController autoreleasingInstanceShowingWindow];
cont = nil;
Вторая строка важна. Во-первых, он устраняет предупреждение о "неиспользованной переменной cont". Во-вторых, он устраняет опасность свисающего указателя. Как только экземпляр LPWindowController освободится, если cont не завершен, он укажет на память мусора.
В любом случае, мой рекомендуемый подход к этой проблеме.