美文网首页
第十六章 Swift 结构体

第十六章 Swift 结构体

作者: 我有小尾巴快看 | 来源:发表于2019-06-06 16:24 被阅读0次

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均为结构体类型。

相关文章

网友评论

      本文标题:第十六章 Swift 结构体

      本文链接:https://www.haomeiwen.com/subject/ovprxctx.html