Swift語(yǔ)法 Swift5 【08 - 屬性】


  • 作者: Liwx
  • 郵箱: 1032282633@qq.com
  • 源碼: 需要源碼的同學(xué), 可以在評(píng)論區(qū)留下您的郵箱

iOS Swift 語(yǔ)法 底層原理內(nèi)存管理分析 專題:【iOS Swift5語(yǔ)法】

00 - 匯編
01 - 基礎(chǔ)語(yǔ)法
02 - 流程控制
03 - 函數(shù)
04 - 枚舉
05 - 可選項(xiàng)
06 - 結(jié)構(gòu)體和類
07 - 閉包
08 - 屬性
09 - 方法
10 - 下標(biāo)
11 - 繼承
12 - 初始化器init
13 - 可選項(xiàng)


目錄

  • 01-屬性
  • 02-存儲(chǔ)屬性
  • 03-計(jì)算屬性
  • 04-枚舉rawValue原理
  • 05-延遲存儲(chǔ)屬性(Lazy Stored Property)
  • 06-延遲存儲(chǔ)屬性注意點(diǎn)
  • 07-屬性觀察器(Property Observer)
  • 08-全局變量、局部變量
  • 09-inout的再次研究
  • 10-inout的本質(zhì)總結(jié)
  • 11-類型屬性(Type Property)
  • 12-類型屬性細(xì)節(jié)
  • 13-單例模式

01-屬性

  • Swift中跟實(shí)例相關(guān)的屬性可以分為2大類型: 存儲(chǔ)屬性(Stored Property), 計(jì)算屬性(Computed Property)

  • 存儲(chǔ)屬性
    • 類似于成員變量這個(gè)概念
    • 存儲(chǔ)在實(shí)例的內(nèi)存中
    • 結(jié)構(gòu)體、類可以定義存儲(chǔ)屬性
    • 枚舉不可以定義存儲(chǔ)屬性

  • 計(jì)算屬性
    • 本質(zhì)就是方法(函數(shù))
    • 不占用實(shí)例的內(nèi)存
    • 枚舉、結(jié)構(gòu)體、類可以定義計(jì)算屬性
struct Circle {
    // 存儲(chǔ)屬性 占用內(nèi)存
    var radius: Double
    // 計(jì)算屬性 不占用內(nèi)存
    var diameter: Double {
        set {
            radius = newValue / 2
        }
        get {
            radius * 2   // 如果只有單一表達(dá)式,可以省略return
        }
        /*
         也可以這樣寫
         set(newDiameter) {
         radius = newDiameter / 2
        }
         */
    }
}

var circle = Circle(radius: 5)
print(circle.radius)        // 5.0
print(circle.diameter)      // 10.0

circle.diameter = 12
print(circle.radius)        // 6.0
print(circle.diameter)      // 12.0

print(MemoryLayout<Circle>.stride)  // 8

02-存儲(chǔ)屬性

  • 關(guān)于存儲(chǔ)屬性, Swift有個(gè)明確的規(guī)定
  • 在創(chuàng)建或者結(jié)構(gòu)體的實(shí)例時(shí),必須為所有的存儲(chǔ)屬性設(shè)置一個(gè)合適的初始值
    • 可以在初始化器里為存儲(chǔ)屬性設(shè)置初始值
    • 可以分配一個(gè)默認(rèn)的屬性值作為屬性定義的一部分

  • 在初始化器里為存儲(chǔ)屬性設(shè)置一個(gè)初始值
struct Point {
    var x: Int
    var y: Int
    init() {
        x = 11
        y = 22
    }
}

  • 分配一個(gè)默認(rèn)的屬性值作為屬性定義的一部分
struct Point {
    var x: Int = 11
    var y: Int = 22
}
var p = Point()

03-計(jì)算屬性

  • set傳入的新增默認(rèn)叫做newValue,也可以自定義
struct Circle {
    var radius: Double
    var diameter: Double {
//        set(newDiameter) {    // 自定義
//            radius = newDiameter / 2
//        }
        set {
            radius = newValue / 2
        }
        get {
            radius * 2
        }
    }
}

  • 定義計(jì)算屬性只能用var, 不能用let
    • let代表常量: 值是一成不變
    • 計(jì)算屬性的值是可能發(fā)生變化的(即使是只讀計(jì)算屬性)

  • 只讀計(jì)算屬性: 只有get, 沒有set
struct Circle {
    var radius: Double
    var diameter: Double {
        get {
            radius * 2
        }
    }
}

  • 只讀計(jì)算屬性, 只有get方法,可以省略get
struct Circle {
    var radius: Double
    var diameter: Double { radius * 2 }
}
  • 注意: 不能只有set方法, 沒有g(shù)et方法

04-枚舉rawValue原理

  • 枚舉原始值rawValue的本質(zhì)是: 只讀計(jì)算屬性
enum TestEnum : Int {
    case test1 = 1, test2 = 2, test3 = 3
    var rawValue: Int {
        switch self {
        case .test1:
            return 10
        case .test2:
            return 11
        case .test3:
            return 12
        }
    }
}

print(TestEnum.test3.rawValue)  // 12

05-延遲存儲(chǔ)屬性(Lazy Stored Property)

  • 使用lazy可以定義一個(gè)延遲存儲(chǔ)屬性, 在第一次用到屬性的時(shí)候才會(huì)進(jìn)行初始化
  • lazy屬性必須是var, 不能是let
    • let必須在實(shí)例的初始化方法完成之前就擁有值
  • 如果多條線程同時(shí)第一次訪問lazy屬性
    • 無法保證屬性只被初始化1次 (不是線程安全)
class Car {
    init() {
        print("Car init!")
    }
    func run() {
        print("Car is running!")
    }
}

class Person {
    lazy var car = Car()
//    lazy let car1 = Car()   // 延遲存儲(chǔ)屬性必須用var,  error: 'lazy' cannot be used on a let
    init() {
        print("Person init!")
    }
    func goOut() {
        car.run()
    }
}

let p = Person()    // 還沒用到car延遲屬性,因此此次不會(huì)創(chuàng)建car屬性
//Person init!
print("------")
p.goOut()    // 用到car延遲屬性,此時(shí)才創(chuàng)建Car實(shí)例對(duì)象,調(diào)用Car init 方法
//Car init!
//Car is running

  • 應(yīng)用場(chǎng)景
class PhotoView {
    lazy var image: Image = {
       let url = "https://upload-images.jianshu.io/upload_images/1253159-f30fb4cd197ff37d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240"
        let data = Data(url: url)
        return Image(data: data)
    }()
}

06-延遲存儲(chǔ)屬性注意點(diǎn)

  • 當(dāng)結(jié)構(gòu)體包含一個(gè)延遲存儲(chǔ)屬性時(shí), 只有var才能訪問延遲存儲(chǔ)屬性
    • 因?yàn)?code>延遲存儲(chǔ)屬性初始化時(shí)需要改變結(jié)構(gòu)體的內(nèi)存
struct Point {
    var x = 0
    var y = 0
    lazy var z = 0
}

/*
let p = Point() // 此處用let,不能訪問延遲存儲(chǔ)屬性p.z
print(p.z)  // error: cannot use mutating getter on immutable value: 'p' is a 'let' constant
 */

var p = Point() // 此處需用var, 才能正常訪問p.z
print(p.z)

07-屬性觀察器(Property Observer)

  • willSet會(huì)傳遞新值,默認(rèn)叫newValue
  • didSet會(huì)傳遞舊值, 默認(rèn)叫oldValue
  • 初始化器中設(shè)置屬性值不會(huì)觸發(fā)willSetdidSet
  • 屬性定義時(shí)設(shè)置初始值也不會(huì)觸發(fā)willSetdidSet

  • 可以為非lazyvar 存儲(chǔ)屬性設(shè)置屬性觀察器
struct Circle {
    var radius: Double {
        willSet {
            print("willSet", newValue)
        }
        didSet {
            print("didSet", oldValue, radius)
        }
    }
    init() {
        self.radius = 1.0
        print("Circle init!")
    }
}

// Circle init!
var circle = Circle()

// willSet 10.5
// didSet 1.0 10.5
circle.radius = 10.5

// 10.5
print(circle.radius)

08-全局變量、局部變量

  • 屬性觀察器、計(jì)算屬性的功能,同樣可以應(yīng)用在全局變量、局部變量身上

  • 全局變量 計(jì)算屬性

// 全局變量 計(jì)算屬性 屬性觀察器應(yīng)用
var num: Int {
    get {
        return 10
    }
    set {
        print("setNum", newValue)
    }
}

num = 11    // setNum 11
print(num)  // 10

  • 局部變量 存儲(chǔ)屬性 屬性觀察器應(yīng)用
// 局部變量 存儲(chǔ)屬性 屬性觀察器應(yīng)用
func test() {
    
    var age = 10 {
        willSet {
            print("willSet", newValue)
        }
        didSet {
            print("didSet", oldValue, age)
        }
    }
    
    age = 11
    // willSet 11
    // didSet 10 11
}
test()

09-inout的再次研究

