Swift學(xué)習(xí):屬性

本篇將詳細(xì)總結(jié)介紹Swift屬性的用法;
屬性是與特定的Swift類、結(jié)構(gòu)體、枚舉相關(guān)聯(lián)的值;與其他語言相比,屬性不再是被類所特有。

主要內(nèi)容:
1.存儲(chǔ)屬性與計(jì)算屬性
2.屬性觀察器
3.類型屬性
4.全局變量與局部變量

一、存儲(chǔ)屬性與計(jì)算屬性

從屬性被定義的方式上看,Swift屬性有存儲(chǔ)屬性和計(jì)算屬性兩種:
存儲(chǔ)屬性:存儲(chǔ)在特定類或結(jié)構(gòu)體實(shí)例里的一個(gè)常量(let)或變量(var),作為實(shí)例的一部分;

計(jì)算屬性:計(jì)算屬性不直接存儲(chǔ)值,而是提供一個(gè)getter和一個(gè)可選的setter,來間接設(shè)置其他屬性或變量值;
下面通過一段代碼演示這兩種屬性的區(qū)別:

struct Square{
    //存儲(chǔ)屬性
    var width:Double
    
    //計(jì)算屬性:通過一定計(jì)算方法得到的屬性
    var area:Double{
        get{
            return width * width
        }
    }
}
let square = Square(width: 10.0)
print("正方形邊長:\(square.width)")     //正方形邊長:10.0
print("正方形面積:\(square.area)")      //正方形面積:100.0

總結(jié)存儲(chǔ)屬性和計(jì)算屬性的用法還有如下幾種情況:

1.1.常量結(jié)構(gòu)體的存儲(chǔ)屬性

如果創(chuàng)建一個(gè)結(jié)構(gòu)體的實(shí)例并且將其賦值給一個(gè)常量,則無法再修改該實(shí)例的任何屬性(包括其中的變量屬性)。這是因?yàn)榻Y(jié)構(gòu)體是值類型,值類型實(shí)例被聲明為常量,其所有屬性都成了常量;在這點(diǎn)上,類與結(jié)構(gòu)體不同,這種情況下,類中的可變屬性可以被修改。

let square1 = Square(width: 10.0)
//square1.width = 11.0      //報(bào)錯(cuò)
var square2 = Square(width:20.0)
square2.width = 21.0      //可以修改

//注:如果Squre是一個(gè)類,那么以上兩種情況都可以通過

1.2.延遲存儲(chǔ)屬性

延遲存儲(chǔ)屬性:第一次被調(diào)用的時(shí)候才會(huì)計(jì)算其初始值的屬性。在屬性聲明前使用lazy來表示一個(gè)延遲存儲(chǔ)屬性。
延遲屬性作用:當(dāng)屬性的值依賴于在實(shí)例的構(gòu)造過程結(jié)束后才會(huì)知道影響值的外部因素時(shí),或者當(dāng)獲得屬性的初始值需要復(fù)雜或大量計(jì)算時(shí),可以只在需要的時(shí)候計(jì)算它。

class Number{
    //存儲(chǔ)屬性
    var startNum: Int!
    var endNum:Int!
    //計(jì)算屬性
    var length :Int{
        return endNum - startNum + 1
    }
    
    //延遲屬性:使用閉包計(jì)算出了延遲屬性的值,此過程只執(zhí)行一次
    lazy var sum: Int = {
        print("計(jì)算延遲屬性。。。。")
        var tempNum = 0;
        for i in self.startNum...self.endNum{
            tempNum += i;
        }
        return tempNum
    }()
    
    //可失敗的構(gòu)造方法
    init?(startNum: Int , endNum:Int){
        if(startNum > endNum){
            return nil
        }
        self.startNum = startNum
        self.endNum = endNum
    }
}

let number = Number(startNum: 1, endNum: 100)
number?.length  //100
number?.sum    //5050
number?.sum    //5050

注意
1.必須將延遲存儲(chǔ)屬性聲明成變量(使用var關(guān)鍵字),因?yàn)閷傩缘某跏贾悼赡茉趯?shí)例構(gòu)造完成之后才會(huì)得到。而常量屬性在構(gòu)造過程完成之前必須要有初始值,因此無法聲明成延遲屬性。
2.如果一個(gè)被標(biāo)記為 lazy 的屬性在沒有初始化時(shí)就同時(shí)被多個(gè)線程訪問,則無法保證該屬性只會(huì)被初始化一次。

1.3.計(jì)算屬性的使用

計(jì)算屬性不直接存儲(chǔ)值,而是提供一個(gè)getter和一個(gè)可選的setter,來間接設(shè)置其他屬性或變量值;總結(jié)它的使用特點(diǎn)如下:
1.只有g(shù)etter沒有setter的計(jì)算屬性就是只讀計(jì)算屬性。只讀屬性通過點(diǎn)運(yùn)算符訪問,只能返回值而不可設(shè)置新值;
2.計(jì)算屬性與其他屬性相關(guān),是變化的,所以必須使用var關(guān)鍵字進(jìn)行修飾,包括只讀計(jì)算屬性;
3.只讀計(jì)算屬性可以去掉get關(guān)鍵字和花括號(hào);

struct Point {
    var x = 0.0
    var y = 0.0
}
struct Size {
    var width = 0.0
    var height = 0.0
}
class Rectangle{
    //存儲(chǔ)型數(shù)據(jù)
    var originPoint  = Point()
    var size = Size()
    
    //計(jì)算型屬性
    var center:Point{
        //get方法:獲取計(jì)算屬性值
        get{
            let center_x = originPoint.x + size.width/2
            let center_y = originPoint.y + size.height/2
            return Point(x: center_x, y: center_y)
        }
        
        //如果沒有set方法,是只讀,
        /*
         set(newCenter){
            originPoint.x = newCenter.x - size.width/2
            originPoint.y = newCenter.y - size.height/2
         }
         */
        
        //set方法:設(shè)置計(jì)算屬性新值
        //這里也可以省略括號(hào)和newCenter.使用newValue
        set{
            originPoint.x = newValue.x - size.width/2
            originPoint.y = newValue.y - size.height/2
        }
    }
    
    //計(jì)算屬性:area屬性只有g(shù)et,可以不顯式的聲明出get;此屬性為只讀屬性
    var area:Double{
        return size.width * size.height
    }
    
    init(origin: Point, size: Size){
        self.originPoint  = origin
        self.size  = size
    }
}
//創(chuàng)建一個(gè)長方形
var rect = Rectangle(origin: Point(x: 0, y: 0), size: Size(width: 100, height: 100))
rect.center
rect.area  //10000

二、屬性觀察器

屬性觀察器監(jiān)控和響應(yīng)屬性值的變化,每次屬性被設(shè)置值的時(shí)候都會(huì)調(diào)用屬性觀察器,即使新值和當(dāng)前值相同的時(shí)候也不例外。

屬性觀察器可以為延遲屬性外的其他存儲(chǔ)屬性添加屬性觀察,也可以通過繼承的方式重寫父類屬性,為其添加屬性觀察期。但是我們沒有必要為非重寫的計(jì)算屬性添加屬性觀察器,因?yàn)樗旧砭涂梢酝ㄟ^自己的setter直接監(jiān)控和響應(yīng)值的變化。

添加屬性觀察器方式如下:
willSet方法:
在新的值被設(shè)置之前調(diào)用,擁有一個(gè)默認(rèn)參數(shù)newValue(代表新的屬性值);
didSet方法:
在新的值被設(shè)置之后立刻調(diào)用,擁有一個(gè)默認(rèn)參數(shù)oldValue(代表就的屬性值);

下面通過lightBlub演示用法,其中為currentDianYa屬性添加了觀察器:

class lightBlub {
    //最大電壓和當(dāng)前電壓
    static let maxDianYa = 30
    
