获取 Swift ReferenceWritableKeyPath 的字符串表示形式失败(返回指针而不是类型)

Getting String representation of Swift ReferenceWritableKeyPath is failed (returns pointer instead of type)

提问人:Alex Sh. 提问时间:9/22/2023 最后编辑:Alex Sh. 更新时间:9/25/2023 访问量:39

问:

我有一个继承自 Realm 的类:ProductObject

@objc class Product: Object {
    @Persisted(primaryKey: true) var id: String = ""
    @Persisted var externalId: String?
    @Persisted var name: String?
}

我尝试编写一个用于对象更新的通用方法,该方法采用带有 keyPaths 和值的字典,并使用以下 Realm 方法更新对象:

func setValue(_ value: Any?, forKey key: String)

这是一个计算变量,用于获取 KeyPath 的 String 表示形式:

fileprivate extension KeyPath {
    var asString: String {
        let wholeString = String(describing: self)
        let dropLeading =  "\\" + String(describing: Root.self) + "."
        let keyPathString = "\(wholeString.dropFirst(dropLeading.count))"
        return keyPathString
    }
}

它在调试模式下可以完美运行,但是当我使用 Release 方案而不是它构建应用程序时,它会返回类似的东西,并且应用程序在尝试为此键路径设置值时崩溃。"Product.name""\Product.<computed 0x000000010c51ed18 (Bool)>

如果有人知道如何解决这个问题,将不胜感激。

更新:这个想法是将 Realm 的使用封装在类中。这是更新 Realm 实体的原始方法:

func updateObjects<DBEntity: Object, KeyPathType: NSObject, T>(
   ofType objectType: DBEntity.Type,
   where query: ((Query<DBEntity>) -> Query<Bool>)? = nil,
   with keyedValues: [KeyPath<KeyPathType, T>: T],
   completion: (Error?) -> Void?
)

使用 KeyPath 而不是简单的 String 的主要动机是最大程度地减少错误并防止因属性名称更改而导致的崩溃。因此,使用 for 参数不是一种选择。[String : T]keyedValues

目前,似乎不能保证 Swift KeyPath 被转换为人类可读的字符串,因此该选项也不可用。

我最终修改了我的更新方法,如下所示:

func updateObjects<DBEntity: Object>(
    ofType objectType: DBEntity.Type,
    where query: ((Query<DBEntity>) -> Query<Bool>)? = nil,
    updateBlock: @escaping ([DBEntity]) -> Void,
    completion: DBOperationCompletion?
)

现在,我更新了 .updateBlock

领域 swift-keypath

评论

1赞 Alexander 9/22/2023
String(describing:)并且绝对不是这项工作的正确工具。如您所见,编译器在不需要属性名称的情况下会优化属性名称。哎呀,它甚至可以完全优化属性的存在(即,具有常量值的存储属性可以内联,许多存储的属性可以内联,就好像它们是任何其他函数一样)。 没有提供用于获取字符串名称的 API,这是有意为之的,因为这将是所有这些优化的巨大障碍。String(reflecting:)KeyPath
0赞 Alex Sh. 9/22/2023
谢谢你的评论。看来我必须重新考虑存储库的更新方法,而不使用 swift 键路径。
0赞 Alexander 9/23/2023
事实上,如果 String 是你的真正意思,请使用String
1赞 Jay 9/23/2023
不知道为什么要这样做。为什么不直接在对象上设置属性,而不是调用函数来执行此操作呢?Realm 对象还可以像这样通过键值对创建或更新插入多个属性。也许如果用例更清晰,我们也许能够提供更明确的答案。你能澄清一下这个问题吗?someProduct.name = "soap"let p = Product(value: ["id": "1", "name": "soap"])
0赞 Alex Sh. 9/25/2023
我更新了帖子,添加了用例。这个想法是封装更新的领域对象,但使其不会因属性名称更改而出错。

答: 暂无答案