【Swift 3.1】10 - 屬性 (Properties)

屬性 (Properties)

自從蘋(píng)果2014年發(fā)布Swift,到現(xiàn)在已經(jīng)兩年多了摸屠,而Swift也來(lái)到了3.1版本。去年利用工作之余,共花了兩個(gè)多月的時(shí)間把官方的Swift編程指南看完』钅妫現(xiàn)在整理一下筆記,回顧一下以前的知識(shí)拗胜。有需要的同學(xué)可以去看官方文檔>>蔗候。


存儲(chǔ)屬性作為實(shí)例的一部分,存在常量和變量的值埂软,而計(jì)算屬性是計(jì)算一個(gè)值锈遥,而不是存儲(chǔ)一個(gè)值。計(jì)算屬性由類(lèi)勘畔、結(jié)構(gòu)和枚舉提供所灸;而存儲(chǔ)屬性只能由類(lèi)和結(jié)構(gòu)提供。

存儲(chǔ)和計(jì)算屬性通常有關(guān)聯(lián)著一個(gè)實(shí)例咖杂。然而庆寺,屬性可以關(guān)聯(lián)類(lèi)型。

另外诉字,可以定義屬性觀察者來(lái)監(jiān)測(cè)屬性值的變化。屬性觀察者可以添加到自己定義的存儲(chǔ)屬性中知纷,也可以添加到從父類(lèi)繼承的屬性中壤圃。

存儲(chǔ)屬性 (Stored Properties)

struct FixedLengthRange {
    var firstValue: Int
    let length: Int
}
var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)
// the range represents integer values 0, 1, and 2
rangeOfThreeItems.firstValue = 6
// the range now represents integer values 6, 7, and 8

firstValue是變量存儲(chǔ)屬性,length是常量存儲(chǔ)屬性琅轧。

常量結(jié)構(gòu)實(shí)例的存儲(chǔ)屬性 (Stored Properties of Constant Structure Instances)

如果創(chuàng)建一個(gè)結(jié)構(gòu)實(shí)例伍绳,并賦給一個(gè)常量,那么就不能修改實(shí)例的屬性乍桂,即使這個(gè)屬性是一個(gè)變量屬性:

let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
// this range represents integer values 0, 1, 2, and 3
rangeOfFourItems.firstValue = 6
// this will report an error, even though firstValue is a variable property

因?yàn)?code>rangeOfFourItems被聲明為常量冲杀,所以不能修改firstValue屬性,即使firstValue是變量屬性睹酌。

出現(xiàn)這種現(xiàn)象权谁,其實(shí)是因?yàn)榻Y(jié)構(gòu)是值類(lèi)型。只要實(shí)例是常量憋沿,那么它的屬性也都是常量旺芽。

而對(duì)于引用類(lèi)型的類(lèi)而言,把一個(gè)引用類(lèi)型的實(shí)例賦值給一個(gè)常量,我們?nèi)稳豢梢孕薷膶?shí)例的變量屬性采章。

懶惰存儲(chǔ)屬性 (Lazy Stored Properties)

一個(gè)屬性在第一次使用之前运嗜,他的初始值沒(méi)有被計(jì)算,那么這個(gè)屬性稱(chēng)為懶惰存儲(chǔ)屬性悯舟。

注意:懶惰存儲(chǔ)屬性必須聲明為一個(gè)變量担租,因?yàn)閷?shí)例初始化完成后,懶惰屬性可能還沒(méi)被獲取抵怎。常量屬性在實(shí)例初始化完成之前必須有一個(gè)值翩活,所有不能聲明為懶惰屬性。

當(dāng)一個(gè)屬性的初始值依賴(lài)于外部因素便贵,而外部因素的值需要在實(shí)例初始化完成之后才能確定菠镇,那么把這個(gè)屬性聲明為懶惰屬性非常有用。當(dāng)一個(gè)屬性的初始值需要很復(fù)雜的計(jì)算時(shí)承璃,懶惰屬性也非常有用利耍。

下面這個(gè)例子使用懶惰屬性來(lái)避免不必要的一個(gè)復(fù)雜類(lèi)的初始化。

class DataImporter {
    /*
     DataImporter is a class to import data from an external file.
     The class is assumed to take a non-trivial amount of time to initialize.
     */
    var fileName = "data.txt"
    // the DataImporter class would provide data importing functionality here
}
 
class DataManager {
    lazy var importer = DataImporter()
    var data = [String]()
    // the DataManager class would provide data management functionality here
}
 
let manager = DataManager()
manager.data.append("Some data")
manager.data.append("Some more data")
// the DataImporter instance for the importer property has not yet been created

DataManager定義了一個(gè)DataImporter實(shí)例盔粹,這里假設(shè)初始化DataImporter實(shí)例需要很長(zhǎng)時(shí)間隘梨。DataManager實(shí)例可以自己管理數(shù)據(jù),而不必使用DataImporter實(shí)例來(lái)打開(kāi)文件舷嗡,所以當(dāng)創(chuàng)建DataManger實(shí)例時(shí)轴猎,無(wú)需把DataImporter實(shí)例同時(shí)創(chuàng)建,而是在第一次使用的時(shí)候才創(chuàng)建进萄。

print(manager.importer.fileName)
// the DataImporter instance for the importer property has now been created
// Prints "data.txt"

注意:如果一個(gè)懶惰屬性被多個(gè)線(xiàn)程同時(shí)訪問(wèn)捻脖,而且這個(gè)屬性還沒(méi)有被初始化時(shí),不能保證這個(gè)屬性只被初始化一次中鼠。

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

計(jì)算屬性提供了一個(gè)getter和一個(gè)可選的setter方法來(lái)間接地獲取和設(shè)置其他屬性和其他值可婶。

struct Point {
    var x = 0.0, y = 0.0
}
struct Size {
    var width = 0.0, height = 0.0
}
struct Rect {
    var origin = Point()
    var size = Size()
    var center: Point {
        get {
            let centerX = origin.x + (size.width / 2)
            let centerY = origin.y + (size.height / 2)
            return Point(x: centerX, y: centerY)
        }
        set(newCenter) {
            origin.x = newCenter.x - (size.width / 2)
            origin.y = newCenter.y - (size.height / 2)
        }
    }
}
var square = Rect(origin: Point(x: 0.0, y: 0.0),
                  size: Size(width: 10.0, height: 10.0))
let initialSquareCenter = square.center
square.center = Point(x: 15.0, y: 15.0)
print("square.origin is now at (\(square.origin.x), \(square.origin.y))")
// Prints "square.origin is now at (10.0, 10.0)"

這個(gè)例子定義了三個(gè)結(jié)構(gòu):

  • Point封裝了一個(gè)點(diǎn)的x和y坐標(biāo)
  • Size封裝了寬和高
  • Rect使用原點(diǎn)和尺寸定義了一個(gè)矩形

Rect還定義了一個(gè)計(jì)算屬性center。矩形的中心可以同原點(diǎn)和尺寸計(jì)算出來(lái)援雇,所以不必用存儲(chǔ)屬性來(lái)定義矩形的中心矛渴。Rect為計(jì)算屬性定義了一個(gè)自定義的getter和setter。

上面這個(gè)例子中創(chuàng)建了一個(gè)正方形square惫搏,如下圖中的藍(lán)色矩形具温。然后squarecenter屬性通過(guò)點(diǎn)語(yǔ)法被訪問(wèn),會(huì)使center的getter方法被調(diào)用筐赔,用來(lái)獲取屬性的當(dāng)前值铣猩,getter會(huì)返回一個(gè)新的Point代表正方形的中心(當(dāng)前是(5, 5))。

接著center被設(shè)置為(15, 15)川陆,把正方形往右上角移動(dòng)剂习,如下圖的橙色正方形蛮位。設(shè)置center屬性會(huì)調(diào)用setter方法,修改xy的值鳞绕,把正方形移到一個(gè)新的位置失仁。

Coordinate
簡(jiǎn)寫(xiě)Setter的定義 (Shorthand Setter Declaration)

如果setter方法沒(méi)有給新的值定義名字,我們可以使用一個(gè)默認(rèn)名字newValue们何。

struct AlternativeRect {
    var origin = Point()
    var size = Size()
    var center: Point {
        get {
            let centerX = origin.x + (size.width / 2)
            let centerY = origin.y + (size.height / 2)
            return Point(x: centerX, y: centerY)
        }
        set {
            origin.x = newValue.x - (size.width / 2)
            origin.y = newValue.y - (size.height / 2)
        }
    }
}
只讀計(jì)算屬性 (Read-Only Computed Properties)

一個(gè)計(jì)算屬性只有g(shù)etter沒(méi)有setter萄焦,那么這個(gè)計(jì)算屬性就是只讀計(jì)算屬性。只讀計(jì)算屬性智能返回一個(gè)值冤竹,通過(guò)點(diǎn)語(yǔ)法訪問(wèn)拂封,但是不能設(shè)置新的值。

