08-屬性

屬性

Swift中跟實(shí)例相關(guān)的屬性可以分為2大類

存儲屬性(Stored Property)

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

計(jì)算屬性(Computed Property)

\color{green}{??}本質(zhì)就是方法(函數(shù))
\color{green}{??}不占用實(shí)例的內(nèi)存
\color{green}{??}枚舉、結(jié)構(gòu)體、類都可以定義計(jì)算屬性

struct Circle {
    // 存儲屬性
    var radius: Double

    // 計(jì)算屬性
    var diameter: Double {
        set {
           radius = newValue / 2                                                              
        }
        get {                                                            
            radius * 2 // 省了return
        }                                                               
    }
}

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
// 本質(zhì)就是方法,接下來看匯編推導(dǎo)    
//    func setDiameter(newValue: Double) {
//        radius = newValue / 2  
//    }

//    func getDiameter() -> Double {
//        radius * 2
//    }
推導(dǎo)過程:
  • 先看下17行radius的匯編:
struct Circle {
    // 存儲屬性
    var radius: Int // 這里改成int,方便查看匯編

    // 計(jì)算屬性
    var diameter: Int {
        set {
           radius = newValue / 2                                                              
        }
        get {                                                            
            radius * 2 // 省了return
        }                                                               
    }
}

var c = Circle(radius: 10)
c.radius = 11
c.diameter = 12
var d = c.diameter
image.png

17 行:radius 存儲在結(jié)構(gòu)體的內(nèi)存里面。

  • 看下diameter的匯編
image.png
image.png

調(diào)用的是一個(gè)setter方法。

image.png

19行:調(diào)用的是一個(gè)getter方法

存儲屬性

\color{green}{*} 關(guān)于存儲屬性,Swift有個(gè)明確的規(guī)定

\color{green}{*} 在創(chuàng)建類 或 結(jié)構(gòu)體的實(shí)例時(shí),必須為所有的存儲屬性設(shè)置一個(gè)合適的初始值

\color{green}{??}可以在初始化器里為存儲屬性設(shè)置一個(gè)初始值
\color{green}{??}可以分配一個(gè)默認(rèn)的屬性值作為屬性定義的一部分

計(jì)算屬性

\color{green}{*} set傳入的新值默認(rèn)叫做newValue,也可以自定義

struct Circle {
    var radius: Double
    var diameter: Double {
        set(newDiameter) {
            radius = newDiameter / 2                                                       
        }
        get {                                                        
            radius * 2
        }                                                        
    }
}

\color{green}{*} 只讀計(jì)算屬性:只有g(shù)et,沒有set

struct Circle {
    var radius: Double
    var diameter: Double {
        get {
            radius * 2                                                          
        }
    }                                                                
}

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

枚舉rawValue

\color{green}{*} 枚舉原始值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

延遲存儲屬性(Lazy Stored Property)

\color{green}{*} 使用lazy可以定義一個(gè)延遲存儲屬性,在第一次用到屬性的時(shí)候才會(huì)進(jìn)行初始化

\color{green}{*} lazy屬性必須是var,不能是let

\color{green}{*} let必須在實(shí)例的初始化方法完成之前就擁有值

  • 如果多條****線程同時(shí)第一次訪問lazy屬性

    • 無法保證屬性只被初始化1次
class Car {
    init() {
        print("Car init!")
    }
    func run() {
        print("Car is running!")                                                              
    }
}
class Person {
    lazy var car = Car()
    init() {
        print("Person init!")
    }
    func goOut() {
        car.run()                                                           
    }
}
let p = Person()
print("--------")
p.goOut()

Person init!
--------
Car init!
Car is running!

class PhotoView {
    lazy var image: Image = {                                                                
        let url = "https://www.520it.com/xx.png"
        let data = Data(url: url)
        return Image(data: data)                                                        
    }() // 閉包
}

可以理解為一個(gè)函數(shù)調(diào)用返回值

image.png

延遲存儲屬性注意點(diǎn)

  • 當(dāng)結(jié)構(gòu)體包含一個(gè)延遲存儲屬性時(shí),只有var才能訪問延遲存儲屬性

    • 因?yàn)檠舆t屬性初始化時(shí)需要改變結(jié)構(gòu)體的內(nèi)存
struct Point {
    var x = 0
    var y = 0
    lazy var z = 0
}

let p = Point()
print(p.z)     Cannot use mutating getter on immutable value: 'p' is a 'let' constant

屬性觀察器 (Property Observer)

  • 可以為非lazy的var存儲屬性設(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)
  • willSet會(huì)傳遞新值,默認(rèn)叫newValue

  • didSet會(huì)傳遞舊值,默認(rèn)叫oldValue

  • 在初始化器中設(shè)置屬性值不會(huì)觸發(fā)willSet和didSet

  • 在屬性定義時(shí)設(shè)置初始值也不會(huì)觸發(fā)willSet和didSet

全局變量、局部變量

  • 屬性觀察器、計(jì)算屬性的功能,同樣可以應(yīng)用在全局變量、局部變量身上
var num: Int {
    get {                                                          
        return 10
    }
    set {
        print("setNum", newValue)                                                        
    }
}

