面试题答案
一键面试可能出现性能问题的场景
- 大量观察者频繁添加移除:如果在一个循环中或者高频率的事件处理中,不断地添加和移除观察者,会导致系统频繁分配和释放内存,增加内存管理的开销。例如在一个
UITableView
的cellForRowAt
方法中,每次创建cell
时添加观察者,cell
被重用时移除观察者,随着tableView
的滚动,这种操作会非常频繁。 - 通知中心内部维护开销:通知中心需要维护观察者的列表,频繁的添加和移除操作会使通知中心花费更多时间来更新这个列表,尤其是在观察者数量较多时,每次更新列表的时间复杂度会增加。
优化方案
- 合并操作:尽量减少添加和移除观察者的频率。可以在合适的生命周期方法中进行添加和移除,例如在视图控制器的
viewDidLoad
中添加观察者,在viewWillDisappear
或deinit
中移除观察者,而不是在频繁调用的方法中操作。 - 使用弱引用:通过使用弱引用的方式来添加观察者,这样可以避免因循环引用导致的内存泄漏问题,并且在对象被释放时,通知中心会自动移除对应的观察者,减少手动移除的操作。
代码示例(使用弱引用优化方案)
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(handleNotification(_:)), name: UIApplication.didEnterBackgroundNotification, object: nil)
}
@objc func handleNotification(_ notification: Notification) {
print("Received didEnterBackgroundNotification")
}
deinit {
NotificationCenter.default.removeObserver(self, name: UIApplication.didEnterBackgroundNotification, object: nil)
}
}
在上述代码中,在 viewDidLoad
方法里添加了一个通知观察者,在 deinit
方法里移除观察者。如果不想手动在 deinit
里移除,可以使用弱引用的方式添加观察者:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let weakSelf = WeakRef(self)
NotificationCenter.default.addObserver(forName: UIApplication.didEnterBackgroundNotification, object: nil, queue: nil) { notification in
weakSelf.value?.handleNotification(notification)
}
}
func handleNotification(_ notification: Notification) {
print("Received didEnterBackgroundNotification")
}
}
class WeakRef<T: AnyObject> {
weak var value: T?
init(_ value: T) {
self.value = value
}
}
这样在 ViewController
对象被释放时,通知中心会自动处理相关的观察者移除,无需手动调用 removeObserver
。