struct Shape {
    var width: Int
    var side: Int {
        willSet {
            print("willSetSide", newValue)
        }
        didSet {
            print("didSetSide", oldValue, side)
        }
    }
    var girth: Int {
        set {
            width = newValue / side
            print("setGirth", newValue)
        }
        get {
            print("getGirth")
            return width * side
        }
    }
    func show() {
        print("width=\(width), side=\(side), girth=\(girth)")
    }
}

func test(_ num: inout Int) {
    num = 20
}

var s = Shape(width: 10, side: 4)
test(&s.width)
s.show()
print("---------")
test(&s.side)
s.show()
print("---------")
test(&s.girth)
s.show()
// 運(yùn)行結(jié)果:
getGirth
width=20, side=4, girth=80
---------
willSetSide 20
didSetSide 4 20
getGirth
width=20, side=20, girth=400
---------
getGirth
setGirth 20
getGirth
width=1, side=20, girth=20
image.png

10-inout的本質(zhì)總結(jié)

  • 如果實(shí)參有物理內(nèi)存地址,且沒有設(shè)置屬性觀察器
    • 直接將實(shí)參的內(nèi)存地址傳入函數(shù)(實(shí)參進(jìn)行引用傳遞)
  • 如果實(shí)參是計(jì)算屬性 或者 設(shè)置了屬性觀察器
    • 采取了Copy In Copy Out的做法
      • 調(diào)用該函數(shù)是,先復(fù)制實(shí)參的值,產(chǎn)生副本 [get]
      • 副本的內(nèi)存地址傳入函數(shù)(副本進(jìn)行引用傳遞),在函數(shù)內(nèi)部可以修改副本的值
      • 函數(shù)返回后,再將副本的值覆蓋實(shí)參的值[set]
  • 總結(jié): inout的本質(zhì)就是引用傳遞(地址傳遞)

11-類型屬性(Type Property)

  • 嚴(yán)格來說, 屬性可以分為
    • 實(shí)例屬性(Instance Property): 只能通過實(shí)例去訪問
      • 存儲(chǔ)實(shí)例屬性(Stored Instance Property): 存儲(chǔ)在實(shí)例的內(nèi)存中,每個(gè)實(shí)例都有1份
      • 計(jì)算實(shí)例屬性(Computed Instance Property)
    • 類型屬性(Type Property): 只能通過類型去訪問
      • 存儲(chǔ)類型屬性(Stored Type Property): 整個(gè)程序運(yùn)行過程中,就只有1份內(nèi)存(類似于全局變量)
      • 計(jì)算類型屬性(Computed Type Property)
    • 可以通過static定義類型屬性
      • 如果是,也可以用關(guān)鍵字class

struct Car {
    static var count: Int = 0
    init() {
        Car.count += 1
        // 構(gòu)造器,類似實(shí)例方法,實(shí)例方法不能調(diào)用使用 count 或者self.count 調(diào)用類型屬性
//        count += 1  // Static member 'count' cannot be used on instance of type 'Car'
    }
}

let c1 = Car()
let c2 = Car()
let c3 = Car()
print(Car.count) // 3

12-類型屬性細(xì)節(jié)

  • 存儲(chǔ)類型屬性默認(rèn)就是lazy, 會(huì)在第一次使用的時(shí)候才初始化
    • 就算被多個(gè)線程同時(shí)訪問,保證只會(huì)初始化一次(線程安全)
    • 存儲(chǔ)類型屬性可以是let

  • 枚舉類型也可以定義類型屬性(存儲(chǔ)類型屬性、計(jì)算類型屬性)
enum TestEnum : Int {
    static var count: Int = 0   // 存儲(chǔ)類型屬性、計(jì)算類
    case test1 = 1, test2 = 2
    func plus() {
        TestEnum.count += 1
    }
    static func getCount() -> Int { // 計(jì)算類型屬性
        return count
    }
}

var t1 = TestEnum.test1
t1.plus()
print(TestEnum.count)
t1.plus()
print(TestEnum.count)
t1.plus()
print(TestEnum.count)
print(TestEnum.getCount())

13-單例模式

public class FileManager {
    public static let shared = FileManager()    // 默認(rèn)是lazy 就算被多個(gè)線程同時(shí)訪問,保證`只會(huì)初始化一次`(線程安全)
    private init() {  }
}
public class FileManager {
    public static let shared = { // 默認(rèn)是lazy 就算被多個(gè)線程同時(shí)訪問,保證`只會(huì)初始化一次`(線程安全)
        // do something
        return FileManager()
    }()
    private init() {  }
}

let manager = FileManager.shared

iOS Swift 語(yǔ)法 底層原理內(nèi)存管理分析 專題:【iOS Swift5語(yǔ)法】

下一篇: 09 - 方法
上一篇: 07 - 閉包


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請(qǐng)通過簡(jiǎn)信或評(píng)論聯(lián)系作者。