Swift5 基礎(chǔ)(三)屬性、方法、下標(biāo)

Swift5 基礎(chǔ)教程與進(jìn)階合集

一. 屬性

Swift中跟實(shí)例相關(guān)的屬性可以分為兩大類:
存儲(chǔ)屬性(Stored Property)

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

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

  • 本質(zhì)就是方法(函數(shù))
  • 不占用實(shí)例的內(nèi)存
  • 枚舉、結(jié)構(gòu)體、類都可以定義計(jì)算屬性
struct Circle{
    //存儲(chǔ)屬性
    var radius: Double
    //計(jì)算屬性
    var diameter: Double{
        set {
            radius = newValue/2
        }get{
            radius * 2
        }
    }
}

存儲(chǔ)屬性

關(guān)于存儲(chǔ)屬性,Swift有個(gè)明確的規(guī)定
在創(chuàng)建類或結(jié)構(gòu)體的實(shí)例時(shí),必須為所有的存儲(chǔ)屬性設(shè)置一個(gè)合適的初始值

  • 可以在初始化器里為存儲(chǔ)屬性設(shè)置一個(gè)初始值
  • 可以分配一個(gè)默認(rèn)的屬性值作為屬性定義的一部分

計(jì)算屬性

  • set傳入的新值默認(rèn)叫做newValue,也可以自定義
struct Circle{
    var radius: Double
    var diameter: Double{
        set (newDiamater){
            radius = newDiamater/2
        }get{
            radius * 2
        }
    }
}
  • 定義計(jì)算屬性只能用var,不能用let

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

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

//只有g(shù)et的話可以省略get關(guān)鍵字
struct Circle{
    var radius: Double
    var diameter: Double{
        radius * 2
    }
}

枚舉rawValue的原理

枚舉原始值rawValue的本質(zhì)是:只讀計(jì)算屬性

enum TestEnum : Int {
    case test1 = 1,test2,test3
    //這里對(duì)rawValue進(jìn)行了重寫
    var rawValue: Int{
        switch self {
        case .test1:
            return 10
        case .test2:
            return 11
        case .test3:
            return 12
        }
    }
}
print(TestEnum.test3.rawValue)//12

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

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

  • lazy屬性必須是var,不能是let,因?yàn)閘et必須在實(shí)例的初始化方法完成之前就擁有值
  • 如果多條線程同時(shí)第一次訪問lazy屬性,無法保證屬性只被初始化一次
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()
p.goOut()
//打印結(jié)果
//Person init
//Car init
//Car is running

當(dāng)結(jié)構(gòu)體包含一個(gè)延遲存儲(chǔ)屬性時(shí),只有var才能訪問延遲存儲(chǔ)屬性。因?yàn)檠舆t屬性初始化時(shí)需要改變結(jié)構(gòu)體的內(nèi)存

struct Point{
    var x = 0
    var y = 0
    lazy var z = 0
}
//let的p無法訪問到延遲存儲(chǔ)屬性
//let p = Point()
//print(p.z)
var p = Point()
print(p.z)//0

屬性觀察器

可以為非lazyvar存儲(chǔ)屬性設(shè)置屬性觀察器

struct Circle{
    var radius: Double{
        willSet{
            print("willSet",newValue)
        }
        didSet{
            print("didSet",oldValue)
        }
    }
    init() {
        radius = 1.0
        print("Circle init")
    }
}
var circle = Circle()
circle.radius = 2.5
print(circle.radius)
//打印結(jié)果
//Circle init
//willSet 2.5
//didSet 1.0
//2.5
  • willSet會(huì)傳遞新值,默認(rèn)叫newValue,也可以自定義
  • didSet會(huì)傳遞舊值,默認(rèn)叫oldValue,也可以自定義
  • 初始化器中設(shè)置屬性值不會(huì)觸發(fā)willSet和didSet
  • 屬性定義時(shí)設(shè)置初始值也不會(huì)觸發(fā)willSet和didSet

全局變量、局部變量

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

//全局計(jì)算屬性
var num: Int{
    get{
        10
    }
    set{
        print("setNum",newValue)
    }
}
num = 22//setNum 22
print(num)//10 由于計(jì)算屬性不具有存儲(chǔ)功能,之前設(shè)置的值沒有保存,所以還是直接拿的get返回的值

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

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()
//打印結(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

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

  • 如果實(shí)參有物理內(nèi)存地址,且沒有設(shè)置屬性觀察值,直接將實(shí)參的內(nèi)存地址傳入函數(shù)(實(shí)參進(jìn)行引用傳遞)
  • 如果實(shí)參是計(jì)算屬性或者設(shè)置了屬性觀察值,采用了Copy In Copy Out的做法,即:

??1.調(diào)用該函數(shù)時(shí),先復(fù)制實(shí)參的值,產(chǎn)生副本(get)
??2.將副本的內(nèi)存地址傳入函數(shù)(副本進(jìn)行引用傳遞),在函數(shù)內(nèi)部可以修改副本的值
??3.函數(shù)返回后,再將副本的值覆蓋實(shí)參的值(set)

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

類型屬性(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
    }
}
let c1 = Car()
let c2 = Car()
let c3 = Car()
print(Car.count)//3

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

  1. 不同于存儲(chǔ)實(shí)例屬性,必須給存儲(chǔ)類型屬性設(shè)置初始值,因?yàn)轭愋蜎]有像實(shí)例那樣的init初始化器來初始化存儲(chǔ)屬性
  2. 存儲(chǔ)類型屬性默認(rèn)就是lazy,會(huì)在第一次使用的時(shí)候才初始化,就算被多個(gè)線程同時(shí)訪問,保證只會(huì)初始化一次;存儲(chǔ)類型屬性可以是let
  3. 枚舉類型也可以定義類型屬性(存儲(chǔ)類型屬性、計(jì)算類型屬性)

