Wo kann der Beobachter für die NS-Benachrichtigung in Swift entfernt werden?

83

Wo soll ich den Beobachter NSNotificationin Swift entfernen , da viewDidUnloadund dealloc()nicht verfügbar sind?

Clement Joseph
quelle
Heutzutage müssen Sie sie nicht mehr manuell entfernen, es sei denn, Sie verwenden den Blockstil.
Fattie

Antworten:

71

Verwenden Sie die folgende Methode, die genauso funktioniert wie dealloc.

deinit {
    // Release all resources
    // perform the deinitialization
}

Ein Deinitializer wird unmittelbar vor der Freigabe einer Klasseninstanz aufgerufen. Sie schreiben Deinitializer mit dem Schlüsselwort deinit, ähnlich wie Intializer mit dem Schlüsselwort init geschrieben werden. Deinitializer sind nur für Klassentypen verfügbar.

Schneller Deinitializer

Kampai
quelle
12
Ab iOS 9 werden Beobachter gemäß einer Antwort unten automatisch für Sie entfernt, es sei denn, Sie verwenden blockbasierte Beobachter.
Crashalot
Die @ Kampai- deinitMethode für ViewControllerA wird nicht aufgerufen, wenn ViewControllerB gedrückt wird.
Anirudha Mahale
@AnirudhaMahale - Nein, da sich ViewControllerA noch im Stapel des Navigationscontrollers befindet. deinitfor ViewControllerA wird nur aufgerufen, wenn es sich nicht im Stapel des Navigationscontrollers befindet. Zum Beispiel: Wechseln zu rootViewController (wenn rootViewController nicht ViewControllerA ist)
Kampai
@Kampai: Dies funktioniert nicht so, als würden Sie einen Beobachter in den View Controller einfügen. Es besteht eine hohe Wahrscheinlichkeit, dass es im Retain-Zyklus gefangen wird und überhaupt nicht callt deinit. Idealer Ort zum Anrufen wärefunc viewDidDisappear(_ animated: Bool)
Bhanu Birani
@BhanuBirani: Kannst du bitte jeden Fall erklären, dass du von "hohen Chancen" sprichst? Nach meiner Erfahrung habe ich keine gesehen.
Kampai
134

Ab iOS 9 (und OS X 10.11) müssen Sie Beobachter nicht mehr selbst entfernen , wenn Sie jedoch keine blockbasierten Beobachter verwenden. Das System erledigt dies für Sie, da es für Beobachter, wo dies möglich ist, auf Nullstellen schwache Referenzen verwendet.

Und wenn Sie blockbasierten Beobachter verwenden, stellen Sie sicher , dass Sie erfassen selbst schwach mit [weak self]in der Schließung der Aufnahmeliste und entfernen Beobachter in deinitVerfahren. Wenn Sie keinen schwachen Verweis auf sich selbst verwenden, wird die deinitMethode (und damit das Entfernen dieses Beobachters) niemals aufgerufen, da das Notification Center auf unbestimmte Zeit einen starken Verweis darauf enthält.

Weitere Informationen finden Sie in den Foundation Release Notes für OS X 10.11 und iOS 9 .

