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

被折叠的 条评论
为什么被折叠?



