如何轻松复制/复制现有领域对象

Posted

技术标签:

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

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

【问题讨论】:

【参考方案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 中分离出来,然后替换对象中的主键(及其关系),然后再次添加? 目前没有办法在领域内置,但您可以手动进行递归复制。请务必将我链接到您提交的问题! @segiddins 见***.com/questions/30144657/…【参考方案3】:

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

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

    使您的领域模型对象类符合可编码的要求
class Dog: Object, Codable
    @objc dynamic var breed:String = "JustAnyDog"

    创建这个帮助类
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
       
    

    当您需要 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
        
    
    

【讨论】:

以上是关于如何轻松复制/复制现有领域对象的主要内容,如果未能解决你的问题,请参考以下文章

如何将现有仪表板复制到Appdynamics中的新项目中

如何在现有 Javascript 对象中添加具有值的动态键? [复制]

如何复制和粘贴或复制现有项目?

我可以替换 Python 中对象的现有方法吗? [复制]

如何读取嵌套的 json 对象? [复制]

是否可以在实例初始化时为现有对象包含一个新方法? [复制]