我有一个Realm对象,它有几个关联关系,请问是否有一个好的代码片段可以通用复制方法,在数据库中创建一个副本。
在我的情况下,我只想创建一个对象而不是持久化它。所以segiddins
的解决方案对我不起作用。
Swift 3
在swift
中创建用户对象的克隆,只需使用:
let newUser = User(value: oldUser)
;
新的用户对象不会被持久化。
如果对象没有主键,您可以使用以下代码创建一个浅拷贝:
realm.create(ObjectType.self, withValue: existingObject)
截至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
}
}
}
//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标准。
虽然这不是理想的解决方案,但它是最有效的解决方法之一。
encode(to:)
方法,否则这种方法无法工作,这违背了解决方案的初衷。 - undefined我曾经遇到过类似的问题,并找到了一种简单的方法来获取一个 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
}
}
class ViewController: UIViewController {
var original = Original()
override func viewDidLoad() {
super.viewDidLoad()
var myCopy = original.copy()
}
}
拥有一份副本的好处在于我可以在不需要进入领域写事务的情况下对其进行修改。这在用户正在编辑某些数据但尚未保存或仅仅是改变了想法时非常有用。
由于这个问题仍然存在,我发布了我的解决方案,它已经能够工作,但还需要改进。我创建了一个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
}
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
// 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()方法中的嵌套对象。我考虑了递归,但代码复杂度太高了。
这不是最优的方法,但它可以工作,如果我找到一种管理嵌套对象的方法,我会更新这个答案。
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
}
}
newUser
对象是原始对象的 浅拷贝!如果它包含对其他 Realm 对象的嵌套引用,则这些引用将在新对象中被引用 - 不会被复制!这样做的一个非常重要的副作用是,当您尝试访问这些嵌套对象时可能会遇到线程问题。相关链接:https://github.com/realm/realm-cocoa/issues/3381 - m_katsifarakis