注意:必須用var把計(jì)算屬性(包括只讀計(jì)算屬性)定義為可變屬性鹦蠕,因?yàn)樗麄兊闹凳遣还潭ǖ摹?/p>

定義只讀屬性時(shí)冒签,我們可以把get關(guān)鍵字和大括號(hào)省略:

struct Cuboid {
    var width = 0.0, height = 0.0, depth = 0.0
    var volume: Double {
        return width * height * depth
    }
}
let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
// Prints "the volume of fourByFiveByTwo is 40.0"

屬性觀察者 (Property Observers)

屬性觀察者觀察屬性值得變化,并作出響應(yīng)钟病。只要屬性的值被設(shè)置萧恕,屬性觀察者就會(huì)被調(diào)用,即使新的值和屬性的當(dāng)前值一樣肠阱。

我們可以把屬性觀察者添加到自己定義的任何存儲(chǔ)屬性中票唆,除了懶惰存儲(chǔ)屬性。也可以通過(guò)重寫(xiě)父類(lèi)的屬性來(lái)添加到父類(lèi)的屬性中屹徘。不必為沒(méi)有重寫(xiě)的計(jì)算屬性定義屬性觀察者走趋,因?yàn)槲覀兛梢栽谟?jì)算屬性的setter方法觀察他的值的變化,并作出響應(yīng)噪伊。

兩種觀察屬性值變化的方法:

  • willSet:新的值被存儲(chǔ)之前調(diào)用
  • didSet:新的值被存儲(chǔ)之后馬上調(diào)用

如果實(shí)現(xiàn)一個(gè)willSet觀察者簿煌,會(huì)把新的值作為一個(gè)常量參數(shù)∷盅纾可以為這個(gè)新的值指定一個(gè)名字啦吧,如果沒(méi)有指定,那么默認(rèn)是newValue拙寡。

如果實(shí)現(xiàn)了一個(gè)didSet觀察者,會(huì)把舊的值作為一個(gè)常量參數(shù)琳水∷粮猓可以為這個(gè)舊的值指定一個(gè)名字,如果沒(méi)有指定在孝,那么默認(rèn)是oldValue诚啃。如果在didSet觀察者里面給屬性賦一個(gè)新值,那么這個(gè)新值會(huì)替換掉剛剛設(shè)置的那個(gè)值渣蜗。

注意:當(dāng)父類(lèi)的屬性在子類(lèi)的初始化器被設(shè)置烁设,那么父類(lèi)屬性的willSetdidSet會(huì)在父類(lèi)的初始化器調(diào)用之后被調(diào)用。在設(shè)置父類(lèi)自己的屬性時(shí)舟误,父類(lèi)的willSetdidSet在父類(lèi)的初始化器調(diào)用之前不會(huì)被調(diào)用造垛。

class StepCounter {
    var totalSteps: Int = 0 {
        willSet(newTotalSteps) {
            print("About to set totalSteps to \(newTotalSteps)")
        }
        didSet {
            if totalSteps > oldValue  {
                print("Added \(totalSteps - oldValue) steps")
            }
        }
    }
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
// About to set totalSteps to 200
// Added 200 steps
stepCounter.totalSteps = 360
// About to set totalSteps to 360
// Added 160 steps
stepCounter.totalSteps = 896
// About to set totalSteps to 896
// Added 536 steps

注意:如果把含有觀察者的屬性作為一個(gè)in-out參數(shù)傳入一個(gè)方法魔招,那么willSetdidSet總是會(huì)被調(diào)用,這是因?yàn)閕n-out參數(shù)的"copy-in copy-out"內(nèi)存模式:在方法結(jié)束時(shí)五辽,這個(gè)值會(huì)被返回被屬性办斑。

全局和本地變量 (Global and Local Variables)

全局變量是在任何方法、閉包和類(lèi)型上下文之外定義的杆逗。而局部變量是在方法乡翅、閉包和類(lèi)型上下文之內(nèi)定義的。

注意:全局常量和變量都是懶惰地計(jì)算罪郊,類(lèi)似懶惰存儲(chǔ)屬性蠕蚜。但是全局常量和變量不需要使用lazy標(biāo)記。本地常量和變量從來(lái)都不是懶惰的計(jì)算悔橄。

類(lèi)型屬性 (Type Properties)

實(shí)例屬性是屬于一個(gè)特定類(lèi)型的實(shí)例靶累。我們可以定義屬于類(lèi)型的屬性。

注意:不同于實(shí)例屬性橄维,我們必須給類(lèi)型屬性一個(gè)默認(rèn)值尺铣。因?yàn)轭?lèi)型沒(méi)有初始化器來(lái)給類(lèi)型屬性賦值。類(lèi)型存儲(chǔ)屬性只要在第一次被訪問(wèn)的時(shí)候才會(huì)初始化争舞,并且保證只會(huì)初始化一次凛忿,即使是在多線(xiàn)程中同時(shí)被訪問(wèn),不需要使用lazy關(guān)鍵字標(biāo)記竞川。

類(lèi)型屬性語(yǔ)法 (Type Property Syntax)

使用static關(guān)鍵字來(lái)定義類(lèi)型屬性店溢,對(duì)于class類(lèi)型的計(jì)算類(lèi)型屬性,可以使用class來(lái)定義委乌,可以讓子類(lèi)重新父類(lèi)的實(shí)現(xiàn)床牧。

struct SomeStructure {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 1
    }
}

enum SomeEnumeration {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 6
    }
}

class SomeClass {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 27
    }
    class var overrideableComputedTypeProperty: Int {
        return 107
    }
}
查詢(xún)和設(shè)置類(lèi)型屬性 (Querying and Setting Type Properties)

就像實(shí)例屬性一樣,類(lèi)型屬性也是通過(guò)點(diǎn)語(yǔ)法來(lái)查詢(xún)和設(shè)置的:

print(SomeStructure.storedTypeProperty)
// Prints "Some value."
SomeStructure.storedTypeProperty = "Another value."
print(SomeStructure.storedTypeProperty)
// Prints "Another value."
print(SomeEnumeration.computedTypeProperty)
// Prints "6"
print(SomeClass.computedTypeProperty)
// Prints "27"

第十部分完遭贸。下個(gè)部分:【Swift 3.1】11 - 方法 (Functions)


如果有錯(cuò)誤的地方戈咳,歡迎指正!謝謝壕吹!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末著蛙,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子耳贬,更是在濱河造成了極大的恐慌踏堡,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,110評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件咒劲,死亡現(xiàn)場(chǎng)離奇詭異顷蟆,居然都是意外死亡诫隅,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)帐偎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)逐纬,“玉大人,你說(shuō)我怎么就攤上這事肮街》缣猓” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,474評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵嫉父,是天一觀的道長(zhǎng)沛硅。 經(jīng)常有香客問(wèn)我,道長(zhǎng)绕辖,這世上最難降的妖魔是什么摇肌? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,881評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮仪际,結(jié)果婚禮上围小,老公的妹妹穿的比我還像新娘。我一直安慰自己树碱,他們只是感情好肯适,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,902評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著成榜,像睡著了一般框舔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上赎婚,一...
    開(kāi)封第一講書(shū)人閱讀 51,698評(píng)論 1 305
  • 那天刘绣,我揣著相機(jī)與錄音,去河邊找鬼挣输。 笑死纬凤,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的撩嚼。 我是一名探鬼主播停士,決...
    沈念sama閱讀 40,418評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼完丽!你這毒婦竟也來(lái)了向瓷?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,332評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤舰涌,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后你稚,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體瓷耙,經(jīng)...
    沈念sama閱讀 45,796評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡朱躺,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,968評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了搁痛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片长搀。...
    茶點(diǎn)故事閱讀 40,110評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖鸡典,靈堂內(nèi)的尸體忽然破棺而出源请,到底是詐尸還是另有隱情,我是刑警寧澤彻况,帶...
    沈念sama閱讀 35,792評(píng)論 5 346
  • 正文 年R本政府宣布谁尸,位于F島的核電站,受9級(jí)特大地震影響纽甘,放射性物質(zhì)發(fā)生泄漏良蛮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,455評(píng)論 3 331
  • 文/蒙蒙 一悍赢、第九天 我趴在偏房一處隱蔽的房頂上張望决瞳。 院中可真熱鬧,春花似錦左权、人聲如沸皮胡。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,003評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)屡贺。三九已至,卻和暖如春瀑梗,著一層夾襖步出監(jiān)牢的瞬間烹笔,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,130評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工抛丽, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留谤职,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,348評(píng)論 3 373
  • 正文 我出身青樓亿鲜,卻偏偏與公主長(zhǎng)得像允蜈,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蒿柳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,047評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容