观察Realm结果集的变化

bmt*_*033 5 realm ios swift

我正在使用Realm数据库的iOS应用程序上工作。在我的中AppDelegate,我定义了一个Realm结果集,如下所示:

var results: Results<RealmWidget>!
var notificationToken: NotificationToken? = nil

do {
    let realm = try Realm()
    if results == nil {
        results = realm.objects(RealmWidget.self)
    }
} catch {
    print(error.localizedDescription)
}
Run Code Online (Sandbox Code Playgroud)

我观察到此结果集的变化如下:

if notificationToken == nil {
    notificationToken = results.observe { (changes: RealmCollectionChange) in
        switch changes {
        case .update(_, _, let insertions, _):
            if insertions.count > 0 {
                // show badge on tab bar
                // play sound to get the user's attention
            }
        default:
            break
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

每当RealmWidget在数据库中插入新对象时(由于从服务器接收数据而发生),我都希望UITabBar在UI中显示徽章。该代码似乎正常工作。

在我的应用程序的另一部分中,我有一个带有的视图控制器,UITableView用于显示所有RealmWidget对象(按时间戳排序)。在该视图控制器内部,我还观察到了结果更改,因此我知道何时重新加载tableview。该代码如下所示:

var results: Results<RealmWidget>!
var notificationToken: NotificationToken? = nil

class MyViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

  override func viewDidLoad() {
      super.viewDidLoad()

      results = RealmHelper.getRealmWidgets() // helper method that  queries Realm for all of the Widget objects and sorts them by timestamp
      notificationToken = results.observe { [weak self] (changes: RealmCollectionChange) in
          DispatchQueue.main.async {
              self!.tableView.reloadData()
          }
      }
  }
}
Run Code Online (Sandbox Code Playgroud)

直到我添加了代码以便在我的观察区中播放声音为止,此代码似乎仍能正常工作AppDelegate。然后我意识到,每当我从该视图控制器中删除一行时UITableView,我也在听声音播放。这是出乎意料的,因为我应该只将徽章添加到中,UITabBar并在插入但不删除时播放声音。

我在自己定义的观察块中添加了一些调试代码,AppDelegate因此如下所示:

if notificationToken == nil {
    notificationToken = results.observe { (changes: RealmCollectionChange) in
        switch changes {
        case .update(_, let deletions, let insertions, let modifications):
            print("widget deletion count: \(deletions.count)")
            print("widget insertion count: \(insertions.count)")
            print("widget modification count: \(modifications.count)")
            if insertions.count > 0 {
                // show badge on tab bar
                // play sound to get the user's attention
            }
        default:
            break
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

当我运行该应用程序时,切换到带有tableview的视图控制器,然后删除其中的一行(这将导致RealmWidget从数据库中删除相应的对象),我在调试控制台中看到以下输出:

widget deletion count: 2
widget insertion count: 1
widget modification count: 0
Run Code Online (Sandbox Code Playgroud)

我不清楚其中有几件事:

  • 为什么仅删除一个RealmWidget对象时删除计数为2?
  • 当我没有添加/插入任何RealmWidget对象时,为什么插入计数为1?

如果有人能解释这里发生的事情,我当然会感谢您的帮助!

-编辑-

我将相同的调试代码添加到了在视图控制器内部定义的观察块中,并发现了其他奇怪的地方。当出现“问题”时,我会在调试控制台中看到以下输出:

widget deletion count (AppDelegate): 2
widget insertion count (AppDelegate): 1
widget modification count (AppDelegate): 0

widget deletion count (View controller): 1
widget insertion count (View controller): 0
widget modification count (View controller): 0
Run Code Online (Sandbox Code Playgroud)

因此,我的视图控制器中的观察块显示1次删除,但AppDelegate中的观察块显示2次删除和1次插入!奇怪的是,这种情况并非100%发生。如果我UITableView显示10个RealmWidget对象,并且一次删除一个对象(大约75%的时间),那么我会在控制台中看到以上输出。但是在其他25%的时间中,输出正是我期望看到的:

widget deletion count (AppDelegate): 1
widget insertion count (AppDelegate): 0
widget modification count (AppDelegate): 0

widget deletion count (View controller): 1
widget insertion count (View controller): 0
widget modification count (View controller): 0
Run Code Online (Sandbox Code Playgroud)

两个观察区均缺失1个。我现在比以前更加困惑!;-)

-编辑#2-

看来,此行为与以下事实有关:Results<RealmWidget>我的AppDelegate中的和Results<RealmWidget>我的视图控制器中的不能以相同的方式对查询结果进行排序。

在my中AppDelegateresultsRealmWidget领域中所有对象的未排序集合。但是在我的视图控制器中,我用来定义的辅助方法results是Realm 中所有对象的排序集合RealmWidgets

bmt*_*033 3

我通过 GitHub 将这个问题发布到 Realm 并得到以下回复:

是的,删除对象时,未排序的结果会重新排列。如果您不要求明确的排序顺序,则对象只是按照它们恰好存储在磁盘上的顺序列出,这是一个不稳定的顺序,当对象被删除时会发生变化。

目前,我们报告最后的对象移动到已删除对象的位置,因为该对象也被删除,然后插入到新位置。

https://github.com/realm/realm-cocoa/issues/6130