Gdzie usunąć obserwatora dla NSNotification w Swift?


83

Gdzie powinienem usunąć obserwatora NSNotificationw Swift, skoro viewDidUnloadi dealloc()są niedostępne?


obecnie nie musisz ich ręcznie usuwać, chyba że używasz stylu blokowego.
Fattie

Odpowiedzi:


71

Użyj poniższej metody, która działa tak samo jak dealloc.

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

Deinitializer jest wywoływany bezpośrednio przed cofnięciem przydziału instancji klasy. Piszesz deinicjalizatory za pomocą słowa kluczowego deinit, podobnie jak w przypadku pisania intializatorów za pomocą słowa kluczowego init. Deinicjatory są dostępne tylko dla typów klas.

Swift Deinitializer


13
Od iOS 9, zgodnie z poniższą odpowiedzią, obserwatorzy są automatycznie usuwani, chyba że używasz tych opartych na blokach.
Crashalot

deinitMetoda @Kampai dla ViewControllerA nie zostanie wywołana, gdy będzie push ViewControllerB.
Anirudha Mahale

@AnirudhaMahale - Nie, ponieważ ViewControllerA nadal znajduje się na stosie kontrolera nawigacji. deinitfor ViewControllerA zostanie wywołane tylko wtedy, gdy nie ma go na stosie kontrolera nawigacji. Na przykład: Przełączanie na rootViewController (jeśli rootViewController nie jest ViewControllerA)
Kampai

@Kampai: To nie zadziała, jakbyś dodawał obserwatora w kontrolerze widoku. Istnieje duże prawdopodobieństwo, że zostanie złapany w cyklu zatrzymania i w ogóle nie zadzwoni deinit. Idealne miejsce by zadzwonićfunc viewDidDisappear(_ animated: Bool)
Bhanu Birani

@BhanuBirani: Czy możesz wyjaśnić każdy przypadek, w którym wspomina pan o „dużych szansach”. Cóż, z mojego doświadczenia nie spotkałem się.
Kampai

136

Od iOS 9 (i OS X 10.11) nie musisz usuwać obserwatorów , jeśli jednak nie używasz obserwatorów blokowych. System zrobi to za Ciebie, ponieważ używa słabych zerowo referencji dla obserwatorów, gdzie tylko może.

A jeśli używasz obserwatorów opartych na blokach, upewnij się, że uchwyciłeś siebie słabo za pomocą [weak self]listy przechwytywania zamknięcia i usuń obserwatora w deinitmetodzie. Jeśli nie użyjesz słabego odniesienia do siebie, deinitmetoda (a tym samym usunięcie tego obserwatora) nigdy nie zostanie wywołana, ponieważ Centrum powiadomień będzie utrzymywać silne odniesienie do niego przez czas nieokreślony.

Więcej informacji można znaleźć w informacjach o wydaniu Foundation dla systemu OS X 10.11 i iOS 9 .

Jeśli obserwator może być przechowywany jako odniesienie o słabym zerowaniu, podstawowa pamięć będzie przechowywać obserwatora jako słabe odniesienie zerujące, alternatywnie, jeśli obiekt nie może być przechowywany słabo (tj. Ma niestandardowy mechanizm zatrzymania / zwolnienia, który uniemożliwiłby działanie środowiska wykonawczego przed słabym przechowywaniem obiektu) będzie przechowywać obiekt jako niezbyt słabe odniesienie zerujące. Oznacza to, że obserwatorzy nie muszą wyrejestrowywać się w swojej metodzie zwalniania.

Obserwatorzy blokowi za pośrednictwem metody - [NSNotificationCenter addObserverForName: object: queue: usingBlock] nadal muszą być wyrejestrowani, gdy nie są już w użyciu, ponieważ system nadal posiada silne odniesienie do tych obserwatorów.


1
Jestem ciekawy, czy to działa tak samo dla delegatów? Widziałem w iOS8, delegaci zajmują pamięć i nie zachowują. Kiedyś pisałem delegate = nilw dealloc()metodzie. Czy od teraz działa tak samo?
Kampai

1
Zasadniczo delegatów należy zadeklarować jako słabe referencje i nie jest wymagana żadna inna praca.
Nikola Milicevic

Ponieważ wyraźnie wspomniałeś, że nie działa to dla obserwatorów opartych na blokach: czy możesz wyjaśnić, dlaczego? Czy jest na to sposób? np. [słabe ja]
Philipp Jahoda

62

Możesz użyć trzech metod:

  1. po popViewController, z powrotem navigationControllerlub dismissViewControllerAnimated:

    deinit {
        print("Remove NotificationCenter Deinit")
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }
    
  2. viewDidDisappear, usuń, gdy jest już następnym kontrolerem widoku:

    override func viewDidDisappear(animated: Bool) {
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }
    
  3. viewWillDisappear - przed otwarciem kolejnego widoku:

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

Składnia Swift 3.0:

NotificationCenter.default.removeObserver(self)

1
Deinit to chyba najlepsza opcja.
Glenn Posadas

Od iOS 9, według @Nikola Milicevic, obserwatorzy są automatycznie usuwani, chyba że używasz bloków.
Crashalot

Czy usuwanie obserwatorów po opuszczeniu kontrolera jest sprzeczne z celem posiadania obserwatorów? I czy deinit działa tylko wtedy, gdy programowo przechodzisz z jednej klasy do drugiej bez używania scenorysów?
Cyril

21

W Swift 4.2 jest to jeden ze sposobów usuwania obserwatora

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

skonfigurować powiadomienie addObserver w klasie viewDidLoad

override func viewDidLoad() {
    super.viewDidLoad()
    NotificationCenter.default.addObserver(self, selector: #selector(didReceivedItemDetail), name: Notification.Name.Identifier, object: nil)
}

2
Należy pamiętać, że w przypadku powolnych warunków sieciowych i pewnej aktywności użytkownika, np. Oddalania się podczas zajętego widoku, deinit może nie zostać wywołany. Widziałem to w testach.
Gordon W,

3
@GordonW jeśli metoda deinit nie jest wywoływana na końcu cyklu życia kontrolera widoku, oznacza to, że w tej klasie występuje problem z pamięcią.
Ashim Dahal


4

Chcę również zaznaczyć, że powinieneś użyć tej metody:

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

Zamiast

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

Ten ostatni nie usunie obserwatora (ostatnio napotkałem ten problem). Pierwsza z nich usunie obserwatora, jeśli używasz iOS9.


Kiedy to pierwsze usuwa obserwatora?
Shubham

@Shubham Sprawdź to
Guy Daher

Myślę, że to dlatego, że masz cykl utrzymania w drugiej metodzie i nie usunąłeś obserwatora ręcznie w deallocmetodzie.
Nik Kov


1

Szybki 5

Mam aplikację do czatu, więc za każdym razem, gdy przechodzę z mojego ChatLogViewController do innego ViewController, a następnie wracam, mam 1 dodatkowego obserwatora mojego powiadomienia z klawiatury. Aby to usunąć, usuwam wszystkich obserwatorów, gdy zmieniam viewController lub znikam z mojego chatLogViewController .

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

    NotificationCenter.default.removeObserver(self)
}

0

Dobrze jest również dodać obserwatorów viewWillAppear()i usunąć ichviewWillDisappear()

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.