【发布时间】:2015-07-20 14:22:39
【问题描述】:
我有一个 Realm 对象,它有几个关系,任何人都有一个很好的代码 sn-p 来概括复制方法,以在数据库中创建一个副本。
【问题讨论】:
我有一个 Realm 对象,它有几个关系,任何人都有一个很好的代码 sn-p 来概括复制方法,以在数据库中创建一个副本。
【问题讨论】:
就我而言,我只是想创建一个对象而不是持久化它。所以segiddins 的解决方案对我不起作用。
斯威夫特 3
要在swift 中创建用户对象的克隆,只需使用
let newUser = User(value: oldUser);
新的用户对象没有被持久化。
【讨论】:
newUser 对象是原始对象的浅拷贝!如果它包含对其他 Realm 对象的嵌套引用,这些将在新对象中被引用 - 不复制!这样做的一个非常重要的副作用是,当您尝试访问这些嵌套对象时,您可能会遇到线程问题。也相关:github.com/realm/realm-cocoa/issues/3381
您可以使用以下方法创建对象的浅表副本,只要它没有主键:
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 模型类都符合 Codable。
虽然它不是理想的解决方案,但它是最有效的解决方法之一。
【讨论】:
我遇到了类似的问题,并找到了一个简单的解决方法来获取领域对象的副本。基本上你只需要使对象符合 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()
}
}
拥有一个副本的好处是我可以修改它而不必在领域写入事务中。当用户正在编辑一些数据但尚未点击保存或只是改变主意时很有用。
【讨论】:
由于这个问题仍然存在,我发布了我的解决方案,该解决方案有效但仍需要改进。
我创建了一个 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() 方法中找到一种良好且合理的方法来处理嵌套对象。我想过递归,但是代码复杂度提高了太多。
这不是最佳的,但有效,如果我能找到一种方法来管理嵌套对象,我会更新这个答案。
【讨论】:
斯威夫特 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
}
}
【讨论】: