12、Swift 类与结构体:属性、方法、初始化器及访问控制详解

Swift 类与结构体:属性、方法、初始化器及访问控制详解

1. 类与结构体的定义及计算属性

类和结构体的定义在语法上非常相似,主要区别在于使用 class struct 关键字来定义。计算属性的读写方式与存储属性相同,外部代码通常不需要知道该属性是计算属性。以下是一个 EmployeeStruct 结构体的示例:

var f = EmployeeStruct(firstName: "Jon", lastName: "Hoffman", salaryYear: 39000)
print(f.salaryWeek) // 输出 750.00
f.salaryWeek = 1000
print(f.salaryWeek) // 输出 1000.00
print(f.salaryYear) // 输出 52000.00

在这个例子中,设置 salaryWeek salaryYear 属性会同时改变它们的值。计算属性对于提供同一数据的不同视图非常有用,例如存储长度值时,可以存储厘米值,然后使用计算属性计算米、毫米和千米的值。

2. 属性观察器

属性观察器会在属性值设置时被调用,可以添加到任何非懒加载的存储属性上,也可以通过在子类中重写属性来添加到继承的存储或计算属性上。Swift 中有两种属性观察器: willSet didSet
- willSet :在属性值设置之前被调用。
- didSet :在属性值设置之后被调用。

需要注意的是,属性观察器在初始化时不会被调用。以下是为 EmployeeClass 类和 EmployeeStruct 结构体的 salaryYear 属性添加属性观察器的示例:

var salaryYear: Double = 0.0 {
    willSet(newSalary) {
        print("About to set salaryYear to \(newSalary)")
    }
    didSet {
        if salaryWeek > oldValue {
            print("\(firstName) got a raise")
        } else {
            print("\(firstName) did not get a raise")
        }
    }
}

如果不定义 willSet 观察器的新值名称,新值会被放在一个名为 newValue 的常量中:

willSet {
    print("About to set salaryYear to \(newValue)")
}
3. 方法

方法是与类或结构体关联的函数,用于封装特定任务或功能的代码。定义方法的方式与定义普通函数相同,例如定义一个返回员工全名的方法:

func getFullName() -> String {
    return firstName + " " + lastName
}

访问方法使用与访问属性相同的点语法:

var e = EmployeeClass()
var f = EmployeeStruct(firstName: "Jon", lastName: "Hoffman", salaryYear: 50000)
e.firstName = "Jon"
e.lastName = "Hoffman"
e.salaryYear = 50000.00
print(e.getFullName()) // 输出 Jon Hoffman
print(f.getFullName()) // 输出 Jon Hoffman

在类和结构体中定义修改属性值的方法时有所不同。在类中可以直接修改属性值,例如:

func giveRaise(amount: Double) {
    self.salaryYear += amount
}

但在结构体中,默认不允许在方法中修改属性值。如果需要修改,需要在方法声明的 func 关键字前添加 mutating 关键字:

mutating func giveRase(amount: Double) {
    self.salaryYear += amount
}

self 属性表示当前实例,可以用于区分局部变量和实例变量。例如:

func compareFirstName(firstName: String) -> Bool {
    return self.firstName == firstName
}
4. 自定义初始化器

初始化器在创建类或结构体的新实例时被调用,用于准备实例以供使用。初始化器是特殊的方法,使用 init 关键字作为名称。最简单的初始化器不接受任何参数:

init() {
    // 执行初始化操作
}

所有类和结构体都有一个空的默认初始化器,结构体还有一个额外的默认初始化器,接受每个存储属性的值并进行初始化。以下是为 EmployeeClass 类和 EmployeeStruct 结构体添加的三个自定义初始化器:

init() {
    self.firstName = ""
    self.lastName = ""
    self.salaryYear = 0.0
}

init(firstName: String, lastName: String) {
    self.firstName = firstName
    self.lastName = lastName
    self.salaryYear = 0.0
}

init(firstName: String, lastName: String, salaryYear: Double) {
    self.firstName = firstName
    self.lastName = lastName
    self.salaryYear = salaryYear
}

使用示例:

var g = EmployeeClass()
var h = EmployeeStruct(firstName: "Me", lastName: "Moe")
var i = EmployeeClass(firstName: "Me", lastName: "Moe", salaryYear: 45000)