單例模式

public class FileManager{
    public static let shared = FileManager()
    private init(){}
}
public class FileManager{
    public static let shared = {
        FileManager()
    }()
    private init(){}
}

二.方法

方法(Method)

  • 枚舉、結(jié)構(gòu)體、類都可以定義實(shí)例方法、類型方法
  • 實(shí)例方法(Instance Method):通過實(shí)例對(duì)象調(diào)用
  • 類型方法(Type Method):通過類型調(diào)用,用static或者class關(guān)鍵字定義
struct Car{
    static var count: Int = 0
    init() {
        Car.count += 1
    }
    //大括號(hào)中的count等價(jià)于self.count,Car.self.count,Car.count
    static func getCount() -> Int { count }
}
let c1 = Car()
let c2 = Car()
let c3 = Car()
print(Car.getCount())//3

self:

  • 在實(shí)例方法中代表實(shí)例對(duì)象
  • 在類型方法中代表類型

mutating

結(jié)構(gòu)體和枚舉是值類型,默認(rèn)情況下,值類型的屬性不能被自身的實(shí)例方法修改
在func關(guān)鍵字前面加mutating可以允許這種修改行為

struct Point{
    var x = 0.0
    var y = 0.0
    mutating func moveBy(deltaX: Double,deletaY: Double){
        x += deltaX
        y += deletaY
    }
}

enum StateSwitch{
    case low,middle,high
    mutating func next(){
        switch self {
        case .low:
            self = .middle
        case .middle:
            self = .high
        case .high:
            self = .low
        }
    }
}

@discardableResult

在func前面加個(gè)@discardableResult,可以消除:函數(shù)調(diào)用后返回值未被使用的警告

struct Point{
    var x = 0.0
    var y = 0.0
    @discardableResult mutating func moveBy(deltaX: Double,deletaY: Double) -> Point{
        x += deltaX
        y += deletaY
        return self
    }
}

var p = Point()
p.moveBy(deltaX: 0.4, deletaY: 0.7)


@discardableResult func get() -> Int{
    return 10
}
get()

三. 下標(biāo)

下標(biāo)(subscript)

使用subscript可以給任意類型(枚舉、結(jié)構(gòu)體、類)增加下標(biāo)功能,有些地方也翻譯為:下標(biāo)腳本
subscript的語(yǔ)法類似于實(shí)例方法、計(jì)算屬性,本質(zhì)就是方法(函數(shù))

class Point{
    var x = 0.0
    var y = 0.0
    subscript(index: Int) -> Double{
        set {
            if index == 0 {
                x = newValue
            } else if index == 1 {
                y = newValue
            }
        }
        get{
            if index == 0 {
                return x
            } else if index == 1 {
                return y
            }
            return 0
        }
    }
}

var p = Point()
p[0] = 1.1
p[1] = 2.2
print("p.x=\(p.x),p.y=\(p.y),p[0]=\(p[0]),p[1]=\(p[1])")
//打印結(jié)果
//p.x=1.1,p.y=2.2,p[0]=1.1,p[1]=2.2
  • subscript中定義的返回值類型決定了

??get方法的返回值類型
??set方法中newValue的類型

  • subscript可以接受多個(gè)參數(shù),并且類型任意

  • subscript可以沒有set方法,但必須有g(shù)et方法,如果只有g(shù)et方法,可以省略get

class Point{
    var x = 0.0
    var y = 0.0
    subscript(index: Int) -> Double{
        if index == 0 {
            return x
        } else if index == 1 {
            return y
        }
        return 0
    }
}
  • 可以設(shè)置參數(shù)標(biāo)簽
class Point{
    var x = 0.0
    var y = 0.0
    subscript(index i: Int) -> Double{
        if i == 0 {
            return x
        } else if i == 1 {
            return y
        }
        return 0
    }
}
var p = Point()
p.y = 2.2
print(p[index: 1])//2.2
  • 下標(biāo)可以是類型方法
class Sum{
    static subscript(v1: Int,v2: Int) -> Int{
        v1 + v2
    }
}
print(Sum[10,20])//30

結(jié)構(gòu)體、類作為返回值

class Point{
    var x = 0.0
    var y = 0.0
    
}
class PointManager {
    var point = Point()
    subscript(index: Int) -> Point{
        point
    }
}
var pm = PointManager()
pm[0].x = 11
pm[0].y = 22
print(pm[0])//__lldb_expr_26.Point
print(pm.point)//__lldb_expr_26.Point
struct Point{
    var x = 0.0
    var y = 0.0
    
}
class PointManager {
    var point = Point()
    subscript(index: Int) -> Point{
        set { point = newValue }
        get { point }
    }
}
var pm = PointManager()
pm[0].x = 11
pm[0].y = 22
print(pm[0])//Point(x: 11.0, y: 22.0)
print(pm.point)//Point(x: 11.0, y: 22.0)

接收多個(gè)參數(shù)的下標(biāo)

class Grid{
    var data = [
        [0,1,2],
        [3,4,5],
        [6,7,8]
    ]
    subscript(row: Int, column: Int) -> Int{
        set{
            guard row >= 0 && row < 3 && column >= 0 && column < 3 else {
                return
            }
            data[row][column] = newValue
        } get {
            guard row >= 0 && row < 3 && column >= 0 && column < 3 else {
                return 0
            }
            return data[row][column]
        }
    }
}
var grid = Grid()
grid[0,1] = 77
grid[1,2] = 88
grid[2,0] = 99
print(grid.data)//[[0, 77, 2], [3, 4, 88], [99, 7, 8]]
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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