当领域上的对象失效时该怎么办

What to do when object on realm gets invalidated

在我的 iOS 应用程序中,我使用 Realm 库来存储数据。它工作得很好,直到出于某种原因,对象失效。 (具体情况下的原因可能是我有时会在视图或类似场景中可视化的相同数据上执行后台作业)。

我知道为什么会这样,我知道这是正确的行为,但从那以后:我应该实施的正确行为是什么?

我很想再次从 Realm 中提取数据并继续我的工作,但是当一个对象失效时,每个字段都无法访问并且使环境崩溃(所以,我不知道什么是 unique+immutable对象的id)。

如何在不将 id 存储在 ViewController 的情况下再次安全地从该对象中提取当前数据?

这是一些代码。由于应用程序的结构不同,我不得不对其进行大量编辑,但问题仍然存在。假设 table 视图委托是视图并且所有技术细节都在那里。

// A class variable
var events: RLMResults<RLMMyEvent>
// A table view that shows all "MyEvents"
var tableview: UITableView

func initialiseView(_ predicate: NSPredicate) {
  // Get the events with a predicate from Realm
  events = RLMMyEvent.objects(with: predicate)
  tableview.reloadData()
}

// All tableView delegates, and in particular

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
   let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath) as! MyCell

   let event = self.events[UInt(indexPath.row)]

   if !event.isInvalidated {
   } else {

   /***** HERE is the problem. What to do here?
          HOW to get those data again, since I cannot 
          get that event's unique ID?  ****/
   }
   cell.initialize(event)
   return cell
}

您是否尝试过在访问对象之前使用 RealmObject.isInvalidated 变量检查该对象是否仍然有效?使用此密钥不会导致您遇到的崩溃。

当前场景

realmObject.stringArray.append("")

建议的方法

if !realmObject.isInvalidated {
    realmObject.stringArray.append("")
}

问题

据我了解,您想在对象失效时访问对象的属性。如果我错了请纠正我:)

首先让我们看一下isInvalidated 属性

Indicates if the object can no longer be accessed because it is now invalid.

An object can no longer be accessed if the object has been deleted from the Realm that manages it, or if invalidate() is called on that Realm.

这意味着一个对象只有在被Realm管理时才能失效。

我的解决方案

从管理对象的领域中分离对象。如果对象不受任何领域管理,当然它永远不会失效,您可以根据需要访问属性。

怎么做

每当您从 Realm 获取对象时,将其分离(创建一个克隆对象)。

  1. 将以下代码添加到您的项目中。它用于在从 Realm 获取后分离 Result 中的每个对象。我发现 here

    protocol DetachableObject: AnyObject {
        func detached() -> Self
    }
    
    extension Object: DetachableObject {
    
        func detached() -> Self {
            let detached = type(of: self).init()
            for property in objectSchema.properties {
                guard let value = value(forKey: property.name) else { continue }
    
                if property.isArray == true {
                    //Realm List property support
                    let detachable = value as? DetachableObject
                    detached.setValue(detachable?.detached(), forKey: property.name)
                } else if property.type == .object {
                    //Realm Object property support
                    let detachable = value as? DetachableObject
                    detached.setValue(detachable?.detached(), forKey: property.name)
                } else {
                    detached.setValue(value, forKey: property.name)
                }
            }
            return detached
        }
    }
    
    extension List: DetachableObject {
        func detached() -> List<Element> {
            let result = List<Element>()
    
            forEach {
                if let detachable = [=10=] as? DetachableObject {
                    let detached = detachable.detached() as! Element
                    result.append(detached)
                } else {
                    result.append([=10=]) //Primtives are pass by value; don't need to recreate
                }
            }
    
            return result
        }
    
        func toArray() -> [Element] {
            return Array(self.detached())
        }
    }
    
    extension Results {
        func toArray() -> [Element] {
            let result = List<Element>()
    
            forEach {
                result.append([=10=])
            }
    
            return Array(result.detached())
        }
    }
    
  2. 不要将 events 保持为 RLMResults<RLMMyEvent>,而是将其保持为 [RLMMyEvent]

  3. 获取结果后,分离对象并转换为数组

    events = RLMMyEvent.objects(with: predicate).toArray()
    

现在您可以访问属性而不用担心无效对象和崩溃。

请注意,如果 Realm 中的原始对象或它们的值发生更改,分离的对象将不会更新。

作为解决方案,您可以观察 events 数组中的变化,并相应地 update/delete 您的单元格,例如

    eventsObserveToken = events.observe { [weak tableView] changes in
        guard let tableView = tableView else { return }

        switch changes {
        case .initial:
            tableView.reloadData()
        case .update(_, let deletions, let insertions, let updates):
            tableView.applyChanges(deletions: deletions, insertions: insertions, updates: updates)
        case .error: break
        }
    }

还有一个扩展

extension IndexPath {
  static func fromRow(_ row: Int) -> IndexPath {
    return IndexPath(row: row, section: 0)
  }
}

extension UITableView {
  func applyChanges(deletions: [Int], insertions: [Int], updates: [Int]) {
    beginUpdates()
    deleteRows(at: deletions.map(IndexPath.fromRow), with: .automatic)
    insertRows(at: insertions.map(IndexPath.fromRow), with: .automatic)
    reloadRows(at: updates.map(IndexPath.fromRow), with: .automatic)
    endUpdates()
  }
}

而且我相信,当一个对象失效时,它会从结果数组中删除,因此您的应用程序不会崩溃。

或者,您可以使用 Unrealm。 Unrealm 使您能够轻松地将 Swift 原生 类、StructsEnums 轻松存储到 Realm 中。而且您根本不需要担心无效的对象,因为即使您尝试访问无效的对象,您的应用程序也不会崩溃。此外,如果您尝试从不同的线程访问对象,您的应用程序也不会崩溃。