2010-04-17 1 views
1

В моем делегате NSApp я добавляю наблюдателя объекта, который является подклассом NSWindow, который инициируется в самом делететете и который отправляет уведомление после нажатия на окно. Селектор также находится в делегате. Из этого же класса делегата я инициирую другой объект, который при запуске добавляет себя в качестве наблюдателя для другого окна того же подкласса NSWindow выше, и селектор также входит в этот вновь инициированный класс. Оба уведомления публикуются, но проблема в том, что они публикуются в обоих классах ... Это нормально? Я надеялся, что он только один раз был отправлен.NSNotification для нескольких объектов

@implementation AppController 
- (id)init 
{ 
    if (self = [super init]) 
     [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(toggleTestWindow:) name: @"TestNotification" object: testWindow]; 
    return self; 
} 

- (void)toggleTestWindow: (NSNotification *)aNotification 
{ 
    if (!testWindow) { 
     testWindow = [[MyWindow alloc] init]; 
     [mainWindow addChildWindow: testWindow ordered: NSWindowAbove]; 
    } else { 
     [mainWindow removeChildWindow: testWindow]; 
     [testWindow orderOut: self]; 
     [testWindow release]; 
     testWindow = nil; 
    } 
} 
@end 

ответ

1

Я прав о том, что я сказал в своем комментарии к ответу обратного пути.

Переменные - это контейнеры. Переменная отлична от значения, которое находится в ней. Как правило, когда вы используете имя переменной в своем коде, вы на самом деле ссылаетесь на значение; когда вы говорите foo(bar), вы не передаете переменную bar на функцию foo, вы передаете значение, которое находится в переменной bar.

Локальные переменные не инициализируются ничем, если вы не инициализируете их. Итак, не никогда ссылается на локальную переменную, не присваивая ей или не инициализируя ее ранее. Случайные плохие вещи будут случайным образом случаться.

Переменные экземпляра, с другой стороны, инициализируются до nil и будут содержать nil, пока вы не разместите в них что-то еще. Это важно, потому что все через init вы ничего не поместили в переменную экземпляра testWindow, поэтому она содержит nil.

Затем, указав addObserver:… selector:… name:… object:testWindow, вы передаете это значение по умолчанию, nil, в качестве объекта, для которого вы хотите наблюдать за уведомлениями. Это означает наблюдение за этим уведомлением для любого объекта.

Это не то, что вы имели в виду, но то, что вы имели в виду, не то, что вы написали. Вы хотели добавить себя в качестве наблюдателя для тестового окна. Но вы еще не создали тестовое окно, и вы не указали его указатель на переменную testWindow, так что вы написали, чтобы добавить себя в качестве наблюдателя для любого объекта.

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

Итак, вам нужно создать окно и назначить переменную в init, , затем добавить себя в качестве наблюдателя для уведомления.

Существует два правильных способа создания окна в коде. This - один из них, а this - другой. Не используйте простой init, чтобы создать окно, потому что оно не будет иметь прямоугольник рамки.

Или, еще лучше, вместо того, чтобы делать все это в коде, просто используйте IB, чтобы сделать окно. Вы должны сделать выход testWindow и начать наблюдение в awakeFromNib.

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

В итоге:

  1. Создать окно и назначить его указатель на переменную testWindow в init, перед тем вы посылаете, что addObserver:selector:name:object: сообщение.
  2. Для окна, которое пользователь может отображать и скрывать, сохраняйте время жизни окна отдельно от отображаемого/скрытого состояния. Все в порядке, если у вас заказанный оконный объект; вам не нужно уничтожать его, как только вы его закажете. Во всяком случае, память не так уж и скучна, а не на Mac. Отпустите окно, только когда вы действительно закончите с ним, возможно, только в dealloc.

(О, и стиль/maintanability дело: Не посыпаем буквенные строки, как @"TestNotification" всего кода Определить переменную с этим значением где-то, и использовать эту переменную везде, где вы хотите использовать уведомление Тогда.. , чтобы изменить строку, вы меняете ее ровно в одном месте и переименовываете переменную, вы можете использовать инструмент Refactor Xcode.)

+0

Спасибо за все это. На самом деле я не создаю свое окно таким образом, но я просто не добавил всю часть NSMakeRange(). –

+0

Er, вы имеете в виду 'NSMakeRect'? –

2

NSNotifications можно фильтровать по названию и по отдельности. Либо выберите разные имена для каждого уведомления, либо зарегистрируйте каждого наблюдателя с конкретным экземпляром объекта, который он хочет наблюдать. Селектор просто сообщает центру уведомлений, какой метод вызывать, как только он решил, что наблюдателю требуется конкретное уведомление.

Когда вы регистрируете наблюдателя, передайте экземпляр, который хотите прослушать в качестве параметра объекта. Когда вы отправляете уведомление из этого экземпляра, передайте себя как объект.

+0

Именно это я и делаю (кроме фильтрации по имени). Оба наблюдателя регистрируются двумя разными IBOutlets, которые начинаются с нуля, но когда уведомление отправляется (не обязательно, когда нажимается окно), селектор запускает их и освобождает их позже, когда NSNotification снова публикуется ... I ' m делать это таким образом, чтобы я мог освобождать IBOutlets всякий раз, когда уведомление отправляется, не зная, какое IBOutlet это окно изнутри, но я знаю его из селектора, где я могу его освободить. –

+0

Ян Хендрикс: IBOutlets - это просто переменные. Когда вы говорите 'object: nameOfVariable', вы не передаете переменную, вы передаете указатель объекта, который находится в переменной. Если этот указатель «nil», то он ничем не отличается от «object: nil». Вам нужно иметь окно в этой переменной * до того, как вы передадите его в 'addObserver: selector: name: object:' method. –

+0

Peter Hosey: Вы имеете в виду, что я не должен выделять/инициировать/освобождать IBOutlet своим указателем внутри селектора? –

 Смежные вопросы

  • Нет связанных вопросов^_^