managed与unmanaged
我们都知道,变量都有其生命周期。当存在变量的强引用时,变量会一直存在。比如,有
struct Foo {
var name:String
}
let bar = Foo(name:"John")
bar
在赋值后一直存在。我们始终都可以使用bar.name
来获得name属性的值John
。但是在Realm中一切都变得不一样了。在Realm中,你通过子类继承Object对象来构建你的Model。然后通过Realm.add
来添加这个子类的实例到Realm中。
class Foo:Object {
dynamic var name:String = ""
}
let bar = Foo()
let realm = try! Realm()
try! realm.write {
realm.add(bar)
}
你新建出来的bar
是unmanaged的实例。realm.add(bar)
之后,bar
就是managed的实例了。一旦受管,该实例的用法就不能再那么随心所欲了,之后对它的操作就必须在Realm的交易里完成。如:
let bar = Foo()
bar.name = "Marry" // ok
let realm = try! Realm()
try! realm.write {
realm.add(bar)
bar.name = "Kelly" // ok
}
bar.name = "Jimmy" // *** Terminating app due to uncaught exception 'RLMException', reason: 'Attempting to modify object outside of a write transaction - call beginWriteTransaction on an RLMRealm instance first.'
为什么name被设置成Marry和Kelly的时候没问题,但是一设置成Jimmy就不行了呢?
- 这是因为
bar
的状态发生了改变。在realm.add()
之前,bar
是非受管的状态,此时bar
在Realm数据库中没有记录。它的行为和一般的类的实例类似,因此可以直接设置name
。 - 当
realm.add()
之后,bar
在数据库中有了记录,变成了受管的状态,此时再更改它,就必须在realm的写入交易中完成。就像Kelly
那样。 - 而
Jimmy
的部分,由于没有在写入交易中完成,程序会抛出异常并终止运行。
如果不想添加额外的写入交易,就需要添加新建一个非托管对象,然后在添加它到Realm,如:
let anotherBar = Foo()
anotherBar.name = bar.name
try! realm.write {
realm.add(anotherBar)
}
这样的代码有一点儿问题。anotherBar
和bar
对应的是两个不同的对象。现在数据库中同时存在bar
和anotherBar
对应的对象了。因此我们需要删除bar
。
let anotherBar = Foo()
anotherBar.name = bar.name
try! realm.write {
realm.add(anotherBar)
realm.delete(bar)
}
这很不环保。对于有主键的类的实例,可以使用add(object: Object, update: true)
,比如:
class Foo:Object {
dynamic var id:Int = 0
dynamic var name:String = ""
override class func primaryKey() -> String? {
return "id"
}
}
let bar = Foo()
bar.id = 1
bar.name = "Marry" // ok
let realm = try! Realm()
try! realm.write {
realm.add(bar)
bar.name = "Kelly" // ok
}
let anotherBar = Foo()
anotherBar.id = 1
anotherBar.name = bar.name
try! realm.write {
realm.add(anotherBar, update:true)
}
每次还要重新设置非受管对象还是挺麻烦的,如果能直接生成一个就好了。这就是第三个坑。Realm的坑(三)