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

基于《Swift 5设计模式》状态模式示例代码出现内存访问错误的求助

解决Swift状态模式中“Simultaneous accesses”内存访问错误

嘿,我刚仔细看了你的代码,这个内存访问错误的原因其实挺明确的——你在同一代码路径里同时修改和访问了ATM实例的state属性,触发了Swift的内存独占访问检查。

具体来说,比如在EnterPinState的validate方法里:

context.state = TransactionCompleteState(context:context)
context.state.transactionCompleted(success: false)

你刚把context.state赋值为新的状态对象,紧接着又去读取这个属性调用方法。Swift会认为这时候修改操作还没完全完成,你又发起了访问,违反了“修改时需要独占访问”的规则。同样的问题也出现在WithdrawState的withdraw方法里。

这里有两个靠谱的解决方案,你可以选一个适合你的:

方案一:延迟后续方法调用

把状态切换后的方法调用放到下一个运行循环,确保状态修改完成后再执行访问操作。比如修改EnterPinState的代码:

guard pin == "1234" else {
    print("wrong pin")
    context.state = TransactionCompleteState(context:context)
    // 延迟到下一个主队列循环执行
    DispatchQueue.main.async {
        context.state.transactionCompleted(success: false)
    }
    return
}

同样,把WithdrawState里对应的transactionCompleted调用也用DispatchQueue.main.async包裹起来。这种方式改动小,适合快速修复。

方案二:重构状态逻辑(更优雅)

既然TransactionCompleteState的核心工作就是处理交易完成的逻辑,不如把这个逻辑移到它的初始化方法里,避免切换状态后还要手动调用方法。修改后的TransactionCompleteState和相关调用如下:

首先重构TransactionCompleteState:

fileprivate struct TransactionCompleteState: ATMState {
    let context: ATM
    
    init(context: ATM, success: Bool) {
        self.context = context
        let statusMessage = success ? "Transaction complete..." : "Transaction failed..."
        print(statusMessage)
        context.state = IdleState()
    }
    
    // 协议方法留空,因为不需要外部调用
    func transactionCompleted(success: Bool) {}
}

然后修改EnterPinState里的错误分支:

guard pin == "1234" else {
    print("wrong pin")
    // 初始化时直接处理完成逻辑
    context.state = TransactionCompleteState(context:context, success: false)
    return
}

再修改WithdrawState里的对应部分:

guard amount > 0 && availableFunds >= amount else {
    print("invalid")
    context.state = TransactionCompleteState(context: context, success: false)
    return false
}
availableFunds -= amount
context.state = TransactionCompleteState(context: context, success: true)
return true

这种方式让状态对象的职责更清晰,也从根源上避免了同时访问的问题,是更符合状态模式设计思想的做法。

最后,我把用方案二修改后的完整代码贴出来,你可以直接运行测试:

import Foundation

public final class ATM {
    fileprivate var state: ATMState = IdleState()
    
    public func enter(pin: String) {
        state = EnterPinState(context: self)
        state.validate(pin: pin)
    }
    
    public func withdraw(amount: Float) -> Bool {
        return state.withdraw(amount: amount)
    }
}

fileprivate protocol ATMState {
    func validate(pin: String)
    mutating func withdraw(amount: Float) -> Bool
    func transactionCompleted(success:Bool)
}

extension ATMState {
    func validate(pin:String) {
        print("not implemented")
    }
    
    mutating func withdraw(amount: Float) -> Bool {
        print("not implemented")
        return false
    }
    
    func transactionCompleted(success:Bool) {
        print("not implemented")
    }
}

fileprivate struct IdleState: ATMState {
}

fileprivate struct EnterPinState: ATMState {
    let context: ATM
    
    func validate(pin: String) {
        guard pin == "1234" else {
            print("wrong pin")
            context.state = TransactionCompleteState(context:context, success: false)
            return
        }
        print("pin ok")
        context.state = WithdrawState(context: context)
    }
}

fileprivate struct TransactionCompleteState: ATMState {
    let context: ATM
    
    init(context: ATM, success: Bool) {
        self.context = context
        let statusMessage = success ? "Transaction complete..." : "Transaction failed..."
        print(statusMessage)
        context.state = IdleState()
    }
    
    func transactionCompleted(success: Bool) {}
}

fileprivate struct WithdrawState: ATMState {
    var context: ATM
    var availableFunds: Float = 1000
    
    init(context: ATM) {
        self.context = context
    }
    
    mutating func withdraw(amount: Float) -> Bool {
        print("Withdraw $\(amount)")
        guard amount > 0 && availableFunds >= amount else {
            print("invalid")
            context.state = TransactionCompleteState(context: context, success: false)
            return false
        }
        availableFunds -= amount
        context.state = TransactionCompleteState(context: context, success: true)
        return true
    }
}

let atm = ATM()
atm.enter(pin:"1234")
atm.withdraw(amount: 10)

运行这段代码,你会发现那个内存访问错误已经消失,程序会正常输出:

pin ok
Withdraw $10.0
Transaction complete...

内容的提问来源于stack exchange,提问作者Michael Garfinkle

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 12:12:33