Day11 聚焦结构体的 “进阶特性”——访问控制让我们能隐藏内部数据、避免非法修改(比如银行账户的余额不能直接篡改),静态属性 / 方法则让结构体本身拥有 “共享数据”(比如应用的版本号、学校的总学生数)。这两个特性是从 “写能运行的代码” 到 “写安全、可维护的代码” 的关键,尤其在团队开发或复杂项目中必不可少。整理了实操场景、避坑点和核心用法,新手朋友跟着练能快速掌握 “代码封装” 的进阶思路~

目录

一、访问控制:控制结构体的 “内外可见性”

1. 为什么需要访问控制?

2. 常用访问控制修饰符

3. 实战:给银行账户加访问控制

4. 注意事项:private 属性与自定义初始化器

二、静态属性与方法:让结构体 “自带共享数据”

1. 定义静态属性与方法:用 static 关键字

2. 静态 vs 非静态:访问规则

3. 静态属性的实用场景

场景 1:存储全局共享数据

场景 2:创建示例数据(SwiftUI 预览常用)

三、Day11 总结:访问控制与静态属性的核心价值

明日计划


一、访问控制:控制结构体的 “内外可见性”

访问控制的核心是 “隐藏内部实现,只暴露必要的接口”,避免外部代码绕过逻辑直接修改数据(比如银行账户的 funds 不能直接改,必须通过 deposit/withdraw 方法)。

1. 为什么需要访问控制?

先看一个 “不安全” 的例子:

struct BankAccount {
    var funds = 0  // 外部可直接访问和修改
    
    mutating func deposit(amount: Int) {
        funds += amount  // 正常存款逻辑
    }
    
    mutating func withdraw(amount: Int) -> Bool {
        if funds >= amount {
            funds -= amount  // 正常取款逻辑(判断余额是否足够)
            return true
        } else {
            return false
        }
    }
}

// 问题:外部代码可以直接修改 funds,绕过取款逻辑
var sjxAccount = BankAccount()
sjxAccount.deposit(amount: 1000)  // 正常存款,funds=1000
sjxAccount.funds = -500  // 直接篡改余额为负数,没有触发任何校验
print(sjxAccount.funds)  // 输出 -500(非法状态)
  • 风险:外部代码直接修改内部属性,会破坏结构体的逻辑一致性(比如余额为负、库存为负)。访问控制就是为了避免这种情况。
2. 常用访问控制修饰符

Swift 提供了 4 种核心访问控制修饰符,新手重点掌握前 3 种即可:

修饰符 作用(控制 “谁能访问”) 适用场景
private 仅允许结构体内部访问(外部完全不可见) 隐藏核心数据(如银行账户的 funds 底层存储)
private(set) 允许外部读取,但仅允许结构体内部修改(只读不写) 暴露状态但禁止外部修改(如余额可查不可改)
public 允许任何地方访问(完全公开) 暴露对外接口(如 deposit/withdraw 方法)
fileprivate 允许当前文件内访问(跨文件不可见) 同文件内共享数据(较少用,新手可暂忽略)
3. 实战:给银行账户加访问控制

用 private(set) 修饰 funds,实现 “外部可查、内部可改”,确保修改必须通过方法:

struct SafeBankAccount {
    // private(set):外部能读,内部能改
    private(set) var funds = 0
    
    // 公开方法:外部通过方法存款
    mutating func deposit(amount: Int) {
        // 可额外加校验(如存款金额不能为负)
        guard amount > 0 else {
            print("存款金额必须为正数!")
            return
        }
        funds += amount
    }
    
    // 公开方法:外部通过方法取款
    mutating func withdraw(amount: Int) -> Bool {
        guard amount > 0 else {
            print("取款金额必须为正数!")
            return false
        }
        if funds >= amount {
            funds -= amount
            return true
        } else {
            print("余额不足!")
            return false
        }
    }
}

// 测试:外部只能通过方法修改,无法直接篡改
var safeAccount = SafeBankAccount()
safeAccount.deposit(amount: 1000)  // 正常存款,funds=1000
print(safeAccount.funds)  // 外部可读取:1000(没问题)

// safeAccount.funds = -500  // 编译报错!外部不能修改(private(set) 限制)

let withdrawSuccess = safeAccount.withdraw(amount: 1500)  // 余额不足,返回 false
print(safeAccount.funds)  // 仍为 1000(未被非法修改)
  • 关键效果:外部代码只能通过 deposit/withdraw 方法修改 funds,而方法内的校验逻辑(如金额为正、余额足够)能确保数据合法。
4. 注意事项:private 属性与自定义初始化器

如果结构体有 private 或 private(set) 的属性,Swift 会取消自动生成的默认初始化器,必须手动写自定义初始化器(确保所有属性初始化):

struct User {
    let name: String
    private(set) var age: Int  // 有 private(set) 属性
    
    // 必须手动写初始化器(默认初始化器被取消)
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    
    // 内部方法修改 age
    mutating func birthday() {
        age += 1
    }
}

let user = User(name: "SunJiaxing", age: 25)
print(user.age)  // 外部可读取:25
// user.age = 26  // 编译报错!外部不能修改

二、静态属性与方法:让结构体 “自带共享数据”

默认情况下,结构体的属性和方法属于 “实例”(每个实例有自己的副本,比如每个 BankAccount 有自己的 funds)。而静态属性 / 方法属于 “结构体本身”,所有实例共享一份数据(比如学校的总学生数,不管创建多少 School 实例,总人数只有一个)。

