本篇將詳細(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ì)算。