Swift中如何实现类似Objective-C的setValue:forKey键值赋值功能
Swift中实现类
setValue:forKey:动态属性赋值的方案 Swift中是否能实现和OC同等的键值赋值能力,取决于目标对象的类型,没有对所有Swift类型通用的全局键值赋值API,具体方案对应不同场景:
场景1:目标对象继承自NSObject
包括你用到的NSManagedObject子类、所有显式继承NSObject且对应属性暴露给OC运行时的Swift类型,可以直接调用原生KVC方法,和OC的setValue:forKey:行为完全一致:
// 调用方式和OC无差异 target.setValue(propertyValue, forKey: "propertyName")
注意前提:需要赋值的属性必须标记@objc,否则OC运行时找不到对应属性,会触发运行时崩溃。
场景2:目标对象是纯Swift类型(不继承NSObject)
Swift是静态类型语言,默认不开放运行时动态属性写入能力,原生Mirror只支持读属性、不支持写属性,针对你的CoreData和普通模型同步的业务需求,有三个可落地方案:
- 方案1:普通模型继承NSObject,统一走KVC(改造成本最低,最适配你的场景)
把非托管的普通模型类改为继承NSObject,所有需要同步的属性前加@objc标记,即可和NSManagedObject一样直接调用KVC赋值,你现有的Mirror读取源对象属性名、属性值的逻辑完全不需要改动,就能实现和OC一致的同步体验。
示例代码:
使用时注意提前处理可选值解包、类型匹配,避免给非可选属性传nil、类型不匹配导致的KVC崩溃。// 非托管普通模型定义 class ArticleModel: NSObject { @objc var articleID: Int64 = 0 @objc var title: String = "" @objc var publishTime: Date = Date() } // 通用同步方法 func syncProperties(from source: Any, to target: NSObject) { let mirror = Mirror(reflecting: source) for child in mirror.children { guard let propertyName = child.label else { continue } // 直接调用KVC赋值即可 target.setValue(child.value, forKey: propertyName) } } - 方案2:定义键值赋值协议,静态映射保证安全
如果不想让普通模型依赖OC运行时,可以定义一个通用的键值赋值协议,让所有需要同步的模型(包括NSManagedObject子类、普通Swift模型)遵守该协议,内部显式实现属性名到赋值逻辑的映射,完全规避运行时崩溃问题:
该方案稳定性最高,缺点是每个模型需要手动写属性映射,属性较多时可通过代码生成工具自动生成映射逻辑。protocol KeyValueWriteable { @discardableResult func setValue(_ value: Any?, forKey key: String) -> Bool } // 普通模型实现协议 class ArticleModel: KeyValueWriteable { var articleID: Int64 = 0 var title: String = "" var publishTime: Date = Date() func setValue(_ value: Any?, forKey key: String) -> Bool { switch key { case "articleID": guard let v = value as? Int64 else { return false } articleID = v case "title": guard let v = value as? String else { return false } title = v case "publishTime": guard let v = value as? Date else { return false } publishTime = v default: return false } return true } } // NSManagedObject默认兼容协议 extension NSManagedObject: KeyValueWriteable {} - 方案3:基于Swift元数据的反射库实现动态写入
如果既不想继承NSObject、也不想写手动映射,可以使用基于Swift原生Metadata实现的反射工具库实现动态属性写入,但这类库依赖Swift内部未公开的元数据结构,大版本升级时可能存在兼容性问题,非必要不推荐在核心业务链路使用。
针对你当前CoreData栈内外模型同步的场景,优先选择方案1即可,开发成本最低、稳定性最高,和你之前在OC中使用
setValue:forKey:的体验完全一致。
内容的提问来源于stack exchange,提问作者Kapil Choubisa
相关产品推荐
相关产品推荐