在 Swift 中,初始化器没有返回值,不需要定义返回类型或使用 return 语句。

5. 初始化器的参数名

初始化器的参数可以有内部和外部名称。与函数不同的是,如果不提供外部参数名,Swift 会自动生成。例如:

init(employeeWithFirstName firstName: String, lastName lastName: String, andSalary salaryYear: Double) {
    self.firstName = firstName
    self.lastName = lastName
    self.salaryYear = salaryYear
}

使用示例:

var i = EmployeeClass(employeeWithFirstName: "Me", lastName: "Moe", andSalary: 45000)

使用外部参数名可以提高代码的可读性,并区分不同的初始化器。

6. 可失败初始化器

可失败初始化器可能无法初始化类或结构体所需的资源,导致实例不可用。可失败初始化器的结果是可选类型,包含有效实例或 nil 。通过在 init 关键字后添加问号 ? 来创建可失败初始化器。以下是一个不允许新员工年薪低于 20000 美元的可失败初始化器示例:

init?(firstName: String, lastName: String, salaryYear: Double) {
    self.firstName = firstName
    self.lastName = lastName
    self.salaryYear = salaryYear
    if self.salaryYear < 20000 {
        return nil
    }
}

使用示例:

if let f = EmployeeClass(firstName: "Jon", lastName: "Hoffman", salaryYear: 29000) {
    print(f.getFullName())
} else {
    print("Failed to initialize")
}
7. 访问控制

访问控制用于限制代码的访问和可见性,隐藏实现细节,只暴露需要的接口。Swift 中有三种访问级别:
| 访问级别 | 描述 |
| ---- | ---- |
| Public | 最可见的访问级别,允许在导入模块的任何地方使用。主要用于框架暴露公共 API。 |
| Internal | 默认访问级别,允许在定义源文件和所属模块(应用程序或框架)中使用。 |
| Private | 最不可见的访问级别,只允许在定义它的源文件中使用。 |

如果代码是自包含在单个应用程序中,不需要对外提供,通常可以忽略访问控制,因为默认的 Internal 级别已经满足要求。但如果需要隐藏部分实现,可以将访问级别设置为 Private ,但这应该是特殊情况而非常规做法。在开发框架时,访问控制非常有用,需要将公共接口标记为 Public ,内部接口分别使用 Internal Private 进行标记。

以下是一个简单的 mermaid 流程图,展示了可失败初始化器的流程:

graph TD;
    A[开始初始化] --> B{年薪是否低于 20000 美元};
    B -- 是 --> C[返回 nil,初始化失败];
    B -- 否 --> D[初始化成功];

通过以上内容,我们详细了解了 Swift 中类和结构体的属性、方法、初始化器以及访问控制的相关知识,这些特性为编写高效、安全的代码提供了强大的支持。在实际开发中,根据具体需求合理使用这些特性,可以提高代码的可读性、可维护性和可扩展性。

Swift 类与结构体:属性、方法、初始化器及访问控制详解

8. 访问控制的实际应用

在实际开发中,访问控制的合理运用能够显著提升代码的安全性和可维护性。以下为大家详细介绍不同访问级别的具体应用场景。

  • 公共访问级别(Public) :当我们开发一个框架时,需要将框架的公共接口暴露给其他模块使用,这时就需要将这些接口标记为 Public 。例如,一个网络请求框架,其中用于发起请求的方法和处理响应的协议等都应该是 Public 的,这样其他应用程序在导入该框架后才能正常使用这些功能。
// 定义一个公共的网络请求类
public class NetworkRequest {
    public func sendRequest(url: String) {
        // 实现网络请求逻辑
    }
}
  • 内部访问级别(Internal) :对于框架内部使用的一些工具类、辅助方法等,应该使用 Internal 访问级别。这样可以确保这些接口只能在框架内部使用,外部模块无法访问,从而隐藏了框架的实现细节。
// 定义一个内部的工具类
internal class Utility {
    internal func formatDate(date: Date) -> String {
        // 实现日期格式化逻辑
        return ""
    }
}
  • 私有访问级别(Private) :在源文件中,一些只在当前文件中使用的辅助函数、常量等可以使用 Private 访问级别。这样可以避免这些代码被其他文件误调用,提高代码的安全性。
