Gdzie usunąć obserwatora dla NSNotification w Swift?

83

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

Clement Joseph
źródło
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

Kampai
źródło
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.

Nikola Milicevic
źródło
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)
Pablo Ruan
źródło
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)
}
Ashim Dahal
źródło
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.

Guy Daher
źródło
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
2
deinit {
    NotificationCenter.default.removeObserver(self)
}
Paweł Mołodkin
źródło
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)
}
Simran Singh
źródło
0

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

mayank khare
źródło