关于Realm通知闭包示例未使用weak/unowned self的疑问
Great question—this is a super common gotcha when working with Realm's observation APIs, so let's break this down clearly.
Why the Official Example Omits weak self
First, let's clarify how Realm's observation system works behind the scenes:
- When you call
collection.observe, it returns aNotificationToken. This token is the sole object keeping the observation alive—Realm itself does not hold a strong reference to your closure. Instead, the token holds a strong reference to the closure, and your class (like aUIViewController) holds a strong reference to the token.
The official example skips weak self for two key reasons:
- Focus on core functionality: Documentation examples prioritize showing how the API works over covering every best practice nuance. The goal is to teach you to set up an observer, not dive into memory management details immediately.
- Implicit lifecycle assumptions: If you manually invalidate the token (e.g., in
viewWillDisappearordeinit), the reference cycle gets broken regardless. But relying on this isn't safe for all real-world scenarios.
When You Must Use weak self (or unowned self)
You need to add a weak/unowned capture list when:
- Your closure references
selfdirectly or indirectly (like accessingself.tableViewin the example) - Your class holds a strong reference to the
NotificationToken
Without this, you create a strong reference cycle:self → token → closure → self
This means your class instance (e.g., a view controller) will never be deallocated, leading to memory leaks.
Production-Ready Example
Here's how to rewrite the code safely to avoid leaks:
token = collection.observe { [weak self] changes in guard let self = self else { // Token was invalidated when self was deallocated—exit early return } switch changes { case .initial: self.tableView.reloadData() case .update(_, let deletions, let insertions, let modifications): // Apply table view updates properly self.tableView.beginUpdates() self.tableView.deleteRows(at: deletions.map { IndexPath(row: $0, section: 0) }, with: .automatic) self.tableView.insertRows(at: insertions.map { IndexPath(row: $0, section: 0) }, with: .automatic) self.tableView.reloadRows(at: modifications.map { IndexPath(row: $0, section: 0) }, with: .automatic) self.tableView.endUpdates() case .error(let error): // Handle the error appropriately print("Realm observation error: \(error.localizedDescription)") } }
Don’t forget to invalidate the token when you no longer need it (e.g., in your view controller’s deinit):
deinit { token?.invalidate() }
内容的提问来源于stack exchange,提问作者Martin
相关产品推荐
相关产品推荐

