SwiftUI中如何让两个类共享操作不同变量的相同函数?
解决Swift ObservableObject重复逻辑复用问题
场景说明
我有两个类ItemToEdit和ItemToDisplay,以及两个视图结构体Buttons和Expression。这两个视图分别将ItemToEdit和ItemToDisplay作为EnvironmentObject,而这两个对象来自包含它们的Calculator视图中的@StateObject:
ItemToEdit包含一个@Published变量itemToEdit,Buttons视图通过大量函数多步修改这个变量。Expression是基于itemToDisplay构建的嵌套视图组合。ItemToDisplay的@Published变量itemToDisplay会在Buttons完成所有修改后与itemToEdit同步,避免itemToEdit每次修改都导致Expression频繁更新。
用户提供的代码如下:
struct Item { var operations: [String] var numbers: [String] var lists: [String] } class ItemToEdit: ObservableObject { @Published var itemToEdit: Item = Item(operations: [], numbers: [], lists: []) func edit() { itemToEdit.operations.append("multiply") } } class ItemToDisplay: ObservableObject { @Published var itemToDisplay: Item = Item(operations: [], numbers: [], lists: []) func getNumberIndex() -> Int { itemToDisplay.operations.firstIndex(of: "divide") ?? -1 } } struct Calculator: View { @StateObject var edit: ItemToEdit @StateObject var display: ItemToDisplay var body: some View { VStack { Expression() Buttons() } .environmentObject(edit) .environmentObject(display) } }
问题
ItemToEdit和ItemToDisplay需要一些逻辑完全相同,但分别操作各自的itemToEdit和itemToDisplay变量的函数,这些函数该放在哪里,如何确保操作正确的变量?
解决方案
方案1:提取无状态工具结构体(最简洁)
把通用逻辑抽成独立的静态工具结构体,函数接收Item(或inout Item用于修改)作为参数,两个ObservableObject类调用时传入各自管理的变量即可。
// 通用逻辑统一放在这里 struct ItemLogic { // 只读逻辑:传入Item返回结果 static func getNumberIndex(for item: Item) -> Int { item.operations.firstIndex(of: "divide") ?? -1 } // 修改逻辑:用inout参数修改Item static func addMultiplyOperation(to item: inout Item) { item.operations.append("multiply") } } // 修改ItemToEdit class ItemToEdit: ObservableObject { @Published var itemToEdit: Item = Item(operations: [], numbers: [], lists: []) func edit() { ItemLogic.addMultiplyOperation(to: &itemToEdit) } func getNumberIndex() -> Int { ItemLogic.getNumberIndex(for: itemToEdit) } } // 修改ItemToDisplay class ItemToDisplay: ObservableObject { @Published var itemToDisplay: Item = Item(operations: [], numbers: [], lists: []) func getNumberIndex() -> Int { ItemLogic.getNumberIndex(for: itemToDisplay) } }
方案2:协议+扩展(统一接口)
定义一个协议要求实现类提供Item属性,通过协议扩展提供通用函数的默认实现,让两个ObservableObject类自动拥有这些逻辑。
// 定义协议,要求实现类提供可读写的Item属性 protocol ItemHolder: ObservableObject { var item: Item { get set } } // 协议扩展提供通用逻辑的默认实现 extension ItemHolder { func getNumberIndex() -> Int { item.operations.firstIndex(of: "divide") ?? -1 } func addMultiplyOperation() { // 因为Item是值类型,直接修改内部属性不会触发@Published更新,需要重新赋值 var newItem = item newItem.operations.append("multiply") self.item = newItem } } // 修改ItemToEdit,遵循协议 class ItemToEdit: ItemHolder { @Published var item: Item = Item(operations: [], numbers: [], lists: []) func edit() { addMultiplyOperation() } } // 修改ItemToDisplay,遵循协议 class ItemToDisplay: ItemHolder { @Published var item: Item = Item(operations: [], numbers: [], lists: []) // 自动拥有getNumberIndex等通用函数 }
方案3:组合方式(适合有状态的通用逻辑)
如果通用逻辑需要维护自己的状态,可以创建一个实例类,让ItemToEdit和ItemToDisplay持有这个实例,调用时传入各自的Item变量。
class ItemLogic { func getNumberIndex(for item: Item) -> Int { item.operations.firstIndex(of: "divide") ?? -1 } func addMultiplyOperation(to item: inout Item) { item.operations.append("multiply") } } class ItemToEdit: ObservableObject { @Published var itemToEdit: Item = Item(operations: [], numbers: [], lists: []) private let logic = ItemLogic() func edit() { logic.addMultiplyOperation(to: &itemToEdit) } func getNumberIndex() -> Int { logic.getNumberIndex(for: itemToEdit) } } class ItemToDisplay: ObservableObject { @Published var itemToDisplay: Item = Item(operations: [], numbers: [], lists: []) private let logic = ItemLogic() func getNumberIndex() -> Int { logic.getNumberIndex(for: itemToDisplay) } }
选择建议
- 若通用逻辑是无状态的纯函数,优先用方案1,代码最简洁。
- 若希望两个ObservableObject类有统一的接口,用方案2。
- 若通用逻辑需要维护自身状态,用方案3的组合方式。
内容的提问来源于stack exchange,提问作者Edoardo
相关产品推荐
相关产品推荐

