如何轻松复制/复制现有领域对象
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
【讨论】:
以上是关于如何轻松复制/复制现有领域对象的主要内容,如果未能解决你的问题,请参考以下文章