1

NSAppデリゲートに、デリゲート自体で開始され、ウィンドウがクリックされると通知を送信するNSWindowサブクラスであるオブジェクトのオブザーバーを追加します。セレクターもデリゲートにあります。その同じデリゲートクラスから、別のオブジェクトを開始します。このオブジェクトは、開始されると、上記の同じNSWindowサブクラスの別のウィンドウのオブザーバーとして追加され、セレクターもこの新しく開始されたクラスに含まれます。両方の通知が投稿されますが、問題は両方のクラスに投稿されることです...これは正常ですか?一度だけ投稿されることを期待していました。

@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
4

2 に答える 2

2

NSNotificationsは、名前とインスタンスでフィルタリングできます。通知ごとに異なる名前を選択するか、各オブザーバーを監視する特定のオブジェクトインスタンスに登録します。セレクターは、オブザーバーが特定の通知を必要としていると判断した場合に、通知センターに呼び出すメソッドを指示するだけです。

オブザーバーを登録するときは、リッスンするインスタンスをオブジェクトパラメーターとして渡します。そのインスタンスから通知を投稿するときは、selfをオブジェクトとして渡します。

于 2010-04-17T20:09:39.290 に答える
1

私はdrawonwardの答えに対する私のコメントで私が言ったことについて正しいです。

変数はコンテナです。変数は、その中にある値とは異なります。一般に、コードで変数の名前を使用する場合、実際には値を参照しています。と言うときは、変数自体を関数にfoo(bar)渡すのではなく、変数に含まれる値を渡すことになります。barfoobar

ローカル変数は、初期化しない限り、何にも初期化されません。したがって、ローカル変数に割り当てたり、以前に初期化したりせずに、ローカル変数を参照しないでください。ランダムな悪いことがランダムに起こります。

一方、インスタンス変数はに初期化され、何か他のものを入れるまでnil含まれ続けます。nilこれは重要です。なぜなら、インスタンス変数initには何も入れていないtestWindowので、インスタンス変数にはが含まれているからnilです。

次に、と言うことで、通知を監視するオブジェクトとして、addObserver:… selector:… name:… object:testWindowそのデフォルト値、を渡します。nilこれは、任意のオブジェクトの通知を監視することを意味します。

それはあなたが意味したことではありませんが、あなたが意味したことはあなたが書いたものではありません。あなたが意味したのは、テストウィンドウのオブザーバーとして自分自身を追加することです。ただし、まだテストウィンドウを作成しておらず、そのポインターをtestWindow変数に配置していないため、作成したのは、任意のオブジェクトのオブザーバーとして自分自身を追加することです。

通知が発生した場合にのみ、ウィンドウを作成し(誤ってその時点で)、それを変数に割り当てます。これは、観察に影響を与えるには遅すぎます。割り当ては、その時点で変数にあったもの()を渡すことしかできなかったため、観察方法をさかのぼって変更することはありませんnil。変数も、変数の将来の値も渡すことができず、渡すこともできません。

したがって、ウィンドウを作成し、の変数に割り当ててから、通知のオブザーバーとして自分自身を追加する必要がinitあります

コードでウィンドウを作成するには、2つの正しい方法があります。これはそのうちの1つであり、これはもう1つです。initフレームの長方形がないため、プレーンを使用してウィンドウを作成しないでください。

または、さらに良いことに、これらすべてをコードで行う代わりに、IBを使用してウィンドウを作成します。testWindowアウトレットを作成し、で観察を開始する必要がありますawakeFromNib

releaseいずれにせよ、通知メソッドのウィンドウを破壊する(そしてそれによって破壊するか、少なくとも破壊しようとする)ため、もう一方の端にも問題があります。オブジェクトを破棄した後も、オブジェクトの通知を受信し続けることを期待しないでください。releaseそのメッセージとnil割り当てをコード内の別の場所に移動する必要があります。ウィンドウを一時的に非表示にするだけでなく、ウィンドウを本当に使い終えた場所に移動する必要があります。

要約すれば:

  1. そのメッセージを送信する前に、ウィンドウを作成し、そのポインタをのtestWindow変数に割り当てます。initaddObserver:selector:name:object:
  2. ユーザーが表示および非表示にできるウィンドウの場合、ウィンドウの存続期間を表示/非表示の状態とは別にしてください。注文されたウィンドウオブジェクトがあっても問題ありません。注文したらすぐに破壊する必要はありません。とにかく、メモリはもうそれほど不足していません。Macではそうではありません。ウィンドウを本当に使い終わったときだけ、ウィンドウを解放します。おそらく、でウィンドウを解放しますdealloc

(ああ、スタイル/保守性の問題:@"TestNotification"コード全体のようにリテラル文字列を振りかけないでください。どこかにその値を持つ変数を定義し、通知を使用するすべての場所でその変数を使用します。次に、文字列を変更するには、正確に1つの場所で変更し、変数の名前を変更するには、Xcodeのリファクタリングツールを使用できます。)

于 2010-04-19T06:49:21.927 に答える