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クラスを作成したばかりの場合、何をする必要がありますか?
一般的な答えは、「通知が不要になり次第」です。これは明らかに満足のいく答えではありません。
オブザーバーをきれいに登録解除する最後のチャンスなので、オブザーバーとして使用する予定のクラスのメソッドdealloc
に呼び出し[notificationCenter removeObserver: self]
を追加することをお勧めします。ただし、これは、通知センターが死んだオブジェクトに通知することによるクラッシュからのみ保護します。オブジェクトがまだ通知を適切に処理できる状態になっていない場合、またはもはや通知を受信しない場合、コードを保護できません。このため...上記を参照してください。
編集(答えは思っていたよりも多くのコメントを描くようだから)ここで言いたいことはすべてです。オブザーバーをオブザーバーから削除するのが最善である時期について一般的なアドバイスをするのは本当に難しい通知センター、それは以下に依存するため:
だから、私が思いつくことができる最高の一般的なアドバイス:あなたのアプリを保護するために。少なくとも1つの可能性のある障害に対して、removeObserver:
ダンスをdealloc
で実行します。これが(オブジェクトのライフの)最後のポイントであり、きれいに実行できる場所です。これが意味しないのは、「dealloc
が呼び出されるまで削除を保留するだけで、すべてが正常になります」です。代わりに、オブザーバーを削除しますオブジェクトが通知を受け取る準備ができなくなった(または必要になった)とすぐに。それがまさに正しい瞬間です。残念ながら、上記の質問への答えがわからないので、その瞬間がいつになるか推測することさえできません。
いつでも安全にオブジェクトを複数回removeObserver:
することができます(そして、与えられたオブザーバーでの最初の呼び出し以外はすべてnopsです)。したがって:念のために(もう一度)dealloc
でそれを実行することを考えてください。しかし何よりもまず、適切なタイミングで実行してください(ユースケースによって決まります)。
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)
}
}
override func viewWillDisappear(animated: Bool){
super.viewWillDisappear(animated)
if self.isBeingDismissed() //presented view controller
{
// remove observer here
NSNotificationCenter.defaultCenter().removeObserver(self)
}
}
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];
}
}
- (void)viewWillDisappear:(BOOL)animated{
if ([self isBeingDismissed] == YES) ///presented view controller
{
// remove observer here
[[NSNotificationCenter defaultCenter] removeObserver:observerObjectHere];
}
}
IOS 9以降、オブザーバーを削除する必要はなくなりました。
OS X 10.11およびiOS 9.0では、NSNotificationCenterおよびNSDistributedNotificationCenterは、割り当て解除された登録済みオブザーバーに通知を送信しなくなりました。
オブザーバーがview controllerに追加される場合、viewWillAppear
に追加し、viewWillDisappear
で削除することを強くお勧めします。
-(void) dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
一般に、私はそれをdealloc
メソッドに入れました。
Swiftでは、deallocが使用できないためdeinitを使用します。
deinit {
...
}
Swiftドキュメント:
初期化解除子は、クラスインスタンスの割り当てが解除される直前に呼び出されます。 initializersがinitキーワードで記述される方法と同様に、deinitキーワードでdeinitializersを記述します。非初期化子はクラス型でのみ使用可能です。
通常、インスタンスの割り当てが解除されたときに手動でクリーンアップを実行する必要はありません。ただし、独自のリソースを使用している場合は、追加のクリーンアップを自分で実行する必要がある場合があります。たとえば、ファイルを開いてデータを書き込むカスタムクラスを作成する場合、クラスインスタンスの割り当てを解除する前にファイルを閉じる必要があります。
私の意見では、次のコードはARCでは意味がありません:
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
iOS 6では、viewDidUnload
のオブザーバーを削除する意味もありません。これは、廃止されたためです。
要約すると、私は常にviewDidDisappear
でそれを行います。ただし、@ Dirkが言ったように、要件にも依存します。
*編集:このアドバイスはiOS <= 5に適用されます(そこにviewWillAppear
を追加し、viewWillDisappear
に削除する必要があります-ただし、何らかの理由でviewDidLoad
にオブザーバーを追加した場合はアドバイスが適用されます)
viewDidLoad
にオブザーバーを追加した場合、dealloc
とviewDidUnload
の両方でオブザーバーを削除する必要があります。そうしないと、viewDidLoad
の後にviewDidUnload
が呼び出されたときに、それを2回追加することになります(これはメモリ警告の後に発生します)。これは、viewDidUnload
が非推奨であり、呼び出されないiOS 6では必要ありません(ビューはもはや自動的にアンロードされないため)。
私は信頼できる答えを見つけたと思います!上記の答えはあいまいで、矛盾しているように見えるので、私はしなければなりませんでした。クックブックとプログラミングガイドに目を通しました。
最初に、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:
が呼び出された場合に注意を払うことです。アプリケーションがバックグラウンドに入ったのは私にとってではありませんでした。
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];
}
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)
}
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)
}
}
removeObserver
をdeinit{ ... }
に入れないでください-それは間違いです!