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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 10:10:34