第十六章 Swift 結(jié)構(gòu)體

Swift 結(jié)構(gòu)體同樣擁有函數(shù)和屬性以及協(xié)議等能力,乍一看和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 的異同點(diǎn)

struct class
傳遞方式 傳值 引用
能否繼承 不可繼承 可以繼承
協(xié)議、泛型 支持 支持
內(nèi)存管理 系統(tǒng)自動管理 引用計(jì)數(shù)
內(nèi)存位置
Runtime 不支持 支持
與OC互動 不支持 支持
歸檔 不支持 支持

2. 結(jié)構(gòu)體的使用

結(jié)構(gòu)體的使用和類基本相同,但是也有一些不同。

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 修改結(jié)構(gòu)體的屬性值

聲明一個(gè)Student常量,如果我們想要修改math值,將會報(bào)錯(cuò),因?yàn)閟truct被聲明為了常量,它作為一個(gè)整體,屬性也都不允許修改,除非聲明為變量(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 結(jié)構(gòu)體的函數(shù)修改自身屬性值

給Student擴(kuò)展兩個(gè)函數(shù),如果函數(shù)想要修改自身屬性值,則必須添加mutating關(guān)鍵字,否則會報(bào)錯(cuò),因?yàn)槟J(rèn)是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 類包含結(jié)構(gòu)體

結(jié)構(gòu)體作為類的屬性,那么結(jié)構(gòu)體屬性自身遵循結(jié)構(gòu)體的特性,而類自身也不會受到影響。

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結(jié)構(gòu)體被定義為常量則不允許修改。

2.4 結(jié)構(gòu)體包含類

結(jié)構(gòu)體包含類,修改屬性中的類的屬性仍然是可行的。但是由于傳值的特性,class的引用特性將會被干擾,每一次復(fù)制都會復(fù)制里面的class,這樣會造成大量的資源浪費(fèi)。

為了解決這種問題,在結(jié)構(gòu)體中包含類的且可能會有復(fù)制操作的,我們需要使用寫時(shí)復(fù)制的方式處理。
isKnownUniquelyReferenced(_:)函數(shù)用于判斷一個(gè)對象是否僅引用了一次。

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) 
// 如若該行代碼被屏蔽,則不會復(fù)制
model.model.append("2") // copy! 

即便是這樣,在該結(jié)構(gòu)體被其他結(jié)構(gòu)體所包含時(shí),也可能會出現(xiàn)復(fù)制的問題,盡量不要將類作為結(jié)構(gòu)體的一部分。

3. 何時(shí)使用結(jié)構(gòu)體

結(jié)構(gòu)體的主要目的是用來封裝少量相關(guān)簡單數(shù)據(jù)值,相對于OC一切Model皆Class來說,Swift的Struct更適合邏輯很少的存儲型結(jié)構(gòu)。

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
    }
}

上面定義了兩個(gè)類型,Person struct和 User class。

  • Person僅僅存儲了兩個(gè)屬性,并且有一個(gè)簡單的邏輯isAdult,Person的內(nèi)存長度很小,如果不需要?dú)w檔功能并且常用于只讀場景,那么使用struct將會優(yōu)于class。
  • User同樣也是兩個(gè)屬性,但是設(shè)計(jì)需要?dú)w檔,那么它必須是class。即便不是歸檔的原因,在開發(fā)中User也可能被多次讀寫和傳遞,使用class可能會更適合一些。

其實(shí)大多數(shù)情況下struct和class沒什么區(qū)別,但是能用struct的地方還是盡量用struct,棧始終要比堆要快一些(由于struct的傳值方式可能會帶來內(nèi)存峰值占用過高的情況,實(shí)際選擇哪一種類型要看具體的應(yīng)用場景)。

4. 系統(tǒng)結(jié)構(gòu)體

  • Swift中集合類:Array,Dictionary,Set均為結(jié)構(gòu)體類型。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容