num = 11     // setNum 11
print(num)   // 10
func test() {
    var age = 10 {
        willSet {
            print("willSet", newValue)                                                           
        }
        didSet {
            print("didSet", oldValue, age)
        }
    }
    age = 11
    // willSet 11
    // didSet 10 11                                                          
}
test()

inout的再次研究

struct Shape {

    // 存儲屬性
    var width: Int
    var side: Int {
        willSet {                                                  
            print("willSetSide", newValue) 
        }                                                                  
        didSet {
            print("didSetSide", oldValue, side)                                                         
        } 
    } 

    // 計(jì)算屬性
    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)
test(&s.side)
test(&s.girth)

test(&s.width)
s.show()
print("----------")
test(&s.side)
s.show()
print("----------")
test(&s.girth)
s.show()
// log如下
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 
推動(dòng)過程:
  • 先看下width存儲屬性
image.png

s.width: s結(jié)構(gòu)體變量的地址值,傳遞給了test函數(shù),因?yàn)閣idth是存儲屬性,存在結(jié)構(gòu)體里,而且又是第一個(gè),第一個(gè)屬性的內(nèi)存地址值和結(jié)構(gòu)體內(nèi)存的地址值是重疊的

  • 看下girth
// log輸出
getGirth
test
setGirth 20 
image.png
image.png

調(diào)用test之前先調(diào)用get, get方法值放在局部變量去 20

局部變量內(nèi)存?zhèn)鬟f到 test

20 覆蓋掉局部變量

在調(diào)set

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

  • 如果實(shí)參有物理內(nèi)存地址,且沒有設(shè)置屬性觀察器

    • 直接將實(shí)參的內(nèi)存地址傳入函數(shù)(實(shí)參進(jìn)行引用傳遞)
  • 如果實(shí)參是計(jì)算屬性 或者 設(shè)置了屬性觀察器

    • 采取了Copy In Copy Out的做法

    ?? 調(diào)用該函數(shù)時(shí),先復(fù)制實(shí)參的值,產(chǎn)生副本****【get】

    ?? 將副本的內(nèi)存地址傳入函數(shù)(副本進(jìn)行引用傳遞),在函數(shù)內(nèi)部可以修改副本的值

    ?? 函數(shù)返回后,再將副本的值覆蓋實(shí)參的值****【set】

  • 總結(jié):inout的本質(zhì)就是引用傳遞(地址傳遞)

類型屬性 (Type Property)

嚴(yán)格來說,屬性可以分為

  • 實(shí)例屬性(Instance Property):只能通過實(shí)例去訪問

    ?? 存儲實(shí)例屬性(Stored Instance Property):存儲在實(shí)例的內(nèi)存中,每個(gè)實(shí)例都有1份

    ?? 計(jì)算實(shí)例屬性(Computed Instance Property)

  • 類型屬性(Type Property):只能通過類型去訪問

    ?? 存儲類型屬性(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
    }                                                          
}

let c1 = Car()
let c2 = Car()
let c3 = Car()
print(Car.count) // 3
推導(dǎo)過程:

1、先看一下如下代碼的匯編本質(zhì)

var num1 = 10
var num2 = 11
var num3 = 12
image.png
image.png

2、把car放在中間查看匯編代碼本質(zhì)

var num1 = 10
Class Car {
    static var count = 0
}
Car.count = 11
var num3 = 12
image.png
image.png
image.png

Lazy 第一次用到才會(huì)使用:

image.png
image.png

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

  • 不同于存儲實(shí)例屬性,你必須給存儲類型屬性設(shè)定初始值

因?yàn)轭愋蜎]有像實(shí)例那樣的init初始化器來初始化存儲屬性

  • 存儲類型屬性默認(rèn)就是lazy,會(huì)在第一次使用的時(shí)候才初始化

就算被多個(gè)線程同時(shí)訪問,保證只會(huì)初始化一次

  • 存儲類型屬性可以是let

  • 枚舉類型也可以定義類型屬性(存儲類型屬性、計(jì)算類型屬性)

單例模式

public class FileManager {
    public static let shared = FileManager()
    private init () {}
}
public class FileManager {
    public static let shared = {
        // ...
        // ...
        return  FileManager()
    }()
    private init () {}
}
?著作權(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)容

  • 屬性 存儲屬性 計(jì)算屬性 注:不能只有set 枚舉rawValue原理 延遲存儲屬性(Lazy Stored Pr...
    SwordDevil閱讀 224評論 0 0
  • 屬性分類 在Swift中, 嚴(yán)格意義上來講屬性可以分為兩大類: 實(shí)例屬性和類型屬性 實(shí)例屬性(Instance P...
    晚晚_e65f閱讀 216評論 0 0
  • 作者: Liwx 郵箱: 1032282633@qq.com 源碼: 需要源碼的同學(xué), 可以在評論區(qū)留下您的郵箱 ...
    Liwx閱讀 1,729評論 3 11
  • 1. 存儲屬性&計(jì)算屬性 Swift中跟實(shí)例對象相關(guān)的屬性可以分為2大類 存儲屬性(Stored Property...
    happy神悅閱讀 193評論 0 0
  • swift中跟實(shí)例相關(guān)的屬性可以分為2大類。1. 存儲屬性(Stored Property)1.1 相當(dāng)于成員變量...
    翀鷹精靈閱讀 1,161評論 0 3

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