更新Realm对象时出现问题

4

我有两个Realm模型类

class ModelA: Object {
    let id = RealmOptional<Int>()
    dynamic var name: String!
    // some other variables that are also String! type
}

class ModelB: Object {
    let id = RealmOptional<Int>()
    let models = List<ModelA>()
    // other variables
}

我有一些包含模型数据的JSON对象。我创建了一个ModelB实例,然后按照以下方式填充其列表中的ModelA实例:

let json: JSON = ... // get it from somewhere, then use SwiftyJSON
let myModelB = ModelB()
myModelB.id.value = json["id"].object as? Int
// set other properties
let modelsA = json["models"].map { ModelA(value: $0.1.object) }
myModelB.models.appendContentsOf(modelsA)

我在这里使用不同的方法是因为JSON中的属性名与ModelB的属性名不匹配,但对于ModelA来说是可以的。稍后我使用realm.write中的realm.add(objects, update: true),这会导致以下异常:

Terminating app due to uncaught exception 'NSUnknownKeyException',reason: '[valueForUndefinedKey:]:此类未针对键(null)进行键值编码。'

根据文档

如果您的模型类包括主键,则可以使用Realm().add(_:update:)根据其主键值智能更新或添加对象。

因此,ModelAModelB都有primaryKey()函数,我相信应该可以工作,但事实并非如此。
此外,我删除了调用中的update参数,并在添加新对象之前添加了对realm.deleteAll()的调用(在write回调中)。在这种情况下,我会收到以下异常:

Terminating app due to uncaught exception 'RLMException', reason: '无法将主键属性'id'设置为现有值'xxxxxxx'。

另外,如果我尝试浏览调用堆栈,Xcode会崩溃。如果我尝试在调试器中检查任何Realm对象,它也会崩溃。我已经安装了Realm Xcode插件,但没有任何变化。我无法理解出了什么问题,为什么会出现如此奇怪的行为。请问有人能告诉我我的错误在哪里吗?

你能展示一下你用来创建想要添加的领域对象以及模型A本身的代码吗?看起来可能存在问题。 - Kalzem
@BabyAzerty 没问题,我已经编辑了我的问题。谢谢! - Azat
1个回答

3
我从零开始启动项目,基于示例项目。所以我最终完成了:
import UIKit
import RealmSwift

// Dog model
class Dog: Object {
    dynamic var name = ""
    dynamic var age = 0
    dynamic var owner: Person? // Properties can be optional

    override class func primaryKey() -> String? { return "name" }
}

// Person model
class Person: Object {
    dynamic var name = ""
    let dogs = List<Dog>()

    override class func primaryKey() -> String? { return "name" }
}

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?


    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        // Override point for customization after application launch.

        do {
            try NSFileManager.defaultManager().removeItemAtPath(Realm.Configuration.defaultConfiguration.path!)
        } catch {}

        let dogRexJSON: AnyObject = ["name": "Rex", "age" : 20]
        let dogLuckyJSON: AnyObject = ["name": "Lucky", "age" : 25]
        var somePerson = Person(value: ["name" : "Shurik", "dogs" : [dogRexJSON]])


        // Realms are used to group data together
        let realm = try! Realm() // Create realm pointing to default file

        // Save your object
        realm.beginWrite()
        realm.add(somePerson)
        try! realm.commitWrite()

        somePerson = Person(value: ["name" : "Shurik", "dogs" : [dogRexJSON, dogLuckyJSON]])
        try! realm.write { () -> Void in
            realm.add([somePerson], update: true)
            return
        }

        let val = realm.objectForPrimaryKey(Dog.self, key: "Lucky")
        print(val!.name) // as expected log >> Lucky

        return true
    }
}

看起来一切都很正常。


原因是使用 RealmOptional<Int> 作为主键,但我将它设置为可选项仅仅是因为 Swift 的限制:我认为在编译时分配 id 并覆盖 init 不是一个好主意,而且 Realm 目前还不支持覆盖 init。但实际值始终非空。谢谢,我已经找到了原因,但仍在寻找更好的主键解决方案。 - Azat
这个问题已经在 Realm 1.1.0 中得到了修复:https://github.com/realm/realm-cocoa/issues/3671 - Flupp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接