web-dev-qa-db-ja.com

目的C:NSNotificationのオブザーバーを削除する場所

Objective Cクラスがあります。その中で、initメソッドを作成し、NSNotificationを設定しました

//Set up NSNotification
[[NSNotificationCenter defaultCenter] addObserver:self 
                                         selector:@selector(getData)
                                             name:@"Answer Submitted"
                                           object:nil];

このクラスの[[NSNotificationCenter defaultCenter] removeObserver:self]はどこで設定しますか? UIViewControllerの場合、viewDidUnloadメソッドに追加できることを知っています。ObjectiveCクラスを作成したばかりの場合、何をする必要がありますか?

97
Zhen

一般的な答えは、「通知が不要になり次第」です。これは明らかに満足のいく答えではありません。

オブザーバーをきれいに登録解除する最後のチャンスなので、オブザーバーとして使用する予定のクラスのメソッドdeallocに呼び出し[notificationCenter removeObserver: self]を追加することをお勧めします。ただし、これは、通知センターが死んだオブジェクトに通知することによるクラッシュからのみ保護します。オブジェクトがまだ通知を適切に処理できる状態になっていない場合、またはもはや通知を受信しない場合、コードを保護できません。このため...上記を参照してください。

編集(答えは思っていたよりも多くのコメントを描くようだから)ここで言いたいことはすべてです。オブザーバーをオブザーバーから削除するのが最善である時期について一般的なアドバイスをするのは本当に難しい通知センター、それは以下に依存するため:

  • ユースケース(どの通知が観察されますか?いつ送信されますか?)
  • オブザーバーの実装(通知を受信する準備ができたのはいつですか?準備ができなくなったのはいつですか)
  • オブザーバーの意図された寿命(ビューまたはビューコントローラーなど、他のオブジェクトに関連付けられていますか?)
  • ...

だから、私が思いつくことができる最高の一般的なアドバイス:あなたのアプリを保護するために。少なくとも1つの可能性のある障害に対して、removeObserver:ダンスをdeallocで実行します。これが(オブジェクトのライフの)最後のポイントであり、きれいに実行できる場所です。これが意味しないのは、「deallocが呼び出されるまで削除を保留するだけで、すべてが正常になります」です。代わりに、オブザーバーを削除しますオブジェクトが通知を受け取る準備ができなくなった(または必要になった)とすぐに。それがまさに正しい瞬間です。残念ながら、上記の質問への答えがわからないので、その瞬間がいつになるか推測することさえできません。

いつでも安全にオブジェクトを複数回removeObserver:することができます(そして、与えられたオブザーバーでの最初の呼び出し以外はすべてnopsです)。したがって:念のために(もう一度)deallocでそれを実行することを考えてください。しかし何よりもまず、適切なタイミングで実行してください(ユースケースによって決まります)。

111
Dirk

注:これはテスト済みで、100%動作しています

Swift

override func viewWillDisappear(animated: Bool){
    super.viewWillDisappear(animated)

    if self.navigationController!.viewControllers.contains(self) == false  //any other hierarchy compare if it contains self or not
    {
        // the view has been removed from the navigation stack or hierarchy, back is probably the cause
        // this will be slow with a large stack however.

        NSNotificationCenter.defaultCenter().removeObserver(self)
    }
}

PresentedViewController

override func viewWillDisappear(animated: Bool){
    super.viewWillDisappear(animated)

    if self.isBeingDismissed()  //presented view controller
    {
        // remove observer here
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }
}

Objective-C

iOS 6.0 > versionでは、viewWillDisappearメソッドは廃止されるため、viewDidUnloadでオブザーバーを削除することをお勧めします。

 [[NSNotificationCenter defaultCenter] removeObserver:observerObjectHere];

ビューがremove observerから削除されたとき、navigation stack or hierarchyの方が何倍も良いです。

