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
相关产品推荐
相关产品推荐

