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

27

我有一个Realm对象,它有几个关联关系,请问是否有一个好的代码片段可以通用复制方法,在数据库中创建一个副本。

6个回答

38

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

Swift 3

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

let newUser = User(value: oldUser);

新的用户对象不会被持久化。


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

7

如果对象没有主键,您可以使用以下代码创建一个浅拷贝:

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

1
在我的情况下有一个主键,我将在 Github 上提交一个功能请求。 - mvo
想法:有没有一种方法可以从 Realm 中分离一个对象(及其关联),然后替换对象(及其关联)的主键,然后再添加回去? - mvo
目前 Realm 中没有内置的方法可以实现这一点,但您可以手动进行递归复制。请确保将问题链接发送给我! - segiddins
@segiddins 请查看 https://dev59.com/6V0a5IYBdhLWcg3wmZoX#67887043 - Peter Lapisu

5

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

以下是我一直在使用的其中一种,且在我看来限制较少。

  1. 使你的 Realm Model Object 类符合可编码协议(codable)
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对象进行真正的深度复制,无论嵌套了多少个对象。只需确保您的所有Realm模型类都符合Codable标准。

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


1
使用这种方法时出现了“只有非托管的 Realm 对象才能使用自动 Codable 合成进行编码”的问题。 - XY Li
你能分享更多细节吗?我无法理解你的意思。 - ImShrey
当使用DetachedCopy与托管对象时,Realm会抱怨一个错误,指出只有未托管的Realm对象可以被编码。 - XY Li
@Xinyang,你能分享一段代码片段吗?另外确认一下,你在 Realm 模型中明确地遵循了 Codable 协议,对吧? - ImShrey
除非您还实现了encode(to:)方法,否则这种方法无法工作,这违背了解决方案的初衷。 - undefined

4

我曾经遇到过类似的问题,并找到了一种简单的方法来获取一个 Realm 对象的副本。基本上,您只需要让该对象符合 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()
   }
}

拥有一份副本的好处在于我可以在不需要进入领域写事务的情况下对其进行修改。这在用户正在编辑某些数据但尚未保存或仅仅是改变了想法时非常有用。


1

由于这个问题仍然存在,我发布了我的解决方案,它已经能够工作,但还需要改进。我创建了一个Object类的扩展,有一个duplicate方法,该方法获取一个对象objOut并通过查看自身填充平面属性。当发现非平面属性(即嵌套对象)时,跳过该属性。

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

在我的Realms的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()方法中的嵌套对象。我考虑了递归,但代码复杂度太高了。

这不是最优的方法,但它可以工作,如果我找到一种管理嵌套对象的方法,我会更新这个答案。


好的答案,需要最少的工作来解决我的问题。谢谢! - MischkaTheBear

0

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接