为什么在 Realm 对象声明中添加一个便利的 init 会弄乱私有值?

Why does adding a convenience init to a Realm object declaration mess with private values?

我创建了一个需要存储枚举值的 Realm 对象。为此,我使用了一种方法 ,它涉及声明一个 String 类型的私有 属性,然后声明另一个 属性 类型的 Enum sets/reads 私有 属性 使用 getter 和 setter。

为了便于参考,这里是代码:

@objcMembers
class PlaylistRealmObject: Object {

    dynamic var id: String = UUID().uuidString
    dynamic var created: Date = Date()
    dynamic var title: String = ""
    private dynamic var revisionTypeRaw: String = RevisionType.noReminder.rawValue
    var revisionType: RevisionType {
        get { return RevisionType(rawValue: revisionTypeRaw)! }
        set { revisionTypeRaw = newValue.rawValue }
    }
    let reminders = List<ReminderRealmObject>()
    let cardsInPlaylist = List<CardRealmObject>()

    override static func primaryKey() -> String? {
        return "id"
    }

}

我注意到,如果我在 class 声明中添加一个方便的初始化(以便更容易地初始化对象),我最终使用的对象的 revisionType 属性将采用默认值在 class 中声明,而不是使用便利 init 传递给 class 的修订类型值。

这里是 class 带有便利 init

的声明
@objcMembers
class PlaylistRealmObject: Object {

    dynamic var id: String = UUID().uuidString
    dynamic var created: Date = Date()
    dynamic var title: String = ""
    private dynamic var revisionTypeRaw: String = RevisionType.noReminder.rawValue
    var revisionType: RevisionType {
        get { return RevisionType(rawValue: revisionTypeRaw)! }
        set { revisionTypeRaw = newValue.rawValue }
    }
    let reminders = List<ReminderRealmObject>()
    let cardsInPlaylist = List<CardRealmObject>()

    convenience init(title: String, revisionType: RevisionType) {
        self.init()
        self.title = title
        self.revisionType = revisionType
    }

    override static func primaryKey() -> String? {
        return "id"
    }

}

而且 - 让事情变得更加复杂 - 如果我简单地从 revisionTypeRaw 属性 中删除单词 'private',一切正常!

我很困惑。 1)为什么加个convenience init会有这个效果? 2) 为什么使 属性 'public' 解决问题?

我已经创建了一个演示 Xcode 项目来说明这个问题,如果有人需要,可以分享它。


更新: 我发现了问题。它与便利初始化无关。根据 Realm 文档,我在 class 的顶部使用 @objcMembershttps://realm.io/docs/swift/latest/#property-attributes

如果删除它并将 @objc 放在 private 关键字前面,一切都会按预期进行。我想接下来的问题是:如何解释这种行为?

这是一个很好的问题,但我认为问题出在代码的其他地方。让我们测试一下。

我创建了一个 TestClass,它有一个 Realm managed publicly visible var, name,以及一个 non-managed public var visibleVar,它由 Realm managed private var 支持, 私有变量。我还为每个问题提供了一个方便的初始化。重要的部分是 privateVar 被设置为字符串 "placeholder" 所以我们需要看看它是否被覆盖了。

class TestClass: Object {
    @objc dynamic var name = ""
    @objc private dynamic var privateVar = "placeholder"

    var visibleVar: String {
        get {
            return self.privateVar
        }
        set {
            self.privateVar = newValue
        }
    }

    convenience init(aName: String, aString: String) {
        self.init()
        self.name = aName
        self.visibleVar = aString
    }
}

然后我们创建两个实例并将它们保存在Realm

let a = TestClass(aName: "some name", aString: "some string")
let b = TestClass(aName: "another name", aString: "another string")

realm.add(a)
realm.add(b)

然后一个按钮操作从 Realm 加载两个对象并打印它们。

let testResults = realm.objects(TestClass.self)

for test in testResults {
    print(test.name, test.visibleVar)
}

和输出:

some name some string
another name another string

所以在这种情况下,"placeholder" 的默认值在创建实例时被正确覆盖。

编辑:

更多信息。

通过使用@objMembers 定义您的整个 class,它会将您的属性公开给 Objective-C,但随后又将它们隐藏起来。这样 属性 就不会暴露给 ObjC。要扭转这种隐藏,你必须明确地说@objc。因此,更好的做法是使用 @objc dynamic 定义每行的托管领域属性。