EXC_BAD_ACCESS после того, как NSWindowWillCloseNotification удалит окно из массива

Скажем, у меня есть кнопка меню «Создать окно», которая создает новое окно:

MyWindowClass * window = [MyWindowClass new];

Чтобы сохранить его, я добавляю его в изменяемый массив (объявлен и синтезирован как _articleArray = [NSMutableArray new];)

[_articleArray addObject:window]

Это прекрасно работает. Если я включу:

NSLog(@"Windows in mem: %lu",_articleArray.count);

Число увеличивается каждый раз, когда я нажимаю кнопку, и на экране появляется другое окно.

Теперь, если я прикреплю селектор к этой функции «создать окно», чтобы определить, когда окно закрывается:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowClosed:) name:NSWindowWillCloseNotification object:window];

Это создает ошибку:

-(void) windowClosed:(NSNotification*)notification {
    [_articleArray removeObject:[notification object]];
    NSLog(@"Windows in mem: %lu",_articleArray.count);

NSLog уменьшается, когда я закрываю окно, как и ожидалось, но как только функция завершается, выдается ошибка EXC_BAD_ACCESS (код 13, адрес = 0,0)

0x7fff97878710:  movq   24(%rax), %rax

Я очень смущен. Число уменьшается, так что я могу только думать, что функция работает. Так что же здесь происходит?

РЕДАКТИРОВАТЬ: (lldb) обратная трассировка потока

* thread #1: tid = 0x1c07, 0x00007fff97878710 libobjc.A.dylib`objc_msgSend_vtable13 + 16, stop reason = EXC_BAD_ACCESS (code=13, address=0x0)
frame #0: 0x00007fff97878710 libobjc.A.dylib`objc_msgSend_vtable13 + 16
frame #1: 0x00007fff97571503 Foundation`__NSFireTimer + 80
frame #2: 0x00007fff993a6da4 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 20
frame #3: 0x00007fff993a68bd CoreFoundation`__CFRunLoopDoTimer + 557
frame #4: 0x00007fff9938c099 CoreFoundation`__CFRunLoopRun + 1513
frame #5: 0x00007fff9938b6b2 CoreFoundation`CFRunLoopRunSpecific + 290
frame #6: 0x00007fff8df260a4 HIToolbox`RunCurrentEventLoopInMode + 209
frame #7: 0x00007fff8df25e42 HIToolbox`ReceiveNextEventCommon + 356
frame #8: 0x00007fff8df25cd3 HIToolbox`BlockUntilNextEventMatchingListInMode + 62
frame #9: 0x00007fff92ce3613 AppKit`_DPSNextEvent + 685
frame #10: 0x00007fff92ce2ed2 AppKit`-[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 128
frame #11: 0x00007fff92cda283 AppKit`-[NSApplication run] + 517
frame #12: 0x00007fff92c7ecb6 AppKit`NSApplicationMain + 869
frame #13: 0x0000000100006942 myApp`main + 34 at main.m:13
frame #14: 0x00007fff9094f7e1 libdyld.dylib`start + 1

person Dom Vinyard    schedule 17.10.2012    source источник
comment
Как только вы добавили объект окна в массив, вы можете освободить его, так как массив сохранит его. Если вы не используете ARC, вы получите утечку памяти. Также вам нужно удалить наблюдателя из окна в вашем методе уведомления.   -  person trojanfoe    schedule 17.10.2012
comment
@trojanfoe В окне вывода нет трассировки стека, просто (lldb), а затем в главном окне редактора появляется строка EXC_BAD_ACCESS, которую я выделил выше. Я использую ARC, поэтому не решение A, но удаление наблюдателя кажется, что это может быть проблемой - я не могу понять, как на данный момент, буду продолжать попытки, но не могли бы вы мне немного помочь, как я д идти об этом?   -  person Dom Vinyard    schedule 17.10.2012
comment
В консоли отладчика введите thread backtrace (при условии, что lldb), чтобы напечатать трассировку стека, которая будет полезна для продолжения.   -  person trojanfoe    schedule 17.10.2012
comment
@trojanfoe Вау! это действительно полезно (или будет, когда я научусь это понимать!) Я добавил исходный пост с обратной трассировкой. Большое спасибо, что нашли время, чтобы помочь.   -  person Dom Vinyard    schedule 17.10.2012
comment
Хм, след не такой показательный. Судя по внешнему виду, он основан на каком-то событии таймера. Вы сказали, что если убрать уведомление из окна, оно больше не вылетает?   -  person trojanfoe    schedule 17.10.2012
comment
@trojanfoe Исправьте также, если я не удалю элемент из массива: ака, закомментируйте инструкцию: ([_articleArray removeObject:windowToRemove];) он не вылетит. Но он также не удаляет объект из памяти: p   -  person Dom Vinyard    schedule 17.10.2012
comment
Значит проблема решена?   -  person trojanfoe    schedule 17.10.2012
comment
@trojanfoe Наоборот, когда окно закрыто, оно должно быть освобождено из памяти, поэтому я пытаюсь удалить его из массива, который его сохраняет. (Кроме того, я пытаюсь вести подсчет окон, поэтому необходимо поддерживать _articleArray.count). Это очень странное поведение, я буду продолжать попытки. Если вы думаете о чем-то еще, пожалуйста, дайте мне знать.   -  person Dom Vinyard    schedule 17.10.2012
comment
Окно isReleasedWhenClosed == YES?   -  person trojanfoe    schedule 17.10.2012
comment
Это было! Теперь это не так, и это полностью исправлено! Если вы ответите на этот вопрос этим ответом, я отмечу его как правильный. Огромное спасибо!   -  person Dom Vinyard    schedule 17.10.2012


Ответы (1)


Этот ответ был перемещен из комментариев в вопросе по запросу OP

Вам необходимо убедиться, что вы удалили свой NSWindowWillCloseNotification наблюдатель до того, как окно будет уничтожено:

-(void) windowClosed:(NSNotification*)notification {
    NSWindow *window = [notification object];
    [[NotificationCenter defaultCenter] removeObserver:self
                                                  name:NSWindowWillCloseNotification
                                                object:window];
    [_articleArray removeObject:window];
    NSLog(@"Windows in mem: %lu",_articleArray.count);
    ...

А также убедитесь, что для свойства isReleasedWhenClosed окна установлено значение YES, чтобы оно само очищалось при закрытии.

person trojanfoe    schedule 17.10.2012