【问题标题】:How can I easily duplicate/copy an existing realm object如何轻松复制/复制现有领域对象
【发布时间】:2015-07-20 14:22:39
【问题描述】:

我有一个 Realm 对象,它有几个关系,任何人都有一个很好的代码 sn-p 来概括复制方法,以在数据库中创建一个副本。

【问题讨论】:

    标签: swift realm


    【解决方案1】:

    就我而言,我只是想创建一个对象而不是持久化它。所以segiddins 的解决方案对我不起作用。

    斯威夫特 3

    要在swift 中创建用户对象的克隆,只需使用

    let newUser = User(value: oldUser);

    新的用户对象没有被持久化。

    【讨论】:

    • 请注意,生成的newUser 对象是原始对象的浅拷贝!如果它包含对其他 Realm 对象的嵌套引用,这些将在新对象中被引用 - 不复制!这样做的一个非常重要的副作用是,当您尝试访问这些嵌套对象时,您可能会遇到线程问题。也相关:github.com/realm/realm-cocoa/issues/3381
    【解决方案2】:

    您可以使用以下方法创建对象的浅表副本,只要它没有主键:

    realm.create(ObjectType.self, withValue: existingObject)
    

    【讨论】:

    • 在我的情况下,有一个主键,我会在 github 上为它提交一个功能请求。
    • 想法:有没有办法将对象(及其关系)从 Realm 中分离出来,然后替换对象中的主键(及其关系),然后再次添加?
    • 目前没有办法在领域内置,但您可以手动进行递归复制。请务必将我链接到您提交的问题!
    【解决方案3】:

    截至 2020 年 12 月,此问题尚无合适的解决方案。不过我们有很多解决方法。

    这是我一直在使用的,我认为限制较少的一个。

    1. 使您的领域模型对象类符合可编码的要求
    class Dog: Object, Codable{
        @objc dynamic var breed:String = "JustAnyDog"
    }
    
    1. 创建这个帮助类
    class RealmHelper {
        //Used to expose generic 
        static func DetachedCopy<T:Codable>(of object:T) -> T?{
           do{
               let json = try JSONEncoder().encode(object)
               return try JSONDecoder().decode(T.self, from: json)
           }
           catch let error{
               print(error)
               return nil
           }
        }
    }
    
    1. 当您需要 Realm 对象的分离/真正的深层副本时调用此方法,如下所示:
     //Suppose your Realm managed object: let dog:Dog = RealmDBService.shared.getFirstDog()
     guard let detachedDog = RealmHelper.DetachedCopy(of: dog) else{
        print("Could not detach Dog")
        return
     }
    //Change/mutate object properties as you want
     detachedDog.breed = "rottweiler"
    

    如您所见,我们使用 Swift 的 JSONEncoder 和 JSONDecoder,使用 Codable 的强大功能,无论我们的领域对象下有多少嵌套对象,都可以进行真正的深度复制。只需确保所有 Realm 模型类都符合 Codable。

    虽然它不是理想的解决方案,但它是最有效的解决方法之一。

    【讨论】:

    • 这种方法存在“只能使用自动 Codable 合成对非托管领域对象进行编码”的问题。
    • 你能分享更多细节吗?无法理解您的意思。
    • 将 DetachedCopy 与托管对象一起使用时。 Realm 报错说只能对非托管领域对象进行编码。
    • @Xinyang 可以分享一下sn-p的代码吗?也只是确认一下,您在领域模型中明确符合 Codable 协议,对吗?
    【解决方案4】:

    我遇到了类似的问题,并找到了一个简单的解决方法来获取领域对象的副本。基本上你只需要使对象符合 NSCopying 协议,比如:

    import RealmSwift
    import Realm
    import ObjectMapper
    
    class Original: Object, NSCopying{
       dynamic var originalId = 0
       dynamic var firstName = ""
       dynamic var lastName = ""
    
    override static func primaryKey() -> String? {
        return "originalId"
    }
    
    init(originalId: Int, firstName: String, lastName: String){
        super.init()
    
        self.originalId = originalId
        self.firstName = firstName
        self.lastName = lastName
    }
    
    func copy(with zone: NSZone? = nil) -> Any {
        let copy = Original(originalId: originalId, firstName: firstName, lastName: lastName)
    
        return copy
    }
    }
    

    然后你只需在对象上调用“copy()”方法:

    class ViewController: UIViewController {
       var original = Original()
       override func viewDidLoad() {
           super.viewDidLoad()
    
           var myCopy = original.copy()
       }
    }
    

    拥有一个副本的好处是我可以修改它而不必在领域写入事务中。当用户正在编辑一些数据但尚未点击保存或只是改变主意时很有用。

    【讨论】:

      【解决方案5】:

      由于这个问题仍然存在,我发布了我的解决方案,该解决方案有效但仍需要改进。 我创建了一个 Object 类的扩展,它具有此方法副本,它采用对象 objOut 并通过查看 self 来填充平面属性。当找到一个非平面属性(也称为嵌套对象)时,会跳过该属性。

      // Duplicate object with its flat properties
      func duplicate(objOut: Object) -> Object {
      
          // Mirror object type
          let objectType: Mirror = Mirror(reflecting: self);
      
          // Iterate on object properties
          for child in objectType.children {
      
              // Get label
              let label = child.label!
      
              // Handler for flat properties, skip complex objects
              switch String(describing: type(of: child.value)) {
              case "Double", "Int", "Int64", "String":
                  objOut.setValue(self.value(forKey: label)!, forKey: label)
                  break
              default:
                  break
              }
          }
      
          return objOut
      }
      

      在我的领域的 Manager 类中,我有一个方法 copyFromRealm(),我用它来创建我的对象副本。 举一个实际的例子,这是我的 Appointment 类的结构:

      Appointment object
          - flat properties
          - one UpdateInfo object
              - flat properties
          - one AddressLocation object
              - flat properties
              - one Address object
                  - flat properties
              - one Coordinates object
                  - flat properies
          - a list of ExtraInfo
              - each ExtraInfo object
                  - flat properties
      

      这就是我实现 copyFromRealm() 方法的方式:

      // Creates copy out of realm
      func copyFromRealm() -> Appointment {
      
          // Duplicate base object properties
          let cpAppointment = self.duplicate(objOut: Appointment()) as! Appointment
      
          // Duplicate UIU object
          cpAppointment.uiu = self.uiu?.duplicate(objOut: UpdateInfo()) as? UpdateInfo
      
          // Duplicate AddressLocation object
          let cpAddress = self.addressLocation?.address?.duplicate(objOut: Address()) as? Address
          let cpCoordinates = self.addressLocation?.coordinates?.duplicate(objOut: Coordinates()) as? Coordinates
          cpAppointment.addressLocation = self.addressLocation?.duplicate(objOut: AddressLocation()) as? AddressLocation
          cpAppointment.addressLocation?.address = cpAddress
          cpAppointment.addressLocation?.coordinates = cpCoordinates
      
          // Duplicate each ExtraInfo
          for other in self.others {
              cpAppointment.others.append(other.duplicate(objOut: ExtraInfo()) as! ExtraInfo)
          }
      
          return cpAppointment
      }
      

      我无法在我的 duplicate() 方法中找到一种良好且合理的方法来处理嵌套对象。我想过递归,但是代码复杂度提高了太多。

      这不是最佳的,但有效,如果我能找到一种方法来管理嵌套对象,我会更新这个答案。

      【讨论】:

        【解决方案6】:

        斯威夫特 5+

        使用 ID 创建现有 Realm 托管对象的 Realm 托管副本

        extension RLMObject {
            
            func createManagedCopy(withID newID: String) -> RLMObject? {
                
                let realmClass = type(of: self)
                guard let realm = self.realm, let primaryKey = realmClass.primaryKey() else {
                    return nil
                }
                
                let shallowCopy = realmClass.init(value: self)
                shallowCopy.setValue(newID, forKey: primaryKey)
                
                do {
                    realm.beginWriteTransaction()
                    realm.add(shallowCopy)
                    try realm.commitWriteTransaction()
                } catch {
                    return nil
                }
                
                return shallowCopy
                
            }
            
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-11-28
          • 1970-01-01
          • 2012-08-20
          • 1970-01-01
          • 1970-01-01
          • 2014-07-08
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多