    //屬性監(jiān)聽
    //注意:willSet和didSet括號(hào)中的值可以省略,直接使用系統(tǒng)自帶的newVlaue和oldValue
    var currentDianYa = 0 {
        //可以使用系統(tǒng)默認(rèn)的屬性newValue和oldValue
        willSet(newCurrentDianya){
            print("當(dāng)前電壓值將要改變:  \(currentDianYa) -> \(newCurrentDianya)")
        }
        
        //當(dāng)調(diào)用此方法時(shí),已經(jīng)設(shè)置了值的時(shí)候,
        didSet(oldCurentDianYa){
            if(currentDianYa == lightBlub.maxDianYa){
                print("請(qǐng)注意 ,當(dāng)前電壓達(dá)到了最大電壓值")
            }else if(self.currentDianYa > lightBlub.maxDianYa){
                print("當(dāng)前電壓過高,不能設(shè)置新的電壓值")
                currentDianYa = oldCurentDianYa
            }
        }
    }
}
let light = lightBlub()
light.currentDianYa = 10
light.currentDianYa = 30
light.currentDianYa = 40
/*
 打印結(jié)果:
 當(dāng)前電壓值將要改變:  0 -> 10
 當(dāng)前電壓值將要改變:  10 -> 30
 請(qǐng)注意 ,當(dāng)前電壓達(dá)到了最大電壓值
 當(dāng)前電壓值將要改變:  30 -> 40
 當(dāng)前電壓過高,不能設(shè)置新的電壓值
*/

注意:willSet和didSet并不會(huì)在初始化時(shí)被調(diào)用

三、類型屬性

實(shí)例屬性屬于一個(gè)特定類型的實(shí)例,因此實(shí)例之間的屬性相互獨(dú)立。但其實(shí),也可以為類型本身定義屬性,這樣無論創(chuàng)建了多少個(gè)該類型實(shí)例,這些屬性只有唯一的一份,這種屬性就是類型屬性

Swift的類型屬性就相當(dāng)于OC或者C中的類變量,但他們有著以下的不同:
在OC或者C中,與某個(gè)類型相關(guān)的靜態(tài)常量和靜態(tài)變量,是作為全局靜態(tài)變量來定義的。但是Swift中,類型屬性是作為類型定義的一部分寫在類型最外層的花括號(hào)內(nèi),因此它的作用范圍也就在類型支持的范圍內(nèi)。

Swift類型屬性使用關(guān)鍵字static,下面是一個(gè)具體示例:

//測試Int的類型屬性
Int.min 
Int.max

class Player {
    var name: String = ""
    //對(duì)象屬性:本人的得分
    var score: Int = 0
    //類型屬性:本游戲的最高得分,使用類名來訪問,使用關(guān)鍵字static聲明
    static var heighestScore:Int = 0;
    
    //構(gòu)造方法
    init(name: String){
        self.name = name
    }
    //玩一句游戲得分
    func playGame(){
        let tempNum = Int(arc4random()%100)+1
        self.score += tempNum
        print("\(name) 的游戲得分是:\(score)")
        
        if(self.score > Player.heighestScore){
            Player.heighestScore = self.score
        }
        print("當(dāng)前本游戲的最高分是:\(Player.heighestScore)")
    }
}
let player1 = Player(name: "zs")
player1.playGame()
player1.playGame()
let player2 = Player(name: "cf")
player2.playGame()
/*
 打印結(jié)果
 zs 的游戲得分是:11
 當(dāng)前本游戲的最高分是:11
 zs 的游戲得分是:87
 當(dāng)前本游戲的最高分是:87
 
 cf 的游戲得分是:88
 當(dāng)前本游戲的最高分是:88
*/

四、全局變量與局部變量

全局變量:在函數(shù)、方法、閉包或者任意類型之外定義的變量
局部變量:在函數(shù)、方法或者閉包內(nèi)部定義的變量

全局的常量或變量都是延遲計(jì)算的,跟延遲存儲(chǔ)屬性相似,不同的地方在于:全局的常量或變量不需要標(biāo)記lazy修飾符。

局部范圍的常量或變量從不延遲計(jì)算。

最后編輯于
?著作權(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),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請(qǐng)通過簡信或評(píng)論聯(lián)系作者。

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

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