结构和类是灵活结构的构建块,帮助开发者决定如何在程序中存储数据和建立行为模型。Swift中的类通常被视为创建对象的蓝图。
由于能够通过定义属性来存储数值,并通过创建方法来增加功能,类和结构的共享特征在Swift中常常可以互换使用。然而,它们都有差异和独特性,给开发者带来了灵活性,可以在他们认为最好的地方使用它们。
我们将回顾一下类和结构的异同,以及它们在代码中的功能。
Swift类和结构的相似性和差异概述
Swift中的类和结构的相似之处提供了互换性和灵活性。例如,如前所述,类和结构都可以定义属性来存储值,为在代码中存储数据和建模行为提供不同的选择。
其他相似之处包括。
- 通过使用
init()关键字设置初始状态值的初始化器 - 定义子标的能力,提供对序列的值、集合或列表的快速访问
- 使用以下关键字扩展类或结构的能力
[extension](https://blog.logrocket.com/understanding-protocols-in-swift/)关键字 - 协议的一致性
然而,类具有区别于结构的额外功能。类可以继承另一个类的所有属性、行为和方法,也可以在继承的基础上增加额外的功能。
另一个区别是类型转换,它使开发人员能够在运行时检查和解释类的实例类型。
类和结构的语法在Swift中的相似性
在Swift中定义类和结构的语法也很相似。要在Swift中定义一个类或结构,使用关键字class 或struct ,后面是带大括号的类或结构的名称。
值得注意的是,要确保类和结构的名称在Swift中遵循PascalCase的命名规则。
在我们的例子中,让我们创建一个类和结构,名称为User 。
class User {
...
}
struct User {
...
}
现在我们可以在代码中添加类和结构的定义。
类的定义
当我们创建类并添加类定义时,我们可以提供默认值,使定义成为可选项,或者创建我们自己的初始化器。
在Swift中,每个类都必须有一个初始化器。如果一个类有子类,初始化器可以保证编译器的子类继承或实现相同的初始化器。这使我们能够定义类的定义。
例如,我们可以在下面的代码中创建一个自定义的初始化器,将firstName 定义为String ,以初始化并分配给firstName 一些值。
class User {
var firstName: String
var lastName: String
var gender: String
init(firstName: String, lastName: String, gender: String) {
self.firstName = firstName
self.lastName = lastName
self.gender = gender
}
}
注意,在self.firstName = firstName ,self.firstName 指的是我们在var firstName: String 类中定义的firstName 。self 指的是User 的当前实例。
当User 类的属性有默认值时,User 类会自动实现一个默认的初始化器,创建一个新的实例,其属性设置为默认值。
对于一个有缺省值的类定义,我们可以添加以下内容。
class User {
var firstName = "Ejiro"
var lastName = "Asiuwhu"
var gender = "male"
}
如果我们不确定我们是想让一个变量保持一个值还是以后再赋值,我们可以把这个变量变成可选的。对于一个有可选值的类定义,我们可以添加以下内容。
class NewUser {
var firstName: String?
var lastName: String?
var age: Int?
}
结构定义
在Swift中只有一种方法可以定义结构。
struct User {
var firstName = "Ejiro"
var lastName = "Asiuwhu"
var gender = "male"
}
创建Swift类实例
Swift中的类实例被称为对象。要使用我们之前创建的User 类,我们必须创建一个实例。
class User {
// class definition
var firstName: String
var lastName: String
var gender: String
var age: Int
// creating our initilizer
init(firstName: String, lastName: String, gender: String, age: Int) {
self.firstName = firstName
self.lastName = lastName
self.gender = gender
self.age = age
}
}
// creating a class instance
let person:User = User(firstName: "Ejiro", lastName: "Asiuwhu", gender: "male", age: 45)
值得注意的是,Swift类实例是可变的对象,而结构的实例是不可变的值。因为类是一种引用类型,对分配给类实例的变量所做的任何改变都会影响到原来的类,使其成为可变的。
另一方面,由于结构是一种值类型,对分配给结构实例的变量所做的任何改变都会影响原始结构,使其值不可改变。
访问类实例的属性
当我们需要访问一个类的数据时,我们可以使用点符号。例如,要访问我们在之前的例子中创建的User 类的age 属性,我们可以添加以下内容。
// creating a class instance
let person:User = User(firstName: "Ejiro", lastName: "Asiuwhu", gender: "male", age: 45)
person.age // expected output: 45
除了访问数据之外,我们还可以使用点符号语法来为变量属性设置值,从而允许我们添加额外的数据。
// creating a class instance
let person:User = User(firstName: "Ejiro", lastName: "Asiuwhu", gender: "male", age: 45)
person.age = 78 // expected output: 78
在类和结构中创建Swift方法
Swift类和结构都可以定义方法来提供功能。通过使用func 关键字在我们的User 类中创建一个方法,我们可以添加getDetails() 来访问firstName,lastName,age, 和gender 等信息。
class User {
// class definition
var firstName: String
var lastName: String
var gender: String
var age: Int
// creating our initilizer
init(firstName: String, lastName: String, gender: String, age: Int) {
self.firstName = firstName
self.lastName = lastName
self.gender = gender
self.age = age
}
// methods in Swift classes
func getDetails() {
print("\(firstName) \(lastName) is a \(age) year old \(gender)")
}
// creating a class instance
let person:User = User(firstName: "Ejiro", lastName: "Asiuwhu", gender: "male", age: 45)
// the me
person.getDetails() // expected output: Ejiro Asiuwhu is a 45 year old male
注意新创建的getDetails() 方法现在在我们的类实例中是可用的。我们可以在let person:User = User 实例上使用点符号来访问这个方法,然后用括号来调用func 。
同样地,我们也可以用点符号在结构中定义方法,以提供功能。
struct User {
var firstName: String
var lastName: String
var gender: String
var age: Int
func getDetails() {
print("\(firstName) \(lastName) is a \(age) year old \(gender)")
}
}
let person:User = User(firstName: "Ejiro", lastName: "Asiuwhu", gender: "male", age: 45)
person.getDetails() // expected output: Ejiro Asiuwhu is a 45 year old male
Swift类的区别
类的继承
继承是类中区别于结构的一个基本特征。在决定编写Swift时是使用类还是结构时,了解继承的工作原理很重要。
子类允许我们从一个类继承到另一个类,这意味着一个类(指定为子类)可以访问另一个类(指定为超类)的所有数据,如属性和方法*。*
要开始子类化,我们必须定义我们的超类,然后在现有超类的基础上建立一个新的子类。
子类也不会限制我们,因为无论我们继承了什么,我们都可以为我们的子类添加更多的功能和属性。
为了理解继承在Swift类中的作用,让我们重新使用我们的User 类作为超类,并创建一个名为Admin 的子类来继承User 的属性。
class User {
// class definition
var firstName: String
var lastName: String
var gender: String
var age: Int
// creating our initilizer
init(firstName: String, lastName: String, gender: String, age: Int) {
self.firstName = firstName
self.lastName = lastName
self.gender = gender
self.age = age
}
}
class Admin: User {
var authorize: Bool?
}
var admin = Admin(firstName: "Ejiro", lastName: "Asiuwhu", gender: "male", age: 45)
admin.authorize = true;
print(admin.authorize) // expected output: true
注意我们是如何通过添加更多的属性来完善Admin 子类的,而不是从User 超类中继承的属性。
值和引用
将结构体和类区分开来的一个基本特征是,结构体是值类型,类是引用类型。
当创建一个结构并将其分配给一个变量时,值被复制,因为它是一个值类型。通过将point2 结构的值设置为point1 结构的值,我们是在为每个变量创建一个单独的副本。
因此,当point1 的值被改变时,并不影响point2 的值。
struct Coordinates {
var lat: Double
var lng: Double
}
var point1:Coordinates = Coordinates(lat: 5.519, lng: 5.7599)
// here, we are setting the values of point2 to be the value of point1
var point2:Coordinates = point1
point2.lat = 6.45
point2.lng = 8.211
print(point2) // expected output: Coordinates(lat: 6.45, lng: 8.211)
print(point1) // expected output: Coordinates(lat: 5.519, lng: 5.7599)
但是当类被分配给一个变量时,它引用现有的实例而不是复制它。
class User {
var firstName: String
var lastName: String
var gender: String
var age: Int
init(firstName: String, lastName: String, gender: String, age: Int) {
self.firstName = firstName
self.lastName = lastName
self.gender = gender
self.age = age
}
}
var user1:User = User(firstName: "Ejiro", lastName: "Asiuwhu", gender: "male", age: 29)
// user2 now holds the same value as user1
var user2:User = user1
user1.age = 30
print(user1.age) // expected output: 30
print(user2.age) // expected output: 30
注意这里的值和引用类型的区别:当引用类型中的一个值发生变化时,所有被引用的变量也会发生变化。
正如我们在上面的类中看到的,user1.age 和user2.age 现在是同一个值。这是因为user1 不仅仅是user2 的拷贝,而是user1 是user2 。
当我们存储一个类时,我们是在内存中存储它的值,而指向一个类的变量只是持有对该类的引用。
当我们为我们的类添加var user2:User = user1 ,我们是在告诉user2 来引用user1 ,使得两个变量中的所有数据都是同步的。如果我们改变其中一个,另一个也会改变。
在 Swift 中何时使用类与结构
苹果的官方文档主要建议用户默认使用结构体。这主要是因为结构体更安全,没有错误,尤其是在多线程环境中。如果结构体相对较小且可复制,也是比较好的,因为复制结构体比对同一实例有多个引用更安全。
在选择结构体和类的时候,记住关键的区别是很重要的。
- 类是引用类型,而结构体是值类型
- 如果不需要类的继承,结构体的速度更快,内存效率更高
- 对于具有独立状态的对象的唯一副本,使用结构体。
- 在处理少数相对简单的数据值时使用结构体
- 使用类来访问Objective-C的运行时间
- 使用类来控制一个对象的身份
- 当不需要控制对象的身份时,使用结构。
在Swift中工作时,类和结构提供了灵活性。虽然它们通常可以互换,但它们略微不同的功能为开发者提供了他们需要的选择。
欢迎发表评论,让我知道你对这篇文章的看法。你也可以在Twitter和GitHub上找到我。谢谢您的阅读!
The postIntroduction to classes and structs in Swiftappeared first onLogRocket Blog.