You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中ReferenceWritableKeyPath转字符串失败,Release模式返回指针如何解决?

Realm KeyPath转字符串在Release模式下失效的问题与解决过程

场景与问题

我有一个继承自Realm Object的Product类:

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

我尝试实现一个通用的对象更新方法,接收包含KeyPath和对应值的字典,通过Realm的setValue(_:forKey:)方法更新对象:

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

为此我编写了KeyPath的扩展,用来获取它的字符串表示:

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
    }
}

这个实现在Debug模式下完全正常,但切换到Release scheme构建应用时,返回的不是Product.name这类预期值,而是类似\Product.<computed 0x000000010c51ed18 (Bool)>的内容,导致调用setValue时应用直接崩溃。

初衷与尝试

我的核心目标是封装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而非字符串的核心原因是减少错误,避免因属性名称变更导致崩溃,因此将keyedValues参数改为[String: T]并非可行方案。

后来发现Swift的KeyPath并不保证能转换为人类可读的字符串,这条实现路径根本走不通。

最终解决方案

我将更新方法修改为以下形式:

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

现在直接在updateBlock内部完成对象属性的更新操作即可。


内容的提问来源于stack exchange,提问作者Alex Sh.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 09:45:21