// 定义一个私有函数
private func validateInput(input: String) -> Bool {
    // 实现输入验证逻辑
    return true
}
9. 综合示例:完整的员工类与结构体

为了更好地理解上述所有概念,我们来看一个完整的示例,包含员工类和结构体的定义、属性、方法、初始化器以及访问控制的应用。

// 定义员工类
public class EmployeeClass {
    public var firstName: String
    public var lastName: String
    public var salaryYear: Double {
        willSet(newSalary) {
            print("About to set salaryYear to \(newSalary)")
        }
        didSet {
            if salaryYear > oldValue {
                print("\(firstName) got a raise")
            } else {
                print("\(firstName) did not get a raise")
            }
        }
    }

    public init() {
        self.firstName = ""
        self.lastName = ""
        self.salaryYear = 0.0
    }

    public init(firstName: String, lastName: String) {
        self.firstName = firstName
        self.lastName = lastName
        self.salaryYear = 0.0
    }

    public init(firstName: String, lastName: String, salaryYear: Double) {
        self.firstName = firstName
        self.lastName = lastName
        self.salaryYear = salaryYear
    }

    public func getFullName() -> String {
        return firstName + " " + lastName
    }

    public func giveRaise(amount: Double) {
        self.salaryYear += amount
    }
}

// 定义员工结构体
public struct EmployeeStruct {
    public var firstName: String
    public var lastName: String
    public var salaryYear: Double {
        willSet(newSalary) {
            print("About to set salaryYear to \(newSalary)")
        }
        didSet {
            if salaryYear > oldValue {
                print("\(firstName) got a raise")
            } else {
                print("\(firstName) did not get a raise")
            }
        }
    }

    public init() {
        self.firstName = ""
        self.lastName = ""
        self.salaryYear = 0.0
    }

    public init(firstName: String, lastName: String) {
        self.firstName = firstName
        self.lastName = lastName
        self.salaryYear = 0.0
    }

    public init(firstName: String, lastName: String, salaryYear: Double) {
        self.firstName = firstName
        self.lastName = lastName
        self.salaryYear = salaryYear
    }

    public func getFullName() -> String {
        return firstName + " " + lastName
    }

    public mutating func giveRaise(amount: Double) {
        self.salaryYear += amount
    }
}

// 使用示例
let employeeClass = EmployeeClass(firstName: "Alice", lastName: "Smith", salaryYear: 50000)
print(employeeClass.getFullName())
employeeClass.giveRaise(amount: 5000)

var employeeStruct = EmployeeStruct(firstName: "Bob", lastName: "Johnson", salaryYear: 45000)
print(employeeStruct.getFullName())
employeeStruct.giveRaise(amount: 3000)
10. 总结与建议

通过对 Swift 中类和结构体的属性、方法、初始化器以及访问控制的学习,我们可以总结出以下几点建议:
- 合理选择类和结构体 :结构体是值类型,适合用于表示简单的数据结构,如坐标、日期等;类是引用类型,适合用于表示复杂的对象,如网络请求、数据库操作等。
- 灵活运用计算属性和属性观察器 :计算属性可以提供同一数据的不同视图,属性观察器可以在属性值改变时执行特定的操作,合理使用可以提高代码的灵活性和可维护性。
- 谨慎使用可失败初始化器 :可失败初始化器可以处理初始化过程中可能出现的错误,但使用时要确保逻辑清晰,避免过度复杂。
- 严格控制访问级别 :根据代码的实际需求,合理设置访问级别,隐藏实现细节,提高代码的安全性和可维护性。

以下是一个 mermaid 流程图,展示了类和结构体的使用流程:

graph LR;
    A[选择类或结构体] --> B{数据类型简单?};
    B -- 是 --> C[使用结构体];
    B -- 否 --> D[使用类];
    C --> E[定义属性和方法];
    D --> E;
    E --> F[添加初始化器];
    F --> G[设置访问控制];
    G --> H[使用类或结构体];

总之,Swift 中类和结构体的这些特性为开发者提供了强大的工具,能够帮助我们编写更加高效、安全和可维护的代码。在实际开发中,我们应该根据具体需求灵活运用这些特性,不断提升自己的编程能力。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值