- (void)viewWillDisappear:(BOOL)animated{
 if (![[self.navigationController viewControllers] containsObject: self]) //any other hierarchy compare if it contains self or not
    {
        // the view has been removed from the navigation stack or hierarchy, back is probably the cause
        // this will be slow with a large stack however.

        [[NSNotificationCenter defaultCenter] removeObserver:observerObjectHere];
    }
}

PresentedViewController

- (void)viewWillDisappear:(BOOL)animated{
    if ([self isBeingDismissed] == YES) ///presented view controller
    {
        // remove observer here
        [[NSNotificationCenter defaultCenter] removeObserver:observerObjectHere];
    }
}
38
Paresh Navadiya

IOS 9以降、オブザーバーを削除する必要はなくなりました。

OS X 10.11およびiOS 9.0では、NSNotificationCenterおよびNSDistributedNotificationCenterは、割り当て解除された登録済みオブザーバーに通知を送信しなくなりました。

https://developer.Apple.com/library/mac/releasenotes/Foundation/RN-Foundation/index.html#10_11NotificationCenter

37
Sebastian

オブザーバーがview controllerに追加される場合、viewWillAppearに追加し、viewWillDisappearで削除することを強くお勧めします。

25
RickiG
-(void) dealloc {
      [[NSNotificationCenter defaultCenter] removeObserver:self];
      [super dealloc];
}
18
Legolas

一般に、私はそれをdeallocメソッドに入れました。

8

Swiftでは、deallocが使用できないためdeinitを使用します。

deinit {
    ...
}

Swiftドキュメント:

初期化解除子は、クラスインスタンスの割り当てが解除される直前に呼び出されます。 initializersがinitキーワードで記述される方法と同様に、deinitキーワードでdeinitializersを記述します。非初期化子はクラス型でのみ使用可能です。

通常、インスタンスの割り当てが解除されたときに手動でクリーンアップを実行する必要はありません。ただし、独自のリソースを使用している場合は、追加のクリーンアップを自分で実行する必要がある場合があります。たとえば、ファイルを開いてデータを書き込むカスタムクラスを作成する場合、クラスインスタンスの割り当てを解除する前にファイルを閉じる必要があります。

7

私の意見では、次のコードはARCでは意味がありません:

- (void)dealloc
{
      [[NSNotificationCenter defaultCenter] removeObserver:self];
      [super dealloc];
}

iOS 6では、viewDidUnloadのオブザーバーを削除する意味もありません。これは、廃止されたためです。

要約すると、私は常にviewDidDisappearでそれを行います。ただし、@ Dirkが言ったように、要件にも依存します。

5
kimimaro

*編集:このアドバイスはiOS <= 5に適用されます(そこにviewWillAppearを追加し、viewWillDisappearに削除する必要があります-ただし、何らかの理由でviewDidLoadにオブザーバーを追加した場合はアドバイスが適用されます)

viewDidLoadにオブザーバーを追加した場合、deallocviewDidUnloadの両方でオブザーバーを削除する必要があります。そうしないと、viewDidLoadの後にviewDidUnloadが呼び出されたときに、それを2回追加することになります(これはメモリ警告の後に発生します)。これは、viewDidUnloadが非推奨であり、呼び出されないiOS 6では必要ありません(ビューはもはや自動的にアンロードされないため)。

5
Ehren

私は信頼できる答えを見つけたと思います!上記の答えはあいまいで、矛盾しているように見えるので、私はしなければなりませんでした。クックブックとプログラミングガイドに目を通しました。

最初に、addObserver:viewWillAppear:およびremoveObserver:viewWillDisappear:のスタイルは、子View Controllerに通知を投稿しているため、機能しません(テストしました)。親View Controllerでコードを実行します。このスタイルを使用するのは、同じView Controller内で通知を投稿およびリッスンしている場合のみです。

