在Swift中应删除NSNotification的观察者吗?


Answers:


71

使用以下方法,其功能与相同dealloc

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

在取消释放类实例之前,将调用反初始化器。您使用deinit关键字编写反初始化器,类似于使用init关键字编写初始化器。反初始化器仅在类类型上可用。

迅捷反初始化器


12
从iOS 9开始,根据以下答案,除非您使用基于块的观察者,否则观察者将自动为您删除。
Crashalot

deinit将其推入ViewControllerB时,不会调用ViewControllerA的@Kampai方法。
阿尼鲁莎(Anirudha Mahale)'18年

@AnirudhaMahale-不,因为ViewControllerA仍在导航控制器的堆栈中。deinit仅当ViewControllerA不在导航控制器的堆栈中时,才会调用它。例如:切换到RootViewController的(如果RootViewController的不ViewControllerA)
Kampai

@Kampai:这就像在视图控制器中添加观察者一样,将无法正常工作。它很有可能在保留周期内被捕获并且根本不会调用deinit。理想的通话地点func viewDidDisappear(_ animated: Bool)
Bhanu Birani

@BhanuBirani:您能解释一下您提到的“高机会”的任何情况。好吧,根据我的经验,我什么都没面对。
甘榜

135

iOS 9(和OS X 10.11)开始,如果您不使用基于块的观察器,则无需自己删除观察器。该系统将为您执行此操作,因为它会在可能的情况下为观察者使用弱零引用。

并且,如果您使用的是基于块的观察者,请确保在闭包的捕获列表中使用弱捕获自身[weak self],并在方法中删除观察者deinit。如果您不对自身使用弱引用,deinit则将永远不会调用方法(因此移除了该观察者),因为Notification Center会无限期地对其进行强引用。

可以在OS X v10.11和iOS 9的Foundation Release Notes中找到更多信息。

如果观察者能够存储为弱归零引用,则底层存储会将观察者存储为弱归零引用,或者如果对象不能弱存储(即,它具有自定义的保留/释放机制会阻止运行时) (因为能够弱地存储对象),它将对象存储为非弱的清零参考。这意味着不需要观察者取消其释放方法的注册。

当不再使用时,通过-[NSNotificationCenter addObserverForName:object:queue:usingBlock]方法的基于块的观察者仍然需要取消注册,因为系统仍然对这些观察者有很强的引用。


1
我很好奇,对代表们来说也一样吗?我在iOS8中看到过,代表占用内存而不保留。我以前写delegate = nildealloc()方法。从现在开始运作是否一样?
甘榜

1
作为一般规则,应该将代表声明为弱引用,并且不需要其他工作。
Nikola Milicevic,

既然您特别提到它不适用于基于块的观察者:您能否详细说明原因?有办法解决吗?例如[弱者]
Philipp Jahoda '18

62

您可以使用三种方法:

  1. 之后popViewController,返回navigationControllerdismissViewControllerAnimated

    deinit {
        print("Remove NotificationCenter Deinit")
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }
    
  2. viewDidDisappear,请先删除它,然后再删除它:

    override func viewDidDisappear(animated: Bool) {
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }
    
  3. viewWillDisappear -在打开下一个视图之前:

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

Swift 3.0语法:

NotificationCenter.default.removeObserver(self)

1
我猜deinit是最好的选择。
Glenn Posadas

自@ iOS 9起,根据@Nikola Milicevic,除非您使用基于块的观察者,否则观察者将自动为您删除。
Crashalot

离开控制器时删除观察者是否会破坏拥有观察者的目的?而且,仅当您以编程方式从一个班级转移到另一班而不使用情节提要时,deinit才起作用吗?
西里尔(Cyril)

18

在Swift 4.2中,这是删除观察者的方法之一

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

在viewDidLoad类中设置addObserver通知

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

2
请注意,在缓慢的网络条件下和某些用户活动(即在繁忙的视图操作期间导航离开)时,可能不会调用deinit。我已经在测试中看到了。
GordonW

2
@GordonW如果未在视图控制器生命周期结束时调用deinit方法,则该类中存在内存问题。
Ashim Dahal


4

我还想指出,您应该使用此方法:

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

代替

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

后者不会删除观察者(最近遇到了这个问题)。如果您使用的是iOS9,则前者将删除观察者。


前者何时去除观察者?
Shubham

@Shubham检查一下
Guy Daher

我认为那是因为您在第二种方法中有一个保留周期,并且没有在dealloc方法中手动删除观察者。
Nik Kov


0

如果在其中添加观察者viewWillAppear()并将其删除,也很好viewWillDisappear()


0

迅捷5

我有一个聊天应用程序,所以每当我从ChatLogViewController转到其他viewController然后又回来时,我的键盘通知都会有1个额外的Observer。为了消除这一点,我在更改viewController或从chatLogViewController中删除时删除了所有观察者。

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

    NotificationCenter.default.removeObserver(self)
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.