Wenn der Beobachter als Referenz mit schwacher Nullung gespeichert werden kann, speichert der zugrunde liegende Speicher den Beobachter als Referenz mit schwacher Nullung. Alternativ kann das Objekt nicht schwach gespeichert werden (dh es verfügt über einen benutzerdefinierten Aufbewahrungs- / Freigabemechanismus, der die Laufzeit verhindert Wenn das Objekt nicht schwach gespeichert werden kann, wird es als nicht schwache Nullpunktreferenz gespeichert. Dies bedeutet, dass Beobachter sich bei ihrer Freigabemethode nicht abmelden müssen.

Blockbasierte Beobachter über die Methode - [NSNotificationCenter addObserverForName: object: queue: usingBlock] müssen noch abgemeldet werden, wenn sie nicht mehr verwendet werden, da das System immer noch einen starken Verweis auf diese Beobachter enthält.

Nikola Milicevic
quelle
1
Ich bin neugierig, funktioniert das auch bei den Delegierten? Ich habe in iOS8 gesehen, dass Delegierte Speicher belegen und nicht behalten. Ich habe delegate = nilin dealloc()Methode geschrieben. Funktioniert es ab jetzt genauso?
Kampai
1
In der Regel sollten Delegierte als schwache Referenzen deklariert werden, und es sind keine weiteren Arbeiten erforderlich.
Nikola Milicevic
Da Sie ausdrücklich erwähnt haben, dass es bei blockbasierten Beobachtern nicht funktioniert: Können Sie erläutern, warum? Gibt es einen Weg, das zu umgehen? zB [schwaches Selbst]
Philipp Jahoda
62

Sie können drei Methoden verwenden:

  1. nach popViewController, zurück navigationControlleroder dismissViewControllerAnimated:

    deinit {
        print("Remove NotificationCenter Deinit")
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }
    
  2. viewDidDisappear, entfernen, nachdem es bereits der nächste View Controller ist:

    override func viewDidDisappear(animated: Bool) {
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }
    
  3. viewWillDisappear - bevor Sie die nächste Ansicht öffnen:

    override func viewWillDisappear(animated: Bool) {
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }
    

Schnelle 3.0-Syntax:

NotificationCenter.default.removeObserver(self)
Pablo Ruan
quelle
1
Deinit ist hier die beste Option, denke ich.
Glenn Posadas
Ab iOS 9 werden Beobachter laut @Nikola Milicevic automatisch für Sie entfernt, es sei denn, Sie verwenden blockbasierte Beobachter.
Crashalot
Verstößt das Entfernen von Beobachtern beim Verlassen des Controllers gegen den Zweck, Beobachter zu haben? Und funktioniert deinit nur, wenn Sie programmgesteuert von einer Klasse in eine andere wechseln, ohne Storyboards zu verwenden?
Cyril
18

In Swift 4.2 können Sie auf diese Weise Beobachter entfernen

deinit {
    NotificationCenter.default.removeObserver(self, name: Notification.Name.Identifier, object: nil)
}

Richten Sie die addObserver-Benachrichtigung in der viewDidLoad-Klasse ein

override func viewDidLoad() {
    super.viewDidLoad()
    NotificationCenter.default.addObserver(self, selector: #selector(didReceivedItemDetail), name: Notification.Name.Identifier, object: nil)
}
Ashim Dahal
quelle
2
Beachten Sie, dass unter langsamen Netzwerkbedingungen und bestimmten Benutzeraktivitäten, z. B. beim Navigieren während einer Besetztansicht, möglicherweise nicht deinit aufgerufen wird. Ich habe das in Tests gesehen.
GordonW
2
@GordonW Wenn Ihre Deinit-Methode am Ende Ihres View Controller-Lebenszyklus nicht aufgerufen wird, liegt in dieser Klasse ein Speicherproblem vor.
Ashim Dahal
4

Ich möchte auch darauf hinweisen, dass Sie diese Methode verwenden sollten:

func addObserver(_ observer: Any, selector aSelector: Selector, name aName: NSNotification.Name?, object anObject: Any?)

Anstatt

func addObserver(forName name: NSNotification.Name?, object obj: Any?, queue: OperationQueue?, using block: @escaping (Notification) -> Void) -> NSObjectProtocol

Letzterer wird den Beobachter nicht entfernen (ist kürzlich auf dieses Problem gestoßen). Ersteres entfernt den Beobachter, wenn Sie iOS9 verwenden.

Guy Daher
quelle
Wann entfernt der erstere den Beobachter?
Shubham
@ Shubham Check this out
Guy Daher
Ich denke, das liegt daran, dass Sie in der zweiten Methode einen Aufbewahrungszyklus haben und den Beobachter in der deallocMethode nicht manuell entfernt haben .
Nik Kov
2
deinit {
    NotificationCenter.default.removeObserver(self)
}
Pawel Molodkin
quelle
0

Es ist auch gut, wenn Sie Ihren Beobachter hinzufügen viewWillAppear()und entfernenviewWillDisappear()

Mayank Khare
quelle
0

Swift 5

Ich habe eine Chat-Anwendung. Wenn ich also von meinem ChatLogViewController zu einem anderen viewController wechsle und dann zurückkomme, habe ich 1 zusätzlichen Beobachter meiner Tastaturbenachrichtigung. Um dies zu entfernen, entferne ich alle Beobachter, wenn ich meinen viewController ändere oder aus meinem chatLogViewController verschwinde .

override func viewDidDisappear(_ animated: Bool) {    
    super.viewDidDisappear(animated)

    NotificationCenter.default.removeObserver(self)
}
Simran Singh
quelle