Удалить NSWindow из массива после закрытия


Я создаю окна программно, а затем добавляю их в массив, когда показываю их вот так.

NSWindow *window = [[NSWindow alloc] initWithContentRect:frame styleMask:NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask backing:NSBackingStoreBuffered defer:NO];
window.delegate = self;
window.releasedWhenClosed = YES;    
[window makeKeyAndOrderFront:NSApp];
[_array addObject:window];

Мне нужно удалить окна из массива, когда они закрыты, но я не могу заставить это работать. Если я удаляю окно из моего массива в методе делегата -windowWillClose:, как это, мое приложение аварийно завершает работу с EXC_BAD_ACCESS после завершения метода делегата.

- (void)windowWillClose:(NSNotification *)notification
{
    NSWindow *window = notification.object;
    [_array removeObject:window];
}
3 3

3 ответа:

Задайте свойству окна releasedWhenClosed значение false, а не true.

Установка этому свойству значения true, по существу, приводит к дополнительному освобождению, выходящему за рамки обычного управления памятью. Если вы используете ARC, то это один релиз слишком много, потому что сама ARC будет делать сбалансированные сохранения и релизы. (Если вы не используете ARC, то этот релиз будет уместен для балансировки +alloc в вашем первом фрагменте кода, который в противном случае не сбалансирован. Однако даже в этом случае я бы рекомендовал написание кода, который выполняет нормальное, сбалансированное управление памятью, и установка этому свойству значения false.)

Во-вторых, перед удалением окна из массива задайте для свойства delegate значение nil.

Наконец, если это не исправит проблему, покажите детали аварии, включая трассировку стека.

Создание NSWindowController для NSWindow и хранение этих контроллеров в массиве (NSMutableArray<NSWindowController *>) вместо windows решает проблему.

NSWindow *window = [[NSWindow alloc] initWithContentRect:frame styleMask:NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask backing:NSBackingStoreBuffered defer:NO];
window.delegate = self; 
NSWindowController *windowController = [[NSWindowController alloc] initWithWindow:window];
[windowController showWindow:self];
[_array addObject:windowController];

NSWindowDelegate

- (void)windowWillClose:(NSNotification *)notification
{
    NSWindow *window = notification.object;
    [_array removeObject:window.windowController];
}

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