Swift 结构体同样拥有函数和属性以及协议等能力,乍一看和class
没什么不同。
// 语法
struct nameStruct {
Definition 1
Definition 2
……
Definition N
}
struct Book {
var name: String
func eat() {
}
}
let book = Book(name: "Apple")
print(book.name) // "Apple"
1. Struct 与 Class 的异同点
struct | class | |
---|---|---|
传递方式 | 传值 | 引用 |
能否继承 | 不可继承 | 可以继承 |
协议、泛型 | 支持 | 支持 |
内存管理 | 系统自动管理 | 引用计数 |
内存位置 | 栈 | 堆 |
Runtime | 不支持 | 支持 |
与OC互动 | 不支持 | 支持 |
归档 | 不支持 | 支持 |
2. 结构体的使用
结构体的使用和类基本相同,但是也有一些不同。
struct Student {
let name: String
let age: UInt
var math: Int
var english: Int
var chinese: Int
var isAdult: Bool {
return age >= 18
}
}
2.1 修改结构体的属性值
声明一个Student常量,如果我们想要修改math值,将会报错,因为struct被声明为了常量,它作为一个整体,属性也都不允许修改,除非声明为变量(var
)。如果Student是class,则这里允许修改。
let stu = Student.init(name: "linda", age: 15, math: 90, english: 87, chinese: 96)
stu.math = 100 // error: Cannot assign to property: 'stu' is a 'let' constant
var stu2 = Student.init(name: "bob", age: 16, math: 80, english: 70, chinese: 60)
stu2.english = 90 // stu2.english is 90
2.2 结构体的函数修改自身属性值
给Student扩展两个函数,如果函数想要修改自身属性值,则必须添加mutating
关键字,否则会报错,因为默认是immutable
。
extension Student {
func cheat(math: Int) {
self.math = math // Cannot assign to property: 'self' is immutable
}
mutating func cheat(english: Int) {
self.english = english
}
}
2.4 类包含结构体
结构体作为类的属性,那么结构体属性自身遵循结构体的特性,而类自身也不会受到影响。
class Teacher {
let students: [Student]
var salary: Float
init(students: [Student], salary: Float) {
self.students = students
self.salary = salary
}
}
var teacher = Teacher.init(students: [], salary: 10000)
teacher.salary += 1000
teacher.students.append(.init(name: "sam", age: 11, math: 98, english: 78, chinese: 11)) // Cannot use mutating member on immutable value: 'students' is a 'let' constant
teacher = Teacher.init(students: [], salary: 20000)
我们可以修改teacher类的salary和其自身,但是students结构体被定义为常量则不允许修改。
2.4 结构体包含类
结构体包含类,修改属性中的类的属性仍然是可行的。但是由于传值的特性,class的引用特性将会被干扰,每一次复制都会复制里面的class,这样会造成大量的资源浪费。
为了解决这种问题,在结构体中包含类的且可能会有复制操作的,我们需要使用写时复制的方式处理。
isKnownUniquelyReferenced(_:)
函数用于判断一个对象是否仅引用了一次。
struct Model<Element: NSMutableCopying> {
private var _model: Element
var model: Element {
mutating get {
if !isKnownUniquelyReferenced(&_model) {
_model = _model.mutableCopy() as! Element
print("copy!")
}
return _model
}
}
init(_ model: Element) {
_model = model
}
}
let str = NSMutableString(string: "1")
var model = Model(str)
// 如若该行代码被屏蔽,则不会复制
model.model.append("2") // copy!
即便是这样,在该结构体被其他结构体所包含时,也可能会出现复制的问题,尽量不要将类作为结构体的一部分。
3. 何时使用结构体
结构体的主要目的是用来封装少量相关简单数据值,相对于OC一切Model皆Class来说,Swift的Struct更适合逻辑很少的存储型结构。
struct Person {
let name: String
let age: UInt
var isAdult: Bool {
return age >= 18
}
}
class User: NSCoding {
let id: String
let nickName: String
init(id: String,
nickName: String) {
self.id = id
self.nickName = nickName
}
func encode(with aCoder: NSCoder) {
aCoder.encode(id, forKey: "id")
aCoder.encode(nickName, forKey: "nickName")
}
required init?(coder aDecoder: NSCoder) {
guard let id = aDecoder.decodeObject(forKey: "id") as? String else { return nil }
guard let nickName = aDecoder.decodeObject(forKey: "nickName") as? String else { return nil }
self.id = id
self.nickName = nickName
}
}
上面定义了两个类型,Person struct和 User class。
- Person仅仅存储了两个属性,并且有一个简单的逻辑isAdult,Person的内存长度很小,如果不需要归档功能并且常用于只读场景,那么使用struct将会优于class。
- User同样也是两个属性,但是设计需要归档,那么它必须是class。即便不是归档的原因,在开发中User也可能被多次读写和传递,使用class可能会更适合一些。
其实大多数情况下struct和class没什么区别,但是能用struct的地方还是尽量用struct,栈始终要比堆要快一些(由于struct的传值方式可能会带来内存峰值占用过高的情况,实际选择哪一种类型要看具体的应用场景)。
4. 系统结构体
- Swift中集合类:
Array
,Dictionary
,Set
均为结构体类型。
网友评论