如何实现CharacterAttributes字典变更时自动更新CharacterStats字典值?
实现字典值变更时自动更新关联字典的几种方案
针对你的需求,Swift字典属于引用类型,直接给attributes加didSet只会在替换整个字典实例时触发,修改单个键值对不会生效。下面是几种可行的实现方式:
方案一:封装成模型类/结构体,通过方法修改属性值
把两个字典封装到一个模型里,提供专门的方法修改属性值,每次修改后自动同步更新stats:
enum CharacterAttributes: String, Codable { case strength, constitution, dexterity, intelligence } enum CharacterStats: String, Codable { case maxHealthPoint, maxTacticPoint, maxActionPoint, movementSpeed } struct CharacterModel { private var attributes = [CharacterAttributes: Float]() var stats = [CharacterStats: Float]() // 修改属性的方法,内置更新逻辑 mutating func setAttribute(_ key: CharacterAttributes, to value: Float) { attributes[key] = value updateStats() } // 获取属性值的方法 func getAttribute(_ key: CharacterAttributes) -> Float? { attributes[key] } // 统一处理stats的计算逻辑 private mutating func updateStats() { let constitution = attributes[.constitution] ?? 0 let dexterity = attributes[.dexterity] ?? 0 let intelligence = attributes[.intelligence] ?? 0 stats[.maxHealthPoint] = constitution * 100 stats[.maxTacticPoint] = (dexterity + intelligence) * 1.5 stats[.maxActionPoint] = constitution * 10 stats[.movementSpeed] = dexterity + 70 } } // 使用示例 var character = CharacterModel() character.setAttribute(.constitution, to: 5) print(character.stats[.maxHealthPoint]) // 输出500.0 character.setAttribute(.dexterity, to: 10) print(character.stats[.movementSpeed]) // 输出80.0
这种方式逻辑清晰,把属性修改和stats更新的逻辑绑定在一起,避免外部直接操作字典导致遗漏更新。
方案二:自定义可观察字典子类
如果不想封装成模型,可以自定义字典子类,重写下标方法,在设置值时自动触发更新:
class ObservableAttributeDictionary: [CharacterAttributes: Float] { // 定义值变化时的回调闭包 var onValueChanged: (() -> Void)? override subscript(key: CharacterAttributes) -> Float? { get { super[key] } set { super[key] = newValue onValueChanged?() } } } // 使用示例 var attributes = ObservableAttributeDictionary() var stats = [CharacterStats: Float]() // 设置值变化后的更新逻辑 attributes.onValueChanged = { let constitution = attributes[.constitution] ?? 0 let dexterity = attributes[.dexterity] ?? 0 let intelligence = attributes[.intelligence] ?? 0 stats[.maxHealthPoint] = constitution * 100 stats[.maxTacticPoint] = (dexterity + intelligence) * 1.5 stats[.maxActionPoint] = constitution * 10 stats[.movementSpeed] = dexterity + 70 } attributes[.constitution] = 3 print(stats[.maxHealthPoint]) // 输出300.0 attributes[.dexterity] = 8 print(stats[.movementSpeed]) // 输出78.0
这种方式保留了字典的原生使用习惯,同时通过闭包回调实现自动更新。
方案三:使用Combine框架(响应式场景)
如果你的项目基于SwiftUI或采用响应式编程,可以用Combine的CurrentValueSubject包装属性字典,订阅变化并更新stats:
import Combine enum CharacterAttributes: String, Codable { case strength, constitution, dexterity, intelligence } enum CharacterStats: String, Codable { case maxHealthPoint, maxTacticPoint, maxActionPoint, movementSpeed } var attributesSubject = CurrentValueSubject<[CharacterAttributes: Float], Never>([:]) var stats = [CharacterStats: Float]() // 订阅属性变化事件 var cancellable = attributesSubject.sink { attributes in let constitution = attributes[.constitution] ?? 0 let dexterity = attributes[.dexterity] ?? 0 let intelligence = attributes[.intelligence] ?? 0 stats[.maxHealthPoint] = constitution * 100 stats[.maxTacticPoint] = (dexterity + intelligence) * 1.5 stats[.maxActionPoint] = constitution * 10 stats[.movementSpeed] = dexterity + 70 } // 修改属性值(需手动发送通知,因为字典是引用类型) attributesSubject.value[.constitution] = 4 attributesSubject.send(attributesSubject.value) print(stats[.maxHealthPoint]) // 输出400.0
也可以通过创建新字典实例的方式触发通知:
attributesSubject.value = attributesSubject.value.merging([.dexterity: 6]) { $1 }
总结
- 简单场景优先选方案一,模型封装逻辑清晰易维护;
- 想保留字典原生用法选方案二;
- SwiftUI或响应式项目优先方案三。
内容的提问来源于stack exchange,提问作者Bob
相关产品推荐
相关产品推荐

