【问题标题】:What to do when object on realm gets invalidated当领域上的对象无效时该怎么办
【发布时间】:2019-09-06 00:09:33
【问题描述】:

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

我知道它为什么会发生,并且我理解是正确的行为,但从那里开始:我应该实施的正确行为是什么?

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

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

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

// 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
}

【问题讨论】:

  • 添加示例代码,以便我们更准确地帮助您。
  • 谢谢,我做到了。

标签: ios swift realm


【解决方案1】:

问题

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

首先我们来看看isInvalidated属性

指示对象是否因为现在无效而无法再访问。

如果对象已从管理它的 Realm 中删除,或者在该 Realm 上调用了 invalidate(),则无法再访问该对象。

这意味着一个对象只有在由一个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 = $0 as? DetachableObject {
                    let detached = detachable.detached() as! Element
                    result.append(detached)
                } else {
                    result.append($0) //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($0)
            }
    
            return Array(result.detached())
        }
    }
    
  2. 不要将events 保留为RLMResults&lt;RLMMyEvent&gt;,而是将其保留为[RLMMyEvent]

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

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

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

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

【讨论】:

  • 我认为这可能是一个很好的通用解决方案。我会尽快实施并通知您。非常感谢!
【解决方案2】:

作为一种解决方案,您可以观察 events 数组中的变化,并相应地更新/删除您的单元格,例如

    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 中。而且您根本不需要担心无效对象,因为即使您尝试访问无效对象,您的应用也不会崩溃。此外,如果您尝试从不同的线程访问对象,您的应用也不会崩溃。

【讨论】:

  • 这是一个非常好的建议。将观察者放置在崩溃有时会崩溃的每个地方可能是最干净的解决方案。实施 Unrealm 有点太晚了,我将不得不更改项目的很大一部分。我想知道为什么 Realm 不提供这种行为作为选项。
  • @SimoneChelo 实际上,切换永远不会晚。您需要的唯一修改是符合 Realmable 协议而不是从 Object 类继承并删除 @objc dynamic 关键字。 Unrealm 也适用于类。
【解决方案3】:

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

当前情景

realmObject.stringArray.append("")

建议的方法

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

【讨论】:

  • 我使用IsInvalidated,但我的问题是How can I safely pull the data from that object again?IsInvalidated 只是避免了崩溃,但随后我必须再次提取数据。
猜你喜欢
  • 1970-01-01
  • 2011-09-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-03
  • 1970-01-01
  • 2014-04-24
相关资源
最近更新 更多