最も頼りになる答えは、iOSプログラミング:Big Nerd Ranch Guide 4thにあります。 iOSトレーニングセンターがあり、別の料理本を書いているだけではないので、私はBNRを信頼しています。正確であることはおそらく彼らの最大の利益になります。

BNRの例1:addObserver: in init:removeObserver: in dealloc:

BNRの例2:addObserver: in awakeFromNib:removeObserver: in dealloc:

dealloc:でオブザーバーを削除するとき、[super dealloc];を使用しません

これが次の人に役立つことを願っています…

AppleがStoryboardsにほぼ完全に移行したため、上記の説明がすべての状況に適用されるわけではないため、この投稿を更新しています。重要なこと(およびこの投稿を最初に追加した理由)は、viewWillDisappear:が呼び出された場合に注意を払うことです。アプリケーションがバックグラウンドに入ったのは私にとってではありませんでした。

4
Murat Zazi

View Controllerが新しいUIViewを表示するときにもviewWillDisappearが呼び出されることにも注意することが重要です。このデリゲートは、単にView Controllerのメインビューがディスプレイに表示されていないことを示しています。

この場合、UIviewが親View Controllerと通信できるようにするために通知を使用している場合、viewWillDisappearの通知の割り当てを解除するのは不便かもしれません。

解決策として、私は通常、次の2つの方法のいずれかでオブザーバーを削除します。

- (void)viewWillDisappear:(BOOL)animated {
    NSLog(@"viewController will disappear");
    if ([self isBeingDismissed]) {
        NSLog(@"viewController is being dismissed");
        [[NSNotificationCenter defaultCenter] removeObserver:self name:@"actionCompleted" object:nil];
    }
}

-(void)dealloc {
    NSLog(@"viewController is being deallocated");
    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"actionCompleted" object:nil];
}

同様の理由で、最初に通知を発行するとき、ビューがコントローラーの上に表示されると、viewWillAppearメソッドが起動されるという事実を考慮する必要があります。これにより、同じ通知のコピーが複数生成されます。通知が既にアクティブであるかどうかを確認する方法がないため、通知を追加する前に削除することで問題を回避します。

- (void)viewWillAppear:(BOOL)animated {
    NSLog(@"viewController will appear");
    // Add observers
    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"imageGenerated" object:nil]; // This is added to avoid duplicate notifications when the view is presented again
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receivedImageFromCameraOrPhotolibraryMethodOnListener:) name:@"actionCompleted" object:nil];

}
2
Alex
override func viewDidLoad() {   //add observer
  super.viewDidLoad()
  NotificationCenter.default.addObserver(self, selector:#selector(Yourclassname.method), name: NSNotification.Name(rawValue: "NotificationIdentifier"), object: nil)
}

override func viewWillDisappear(_ animated: Bool) {    //remove observer
    super.viewWillDisappear(true)
    NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: "NotificationIdentifier"), object: nil)
}
0
urvashi bhagat

Swift

通知の使用には2つのケースがあります。-View Controllerが画面上にある場合にのみ必要です。 -ユーザーが現在の画面で別の画面を開いた場合でも、常に必要です。

最初の場合、オブザーバーを追加および削除する正しい場所は次のとおりです。

/// Add observers
///
/// - Parameter animated: the animation flag
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    NotificationCenter.default.addObserver(...)
}

/// Remove observers
///
/// - Parameter animated: the animation flag
override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    NotificationCenter.default.removeObserver(self)
}

2番目の場合の正しい方法は次のとおりです。

/// Add observers
override func viewDidLoad() {
    super.viewDidLoad()
    NotificationCenter.default.addObserver(...)
}

/// Remove observers
///
/// - Parameter animated: the animation flag
override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    if self.isBeingDismissed // remove only when view controller is removed disappear forever
    || !(self.navigationController?.viewControllers.contains(self) ?? true) {
        NotificationCenter.default.removeObserver(self)
    }
}

removeObserverdeinit{ ... }に入れないでください-それは間違いです!

0