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

Swift协议扩展中如何保护结构体属性不被直接修改?

Swift协议扩展中如何保护结构体属性不被直接修改?

我是Swift新手,正在尝试使用协议扩展。在以下代码中,我无法保护结构体属性holdings免受直接修改。

enum BankingError: Error {
    case insufficientFunds
}

protocol Banking {
    var holdings: Int { get set }
    mutating func deposit(_ amount: Int)
    mutating func withdraw(_ amount: Int) throws
}

extension Banking {    
    mutating func deposit(_ amount: Int) {
        holdings += amount
    }
    
    mutating func withdraw(_ amount: Int) throws {
        guard amount <= holdings else { throw BankingError.insufficientFunds }
        holdings -= amount
    }
}

struct Bank: Banking {
    var holdings: Int
}

// Usage
var bank = Bank(holdings: 0)
bank.deposit(1)
try bank.withdraw(1)
do {
    try bank.withdraw(1)
} catch BankingError.insufficientFunds {
    print("Computer says 'No'")
}
// Disallow direct mutation of holdings?
bank.holdings = 0
bank.holdings += 1
bank.holdings -= 2
// Getter is OK
print(bank.holdings)

如果方法是作为结构体的一部分实现的,我可以将holdings声明为private(set),这种情况下有没有等效的选项?


解决方案

问题核心在于你定义的Banking协议要求holdings是{ get set },这强制所有遵守协议的类型必须对外暴露可写的holdings属性,直接用private(set)会违反协议要求,因此行不通。

要实现属性保护,需要调整协议设计,把可修改逻辑隐藏起来,对外只暴露只读属性和合法操作方法,具体有两种实用方式:

方式一:拆分协议(跨文件场景适用)

通过拆分对外公开协议和内部私有协议,把可修改属性隐藏在内部协议中,对外仅暴露只读接口:

enum BankingError: Error {
    case insufficientFunds
}

// 对外暴露的公开协议,仅包含只读属性和操作方法
protocol Banking {
    var holdings: Int { get }
    mutating func deposit(_ amount: Int)
    mutating func withdraw(_ amount: Int) throws
}

// 私有内部协议,用于协议扩展访问可修改的存储属性
private protocol BankingInternal {
    var _holdings: Int { get set }
}

// 让Banking协议的扩展仅对同时遵守BankingInternal的类型生效
extension Banking where Self: BankingInternal {
    mutating func deposit(_ amount: Int) {
        _holdings += amount
    }
    
    mutating func withdraw(_ amount: Int) throws {
        guard amount <= _holdings else { throw BankingError.insufficientFunds }
        _holdings -= amount
    }
    
    // 实现Banking协议的只读holdings属性
    var holdings: Int {
        return _holdings
    }
}

// 结构体同时遵守两个协议,用private(set)限制内部存储属性的修改权限
struct Bank: Banking, BankingInternal {
    private(set) var _holdings: Int
    
    init(holdings: Int) {
        self._holdings = holdings
    }
}

// 测试
var bank = Bank(holdings: 0)
bank.deposit(1)
try bank.withdraw(1)
do {
    try bank.withdraw(1)
} catch BankingError.insufficientFunds {
    print("Computer says 'No'")
}
// 以下代码会编译报错,无法直接修改属性
// bank.holdings = 0
// bank._holdings = 0
print(bank.holdings)

方式二:文件私有Setter(单文件场景适用)

如果代码都在同一个文件中,可以直接给协议的属性Setter设置fileprivate权限,这样文件外无法修改,文件内仍可通过协议扩展操作:

enum BankingError: Error {
    case insufficientFunds
}

protocol Banking {
    // 对外只读,文件内可写
    var holdings: Int { get fileprivate(set) }
    mutating func deposit(_ amount: Int)
    mutating func withdraw(_ amount: Int) throws
}

extension Banking {    
    mutating func deposit(_ amount: Int) {
        holdings += amount
    }
    
    mutating func withdraw(_ amount: Int) throws {
        guard amount <= holdings else { throw BankingError.insufficientFunds }
        holdings -= amount
    }
}

struct Bank: Banking {
    // 匹配协议的fileprivate(set)权限
    fileprivate(set) var holdings: Int
}

// 测试
var bank = Bank(holdings: 0)
bank.deposit(1)
try bank.withdraw(1)
do {
    try bank.withdraw(1)
} catch BankingError.insufficientFunds {
    print("Computer says 'No'")
}
// 文件外的代码无法执行以下修改(同一文件内仍能修改,需注意)
// bank.holdings = 0
print(bank.holdings)

这两种方式都能实现需求:对外只能通过deposit和withdraw修改资产,无法直接操作holdings属性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 03:24:51