上一个坑里,Realm的坑(二),我们使用非受管对象避开必须使用写入交易的问题。但是,每次使用时都要设置属性还是挺麻烦的。我们寻求一种可以一劳永逸的方式。
最初的想法
我最初想到的是利用NSCopying协议,然后利用Object对象的properties
属性给Object实例的属性赋值。如:
extension Object:NSCopying {
public func copy(with zone: NSZone? = nil) -> Any {
let o = Object()
for p in objectSchema.properties {
let value = self.value(forKey: p.name)
switch p.type {
case .linkingObjects:
break
default:
o.setValue(value, forKey: p.name)
}
}
return o
}
}
由于NSCopying的返回值是Any,因此,在使用时需要转换,像这样:
let anotherBar = bar.copy() as! Foo
try! realm.write {
realm.add(anotherBar, update:true)
}
但是这里发生了一个问题。当前版本的Realm中有一个错误,不能在swift中直接创建Object(),程序会崩溃。因此我提交了一个issue。
进阶
既然不能直接使用Object(),我决定使用通用类型的函数,像这样:
func unmanagedCopy<T>(of i:T) -> T where T:Object {
let o = T()
for p in i.objectSchema.properties {
let value = i.value(forKey: p.name)
switch p.type {
case .linkingObjects:
break
default:
o.setValue(value, forKey: p.name)
}
}
return o
}
let anotherBar = unmanagedCopy(of: bar)
try! realm.write {
realm.add(anotherBar, update:true)
}
这么做,带来一个好处,就是我在具体使用的时候不用进行类型转换了。缺点就是这是一个全局函数,也许放到一个struct里会更好一些?
最终的解决方案
在上面提单的issue里,**JadenGeller**给了我两个很好的建议。下面的代码是最终的解决方案。
// MARK: - UnmanagedCopy Protocol
protocol UnmanagedCopy {
func unmanagedCopy() -> Self
}
extension Object:UnmanagedCopy{
func unmanagedCopy() -> Self {
let o = type(of:self).init()
for p in objectSchema.properties {
let value = self.value(forKey: p.name)
switch p.type {
case .linkingObjects:
break
default:
o.setValue(value, forKey: p.name)
}
}
return o
}
}
首先建立一个UnmanagedCopy的协议。虽然直接写这个函数也可以,但是建立协议可以使函数的目的更加明确。
然后在Object对象的扩展里实现了这个协议。这里用let o = type(of:self).init()
避开了不能使用Object()
的问题,变量o
的类型是Self
。函数返回值类型Self
可以确保最终的类型与self
的实际类型相同,这就保证了使用时不必再进行二次转换。具体使用:
let anotherBar = bar.unmanagedCopy()
try! realm.write {
realm.add(anotherBar, update:true)
}
这个函数已经基本够用了,它包含你的实例里所有持久性数据的属性,但是不包括ignore函数里包含的属性public class func ignoredProperties() -> [String]
。所以,如果你有额外的需求,就应该在Object的子类里重写这个函数。如:
class Foo:Object {
dynamic var id:Int = 0
dynamic var name:String = ""
dynamic var age:Int = 8
var temp = "temp value"
override class func ignoredProperties() -> [String] {
return ["age", "temp"]
}
override class func primaryKey() -> String? {
return "id"
}
override func unmanagedCopy() -> Self {
let o = type(of:self).init()
for p in objectSchema.properties {
let value = self.value(forKey: p.name)
switch p.type {
case .linkingObjects:
break
default:
o.setValue(value, forKey: p.name)
}
}
o.age = age
o.temp = temp
return o
}
}
有关Self的更多用法,看Self的用法。