1. 定义静态属性与方法:用 static 关键字
struct School {
    // 静态属性:属于 School 结构体本身,所有实例共享
    static var studentCount = 0
    
    // 静态方法:属于 School 结构体本身,操作静态属性
    static func addStudent(name: String) {
        print("\(name) 加入学校!")
        studentCount += 1  // 直接修改静态属性,无需 mutating
    }
}

// 调用:直接用“结构体名.属性/方法”,无需创建实例
School.addStudent(name: "SunJiaxing")
School.addStudent(name: "LiSi")
print("学校总人数:\(School.studentCount)")  // 输出 2
  • 关键区别
    • 普通属性 / 方法:let account = BankAccount() → account.deposit()(需先创建实例);
    • 静态属性 / 方法:School.addStudent()(直接用结构体名调用,无需实例)。
  • 为什么不用 mutating:静态属性属于结构体本身,不是某个实例,因此修改静态属性的方法不需要 mutatingmutating 只用于修改实例的属性)。
2. 静态 vs 非静态:访问规则

静态属性 / 方法和非静态属性 / 方法的访问有严格规则,核心是 “静态不能访问非静态,非静态可以访问静态”:

访问方向 是否允许?原因
静态代码 → 非静态代码 ❌ 不允许(非静态属于实例,静态代码没有实例)
非静态代码 → 静态代码 ✅ 允许(静态属于结构体本身,随时可访问)

示例:规则验证

struct Example {
    // 非静态属性(属于实例)
    var instanceProperty = 10
    
    // 静态属性(属于结构体本身)
    static var staticProperty = 100
    
    // 非静态方法:可以访问静态属性(用“结构体名.属性”或“Self.属性”)
    func instanceMethod() {
        print("静态属性值:\(Example.staticProperty)")  // ✅ 允许
        print("静态属性值:\(Self.staticProperty)")      // ✅ 允许(Self 指当前结构体)
        print("非静态属性值:\(instanceProperty)")       // ✅ 允许(访问自身实例的属性)
    }
    
    // 静态方法:不能访问非静态属性(没有实例,不知道访问哪个 instanceProperty)
    static func staticMethod() {
        // print("非静态属性值:\(instanceProperty)")  // ❌ 编译报错!
        print("静态属性值:\(staticProperty)")          // ✅ 允许(访问自身静态属性)
    }
}

// 测试非静态方法访问静态属性
let example = Example()
example.instanceMethod()  // 输出:静态属性值:100;非静态属性值:10

// 测试静态方法
Example.staticMethod()  // 输出:静态属性值:100
  • 小技巧:非静态代码访问静态属性时,用 Self.静态属性 比 结构体名.静态属性 更灵活(如果结构体名修改,Self 无需同步改)。
3. 静态属性的实用场景

静态属性 / 方法在实际开发中非常常用,新手重点掌握两个场景:

场景 1:存储全局共享数据

比如应用的版本号、配置文件名、基础 URL 等,所有地方都需要访问,用静态属性统一管理:

struct AppConfig {
    // 应用版本号(所有地方都能访问,且不会变)
    static let appVersion = "1.0.0"
    // 数据存储文件名(统一管理,避免硬编码)
    static let saveFileName = "user_data.json"
    // 服务器基础 URL(修改时只需改一处)
    static let baseURL = "https://api.example.com"
}

// 其他地方使用(无需创建实例)
print("当前版本:\(AppConfig.appVersion)")  // 输出 1.0.0
let requestURL = "\(AppConfig.baseURL)/user"  // 拼接 URL
场景 2:创建示例数据(SwiftUI 预览常用)

开发 UI 时,需要示例数据来预览效果(比如展示一个示例用户、示例订单),用静态属性快速生成:

struct Employee {
    let username: String
    let department: String
    let salary: Double
    
    // 静态示例:UI 预览时直接用
    static let example = Employee(
        username: "sunjx",
        department: "iOS 开发",
        salary: 20000.0
    )
}

// SwiftUI 预览时使用(无需重复创建实例)
// struct EmployeeView_Previews: PreviewProvider {
//     static var previews: some View {
//         EmployeeView(employee: Employee.example)  // 直接用示例数据
//     }
// }

三、Day11 总结:访问控制与静态属性的核心价值

特性 核心作用 关键语法 / 注意事项
访问控制(private/private(set) 隐藏内部数据,避免非法修改,保证逻辑一致性 private:仅内部可见;private(set):外部只读、内部可改;有私有属性需手动写初始化器
静态属性 / 方法(static 让结构体拥有共享数据,无需创建实例 用 “结构体名。属性 / 方法” 调用;静态不能访问非静态,非静态可以访问静态;Self 代表当前结构体

明日计划

Day12 要学 Swift 的 “类(Class)”—— 与结构体类似,但支持继承(子类可以复用父类的代码),这是面向对象编程的核心。类和结构体在 Swift 中分工不同:结构体适合存储简单数据(如 AlbumEmployee),类适合复杂逻辑和继承场景(如 UserOrder)。有一起打卡的朋友吗?评论区可以分享你用 “访问控制 + 静态属性” 设计的结构体(比如 ShoppingCart 购物车,用 private(set) 控制商品列表,用静态属性存储默认折扣)~

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