本文小节On this page
一篇面向 Swift 初学者的技术文章近日在 DEV Community 发布,围绕 Swift 结构体中的访问控制展开讲解。文章以游戏公会金库为例,说明如果结构体属性默认可被外部直接访问,业务逻辑可能被绕过;而通过 private、private(set) 等访问控制关键字,可以让 Swift 在编译阶段帮助开发者约束数据读写方式,从而提升代码可靠性与可维护性。
摘要
文章指出,在 Swift 中为结构体添加属性和方法时,如果不显式设置访问级别,属性可能会被外部代码直接读写。对于包含业务规则的数据结构而言,这种开放访问会带来风险:外部代码可以绕开预设方法,直接修改内部状态,导致校验逻辑失效。
作者使用一个“公会金库”结构体作为示例。金库拥有 gold 属性,并提供 deposit(amount:) 和 withdraw(amount:) 两个方法,用于存入和取出金币。表面上看,取款方法已经包含余额不足时返回失败的判断,但如果 gold 可以从结构体外部直接修改,调用方仍然可以通过 fairyTail.gold -= 9999 之类的写法绕过取款校验,使金库余额变为不合理状态。
针对这一问题,文章介绍了两种常见做法:将属性标记为 private,使其只能在结构体内部访问;或使用 private(set),允许外部读取属性值,但禁止外部直接写入。作者认为,这类访问控制并非只是语法细节,而是定义代码使用边界、减少误用和降低维护成本的重要工具。
关键点
- Swift 结构体中的属性如果不加限制,外部代码可能直接修改其值,绕过方法中的业务校验。
private可将属性限制在结构体内部访问,外部直接读写会编译失败。private(set)允许外部读取属性,但只有结构体内部方法可以修改该属性。- 当结构体中存在
private属性时,可能需要自定义初始化方法,因为自动生成的成员初始化器无法从外部设置私有属性。 - 文章还提到
fileprivate与public等访问级别,但强调初学和应用开发中更常用的是private与private(set)。 - 资料未提及 Swift 版本、性能测试数据或具体生产项目案例。
背景:访问控制解决的不是“能不能写”,而是“该不该写”
在示例中,GuildTreasury 结构体代表一个游戏中的公会金库。它包含一个 gold 数值属性,以及两个变更余额的方法:存款方法直接增加余额,取款方法则先检查余额是否足够。如果余额不足,方法会输出提示并返回失败;如果余额充足,则扣除对应金额并返回成功。
问题在于,如果 gold 属性对外开放,调用者并不一定会通过 withdraw(amount:) 方法完成扣款,而是可以直接修改属性值。这种写法会绕过余额检查,使原本集中在方法中的规则失去意义。文章将这种情况描述为访问控制缺失带来的典型风险:代码看似拥有业务逻辑,但并不能阻止外部以错误方式改变状态。
为修复这一问题,作者建议将 gold 声明为 private var gold = 0。这样一来,Swift 编译器会阻止结构体外部访问该属性。外部代码无法直接对 gold 做减法或赋值,只能通过结构体暴露出来的 deposit 和 withdraw 方法操作余额。换言之,规则不再只依赖开发者自觉遵守,而是由语言机制强制执行。
技术细节:private 与 private(set) 的不同边界
文章重点区分了 private 和 private(set) 两种访问方式。
private 是更严格的限制。被标记为 private 的属性只能在当前结构体内部访问,结构体外部既不能读取,也不能写入。这适合完全不希望外界感知的内部状态。例如,如果金库余额只应通过方法体现,而不需要外部直接查看,就可以使用 private。
private(set) 则提供了更灵活的控制。它允许外部读取属性值,但禁止外部直接赋值。文章用金库场景说明:公会成员可以查看金库当前金币数量,但不能直接把余额设置成任意数值;余额变化只能通过官方的存款和取款方法完成。这类设计既保留了状态透明度,又避免了写入逻辑被绕过。
文章还列举了 Swift 中其他访问控制选项。fileprivate 表示同一个 Swift 文件内可访问,public 表示可从任何地方访问,包括模块内外。作者认为,在学习阶段和普通应用开发中,开发者最常接触的是 private 和 private(set);而 fileprivate、public 等在构建框架或更大型、多文件项目时会更常见。
需要注意的是,文章特别提醒,如果结构体中的一个或多个属性使用了 private,开发者可能需要自行编写初始化方法。原因是 Swift 自动生成的成员初始化器不能从结构体外部设置私有属性。来源材料没有进一步展开具体初始化器写法。
影响:提高代码安全边界与团队协作效率
从工程实践看,访问控制的价值不只在于防止“故意破坏规则”。文章强调,更常见的风险来自误用:代码可能在数月后由同一开发者维护,也可能被新加入项目的团队成员修改,还可能作为第三方库被其他人调用。在这些场景中,仅靠注释或约定提醒使用者不要直接修改内部状态,可靠性并不高。
通过访问控制,结构体可以明确告诉调用方:哪些属性和方法属于正式接口,哪些只是内部实现细节。对于阅读代码的人来说,这也能减少理解负担。如果一个结构体包含多个属性和方法,而其中大部分被标记为私有,外部使用者就能更快识别真正需要关注的公开接口。
这种设计对商业项目同样具有实际意义。随着代码规模扩大,数据状态被错误修改可能引发业务异常、调试成本上升和维护风险增加。访问控制通过语言层面的限制,把部分潜在错误提前暴露在编译阶段,有助于降低后续排查成本。来源材料没有提供具体企业案例或量化收益,因此其商业价值主要体现在通用软件工程实践层面,仍需结合具体项目评估。
未确认问题与相关视频
来源文章未提及该内容对应的 Swift 官方文档版本,也未说明示例代码是否覆盖所有访问控制边界情况。资料也没有提供相关视频链接,因此本文无法补充视频来源。