管理观察者的Objective-C设计模式

12

我的应用程序中有一个封装了通过HTTP与服务器通信的对象。该对象会发送一些HTTP请求以“轮询”服务器是否有变化,例如会话不再有效或者用户有新消息等等。

应用程序的UI对象必须向通信对象注册自己,以通过UI对象实现的协议接收通知。注册是通过类似以下方法完成的:

[communicationObject addObserver: self];

并且将其自身移除:

[communicationObject removeObserver: self];
通信对象将观察者保存在可变数组中。在某些情况下,UI对象是被推入 UINavigationController 中的 UIViewControllers。在这种情况下,当用户返回到父控制器时,由于通信对象的观察者数组保留了它,因此 UI 控制器不会被释放,并且无法通过 dealloc 方法从观察者中删除自己(很明显)。
问题是:这种观察者-通知机制是一种糟糕的设计模式吗?有没有一种方法可以检测到 UI 控制器已经被父控制器释放而不使用 viewWillDisappear 方法?有没有最佳实践来解决这种情况?

我忘了说明内存管理是由ARC完成的。 - Marco Jacovone
基本上听起来不错。虽然我从未尝试过,但你应该能够创建一个带有对观察者对象的weak引用的包装器对象,并将其放入列表中。然后,你可以检测观察者是否已被释放。如果在操作对象时同时释放了对象,可能会有些棘手,因此你需要仔细考虑一下。 - Hot Licks
1个回答

13

最佳实践

如果您正在使用观察者模式,希望视图控制器仅在屏幕上时观察某个值,则最好在viewDidAppear中调用addObserver:,并在viewWillDisappear中调用removeObserver:。这不是糟糕的设计或误用这些方法;实际上,这是标准做法和这些视图控制器方法的非常好的使用。

如果要使视图控制器在从屏幕上移除后继续观察某个值,请确保这确实是您想要的。如果是这样,有一些需要记住的事情:

  • 特别地,请确保您的视图控制器设置为:如果它之前存在并被重新带到屏幕上,则具有相同的状态,就像它是从头开始实例化并立即带到屏幕上一样。一种可行的方法(通常是我在自己的项目中使用的方法)是将所有设置代码放在一个setup方法中,并确保在实例化和呈现时都会调用该方法。
  • 此外,请确保避免在后台进行昂贵的额外计算。通常可以通过依赖于在呈现视图控制器时调用setup方法,而不是保持对象寿命期间的一致状态来完成此操作。
  • 最后,请记住,视图控制器仅在屏幕上时才连接其输出(视图/子视图,通常称为视图层次结构)。当它处于非活动状态但被保留时,这些都是nil。检查其视图层次结构是否准备好的一种好方法是通过使用isViewLoaded属性。

保留与呈现

在这里,重要的是不要混淆视图控制器(或任何对象)被保留在某个地方和它在屏幕上。这些是非常不同的事件,通常并不一致。例如,如果您有一个“父”视图控制器(如UINavigationController)管理一个或多个“子”视图控制器,则可能在同时实例化和保留多个视图控制器,而仅在同一时间呈现单个视图控制器。

更好的选择: NSNotificationCenter

如果您喜欢,处理全局事件的另一个选择是通过NSNotificationCenter,它允许您指定要调用观察者的selector,使通知以匿名方式发布,并允许将任意事件对象(userInfo)与通知事件关联起来。通过这种方式,您的communicationObject将向[NSNotificationCenter defaultCenter]发布通知,您的视图控制器将在defaultCenter上观察通知。您仍然会以类似的方式添加/删除观察者对象,但是您会获得一种集中、更健壮的协调全局事件的方式。


嗨,Ryan,首先感谢你的回答。我不会使用viewDidAppear和viewWillDisappear,因为当UI视图实际消失时,它仍应对通知做出反应,否则当用户返回视图时,它就已经过时了...有什么建议吗? - Marco Jacovone
@MarcoJacovone:我已经编辑了我的答案,包括如何处理类似你的情况的详细信息。 - Ryan Artecona
1
非常感谢大家。最终采用了Ryan的第一种策略:UI对象在屏幕上展示时接收通知,当它消失时注销自己。当它再次出现时,“viewWillAppear”设置其状态并重新注册到通信对象中。这是一种清晰且有效的理念。感谢Ryan。 - Marco Jacovone
我将考虑在未来的评估中使用NSNotificationCenter。 - Marco Jacovone

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接