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?


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


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.


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)

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)
}

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.


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


0

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


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)
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.