在后台删除数据会导致UITableView中的索引超出范围崩溃

时间:2015-08-28 19:09:34

标签: ios swift realm

代码

var animals: Results<Animal> {
    didSet {
        self.tableView.reloadData()
    }
}

问题

  • 我有UITableView,显示动物列表
  • 我还有一个后台流程,可以定期同步动物列表
  • 显示UITableView的UIViewController通过成员变量跟踪动物。
  • 如果后台进程在查看列表时从Realm DB中删除了最后一个Animal,则animals集合会更新,但UITableView并不知道它已丢失一行如果我滚动到已删除的Animal所在的位置,我会从Realm收到一个bound of bounds异常并且应用程序崩溃。

尝试解决方案

  1. 我尝试将Results<Animal>复制到内存中的[Animal]并将UITableView绑定到该Array,因为Results<Animal>不会自动刷新.reloadData确实(没有办法将其关闭吗?)。这绕过了索引超出范围的异常,除了现在我收到一个访问已删除/无效对象的属性的异常,因为当单元格读取已删除对象的属性时,它必须返回到Realm。不会工作。
  2. 然后我试图听取Realm的通知并相应地RealmTableHandler表。这有效但会导致过多的表重新加载,并增加了确保通知令牌被释放等的开销,并且记住要在应用程序中的所有表视图中执行此操作。
  3. 解决方法

    我最终创建了一个public extension UITableView { public func addToRealmTableHandler() { RealmTableHandler.sharedInstance.addTrackedTable(self) } } /// Listens for Realm Notifications and reloads tracked UITableViews if needed public class RealmTableHandler { public static let sharedInstance = RealmTableHandler() private var notificationToken: NotificationToken! private var tableIdentifier = 0 private lazy var trackedTables = NSMapTable(keyOptions: NSHashTableWeakMemory, valueOptions: NSHashTableWeakMemory) private lazy var trackedDelegates = NSMapTable(keyOptions: NSHashTableWeakMemory, valueOptions: NSHashTableWeakMemory) private lazy var trackedDatasources = NSMapTable(keyOptions: NSHashTableWeakMemory, valueOptions: NSHashTableWeakMemory) private init() { self.startListening() } private func startListening() { self.notificationToken = Realm().addNotificationBlock { (notification, realm) -> Void in self.checkTrackedTables() } } public func addTrackedTable(table: UITableView) { if self.trackedTables.count == 0 { self.tableIdentifier = 0 } if let delegate = table.delegate, datasource = table.dataSource { self.tableIdentifier++ self.trackedTables.setObject(table, forKey: self.tableIdentifier) self.trackedDelegates.setObject(delegate, forKey: self.tableIdentifier) self.trackedDatasources.setObject(datasource, forKey: self.tableIdentifier) } } private func checkTrackedTables() { // Ensure table, delegate, datasource are all available, sometimes the delegate or datasource are deallocated before the table, but the table keeps a reference to them for key in self.trackedTables.keyEnumerator().allObjects { if let table = self.trackedTables.objectForKey(key) as? UITableView, delegate = self.trackedDelegates.objectForKey(key) as? UITableViewDelegate, datasource = self.trackedDatasources.objectForKey(key) as? UITableViewDataSource { // Check if tracked tables need to be reloaded due to Realm data changes let numSectionsInTableView = table.numberOfSections() var numRowsInTableView = 0 let numSectionsInDatasource = datasource.numberOfSectionsInTableView?(table) ?? numSectionsInTableView var numRowsInDatasource = 0 if numSectionsInTableView <= 0 || numSectionsInDatasource <= 0 { return } for i in 0...(numSectionsInTableView - 1) { numRowsInTableView += table.numberOfRowsInSection(i) } for i in 0...(numSectionsInDatasource - 1) { numRowsInDatasource += datasource.tableView(table, numberOfRowsInSection: i) } if numRowsInTableView != numRowsInDatasource { table.reloadData() } } } } } 单例,它基本上是解决方案#2的一个简化版本。

    UITableViews

    然后我将以下代码添加到任何self.tableView.addToRealmTableHandler() ,如果Realm增加或减少行数,我想重新加载。

    UITableViews

    这似乎工作得很好,即使是分段Results<T>(只要分段数据也是.htaccess)。

    是否有更好/更简单的方法来解决这个问题?

    将来是否会支持表格插入/删除?

1 个答案:

答案 0 :(得分:0)

(免责声明:我为Realm工作。)

嗯,听起来你想要一些类似于Core Data的NSFetchedResultsController的东西,你可以在添加/删除对象时获得通用级别但细粒度的通知。

我不确定这是否可以解决您的问题,但实际上有一个开源库,旨在通过名为RBQFetchedResultsControllerhttps://github.com/Roobiq/RBQFetchedResultsController)的Realm来实现。至少,这可以帮助您避免尝试自己创建自定义解决方案。 :)

如果能解决您的问题,请告诉我!