程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> 更多關於編程 >> Swift教程之屬性詳解

Swift教程之屬性詳解

編輯:更多關於編程

Swift教程之屬性詳解。本站提示廣大學習愛好者:(Swift教程之屬性詳解)文章只能為提供參考,不一定能成為您想要的結果。以下是Swift教程之屬性詳解正文


屬性是描寫特定類、構造或許列舉的值。存儲屬性作為實例的一部門存儲常量與變量的值,而盤算屬性盤算他們的值(不只是存儲)。盤算屬性存在於類、構造與列舉中。存儲屬性僅僅只在類與構造中。

屬性平日與特定類型實例接洽在一路。但屬性也能夠與類型自己接洽在一路,如許的屬性稱之為類型屬性。

別的,可以界說屬性不雅察者來處置屬性值產生轉變的情形,如許你便可以對用戶操作做出反響。屬性不雅察者可以被加在本身界說的存儲屬性之上,也能夠在從父類繼續的子類屬性之上。

1、存儲屬性

最簡略的情況,作為特定類或構造實例的一部門,存儲屬性存儲著常量或許變量的值。存儲屬性可分為變量存儲屬性(症結字var描寫)和常量存儲屬性(症結字let描寫)。

當界說存儲屬性時,你可以供給一個默許值,這些在“默許屬性值”描寫。在初始化進程中你也能夠設置或轉變存儲屬性的初值。這個原則對常量存儲屬性也異樣實用(在“初始化進程中轉變常量屬性”描寫)

上面的例子界說了一個叫FixedLengthRange的構造,它描寫了一個必定規模內的整數值,當創立這個構造時,規模長度是弗成以被轉變的:

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

FixedLengthRange的實例包括一個名為firstValue的變量存儲屬性和名為length的常量存儲屬性。以上的例子中,當規模肯定,length被初始化以後它的值是弗成以被轉變的

常量構造實例的存儲屬性
假如你創立一個構造實例,並將其賦給一個常量,這個實例中的屬性將弗成以被轉變,即便他們被聲明為變量屬性

 
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 thought firstValue is a variable property

由於rangeOfFourItems是一個常量(let),即使firstValue是一個變量屬性,它的值也是弗成以被轉變的

如許的特征是由於構造是值類型。當一個值類型實例作為常量而存在,它的一切屬性也作為常量而存在。

而這個特征對類其實不實用,由於類是援用類型。假如你將援用類型的實例賦值給常量,仍然可以或許轉變實例的變量屬性。

Lazy Stored Properties(懶散存儲屬性?)

懶散存儲屬性是當它第一次被應用時才停止初值盤算。經由過程在屬性聲明前加上@lazy來標識一個懶散存儲屬性。

留意

必需聲明懶散存儲屬性為變量屬性(經由過程var),由於它的初始值直到實例初始化完成以後才被檢索。常量屬性在實例初始化完成之前就應當被賦值,是以常量屬性不克不及夠被聲明為懶散存儲屬性。

當屬性初始值由於內部緣由,在實例初始化完成之前不克不及夠肯定時,就要界說成懶散存儲屬性。當屬性初始值須要龐雜或高價值的設置,在它須要時才被賦值時,懶散存儲屬性就派上用處了。

上面的例子應用懶散存儲屬性來避免類中不用要的初始化操作。它界說了類DataImporter和類DataManager:

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 += "Some data"
manager.data += "Some more data"
// the DataImporter instance for the importer property has not yet been created

類DataManager有一個稱為data的存儲屬性,它被初始化為一個空的String數組。固然DataManager界說的其它部門並沒有寫出來,但可以看出DataManager的目標是治理String數據並為其供給拜訪接口。

DataManager類的部門功效是從文件中援用數據。這個功效是由DataImporter類供給的,這個類須要必定的時光來初始化,由於它的實例須要翻開文件並見內容讀到內存中。

由於DataManager實例能夠其實不須要立刻治理從文件中援用的數據,所以在DataManager實例被創立時,其實不須要立時就創立一個新的DataImporter實例。這就使適合DataImporter實例在須要時才被創立天經地義起來。

由於被聲明為@lazy屬性,DataImporter的實例importer只要在當它在第一次被拜訪時才被創立。例如它的fileName屬性須要被拜訪時:


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

存儲屬性與實例變量

假如你應用過Objective-C,你應當曉得它供給兩種方法來存儲作為類實例一部門的值與援用。除屬性,你可使用實例變量作為屬性值的後備存儲

Swift應用一個單一屬性聲明來同一這些概念。一個Swift屬性沒有與之符合的實例變量,而且屬性的後備存儲也不克不及直接拜訪。這避免了在欠亨高低文中拜訪值的混雜,而且簡化屬性聲明成為一個單一的、終究的語句。關於屬性的一切信息-包括稱號、類型和內存治理等-作為類型界說的一部門而界說。

2、盤算屬性

除存儲屬性,類、構造和列舉可以或許界說盤算屬性。盤算屬性其實不存儲值,它供給getter和可選的setter來直接地獲得和設置其它的屬性和值。


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)
println("square.origin is now at (\(square.origin.x), \(square.origin.y))")
// prints "square.origin is now at (10.0, 10.0)"

這個例子界說了三個處置幾何圖形的構造:

Point包括一個(x,y)坐標
Size包括寬度width和高度height
Rect界說了一個長方形,包括原點和年夜小size
Rect構造包括一個稱之為center的盤算屬性。Rect以後中間點的坐標可以經由過程origin和size屬性得來,所以其實不須要顯式地存儲中間點的值。取而代之的是,Rect界說一個稱為center的盤算屬性,它包括一個get和一個set辦法,經由過程它們來操作長方形的中間點,就像它是一個真實的存儲屬性一樣。

例子中界說了一個名為square的Rect變量,它的中間點初始化為(0, 0),高度和寬度初始化為10,由以下圖形中的藍色正方形部門。

變量square的center屬性經由過程點操作符拜訪,它會挪用center的getter辦法。分歧於直接前往一個存在的值,getter辦法要經由過程盤算能力前往長方形的中間點的值(point)。以上的例子中,getter辦法前往中間點(5,5)。

然後center屬性被設置成新的值(15,15),如許就把這個正方形向右向上挪動到了途中黃色部門所表現的新的地位。經由過程挪用setter辦法來設置center,轉變origin中坐標x和y的值,將正方形挪動到新的地位。

setter聲明的簡單寫法

假如盤算屬性的setter辦法未將被設置的值界說一個稱號,將會默許地應用newValue這個稱號來取代。上面的例子采取了如許一種特征,界說了Rect構造的新版本:

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)
}
}
}

只讀盤算屬性

只讀盤算屬性只帶有一個getter辦法,經由過程點操作符,可以放回屬性值,然則不克不及修正它的值。
留意
應當應用var症結字將盤算屬性-包括只讀盤算屬性-界說成變量屬性,由於它們的值其實不是固定的。let症結字只被常量屬性說應用,以注解一旦被設置它們的值就是弗成轉變的了

經由過程移除get症結字和它的年夜括號,可以簡化只讀盤算屬性的界說:


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)
println("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
// prints "the volume of fourByFiveByTwo is 40.0

這個例子界說了一個三維長方體構造Cuboid,包括了長寬高三個屬性,和一個表現長方體容積的只讀盤算屬性volume。volume值是弗成被設置的,由於它直接由長寬高三個屬性盤算而來。經由過程供給如許一個只讀盤算屬性,Cuboid使內部用戶可以或許拜訪到其以後的容積值。

3、屬性不雅察者

屬性不雅察者不雅察屬性值的轉變並對此做出呼應。當設置屬性的值時,屬性不雅察者就被挪用,即便當新值同原值雷同時也會被挪用。

除懶散存儲屬性,你可認為任何存儲屬性加上屬性不雅察者界說。別的,經由過程重寫子類屬性,也能夠繼續屬性(存儲或盤算)加上屬性不雅察者界說。屬性重寫在“重寫”章節界說。

留意
不用為未重寫的盤算屬性界說屬性不雅察者,由於可以經由過程它的setter辦法直接對值的轉變做出呼應

界說屬性的不雅察者時,你可以零丁或同時應用上面的辦法:
willSet:設置值前被挪用
didSet:設置值後連忙被挪用

當完成willSet不雅察者時,新的屬性值作為常量參數被傳遞。你可認為這個參數起一個名字,假如不的話,這個參數就默許地被定名成newValue。

在完成didSet不雅察者時也是一樣,只不外傳遞的產量參數表現的是舊的屬性值。

留意:
屬性初始化時,willset和didSet其實不會被挪用。只要在初始化高低文以外,當設置屬性值時才被挪用

上面是一個willSet和didSet用法的實例。界說了一個類StepCounter,用來統計人走路時的步數。它可以從計步器或其它計數器上獲得輸出數據,對平常接洽錘煉的步數停止追蹤。


class StepCounter {
var totalSteps: Int = 0 {
willSet(newTotalSteps) {
println("About to set totalSteps to \(newTotalSteps)")
}
didSet {
if totalSteps > oldValue  {
println("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

類StepCounter聲清楚明了一個Int類型的、含有willSet和didSet不雅察者的存儲屬性totalSteps。當這個屬性被付與新值時,willSet和didSet將會被挪用,即便新值和舊值是雷同的。

例子中的willSet不雅察者為參數起了個新的名字newTotalSteps,它簡略地打印了行將被設置的值。

當totalSteps值被更新時,didSet不雅察者被挪用,它比擬totalSteps的新值和舊值,假如新值比舊值年夜,就打印所增長的步數。didSet並沒無為舊值參數定名,在本例中,將會應用默許的名字oldValue來表現舊的值。

留意

假如經由過程didSet來設置屬性的值,即便屬性值方才被設置過,起感化的也將會是didSet,即新值是didSet設置的值

4、全局和部分變量

以上所寫的關於盤算與不雅察屬性值的特征異樣實用於全局和部分變量。全局變量是在任何函數、辦法、閉包、類型高低文內部界說的變量,而部分變量是在函數、辦法、閉包中界說的變量。

後面章節所碰到過的全局、部分變量都是存儲變量。和存儲屬性一樣,存儲變量為特定類型供給存儲空間而且可以被拜訪

然則,你可以在全局或部分規模界說盤算變量和存儲變量不雅察者。盤算變量其實不存儲值,只用來盤算特定值,它的界說方法與盤算屬性一樣。

留意
全局常量和變量平日是延遲盤算的,跟懶散存儲屬性一樣,然則不須要加上@lazy。而部分常量與變量不是延遲盤算的。

5、類型屬性

實例屬性是特定類型實例的屬性。當創立一個類型的實例時,這個實例有本身的屬性值的聚集,這將它與其它實例辨別開來。

也能夠界說屬於類型自己的屬性,即便創立再多的這個類的實例,這個屬性也不屬於任何一個,它只屬於類型自己,如許的屬性就稱為類型屬性。

類型屬性實用於界說那些特定類型實例所通用的屬性,例如一個可以被一切實例應用的常量屬性(就像c中的靜態常量),或許變量屬性(c中的靜態變量)。

可認為值類型(構造、列舉)界說存儲類型屬性和盤算類型屬性。對類而言,只可以或許界說盤算類型屬性。

值類型的存儲類型屬性可所以常量也能夠是變量。而盤算類型屬性平日聲明成變量屬性,相似於盤算實例屬性

留意
不想存儲實例屬性,你須要給存儲類型屬性一個初始值。由於類型自己在初始化時不克不及為存儲類型屬性設置值

類型屬性句法

在C和Objective-C中,界說靜態常量、變量和全局靜態變量一樣。然則在swift中,類型屬性的界說要放在類型界說中停止,在類型界說的年夜括號中,顯示地聲明它在類型中的感化域。

對值類型而言,界說類型屬性應用static症結字,而界說類類型的類型屬性應用class症結字。上面的例子展現了存儲和盤算類型屬性的用法:


struct SomeStructure {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
// return an Int value here
}
}
enum SomeEnumeration {
static var storedTypeProperty = "Some value."     static var computedTypeProperty: Int {     // return an Int value here
}
}
class SomeClass {
class var computedTypeProperty: Int {
// return an Int value here
}
}

留意

下面的例子是針對只讀盤算類型屬性而言的,不外你也能夠像盤算實例屬性一樣界說可讀可寫的盤算類型屬性

查詢與設置類型屬性

像實例屬性一樣,類型屬性經由過程點操作符來查詢與設置。然則類型屬性的查詢與設置是針對類型而言的,其實不是針對類型的實例。例如:


println(SomeClass.computedTypeProperty)
// prints "42"
println(SomeStructure.storedTypeProperty)
// prints "Some value."
SomeStructure.storedTypeProperty = "Another value."
println(SomeStructure.storedTypeProperty)
// prints "Another value.

上面的例子在一個構造中應用兩個存儲類型屬性來展現一組聲響通道的音頻品級表。每一個通道應用0到10來表現聲響的品級。

從上面的圖表中可以看出,應用了兩組聲響通道來表現一個平面聲響頻品級表。當一個通道的品級為0時,一切的燈都不會亮,當品級為10時,一切的燈都邑亮。上面的圖中,右邊的通道表現聲響品級為9,左邊的為7

上述的聲響通道由以下的AudioChannel構造實例來表現:


struct AudioChannel {
static let thresholdLevel = 10
static var maxInputLevelForAllChannels = 0
var currentLevel: Int = 0 {
didSet {
if currentLevel > AudioChannel.thresholdLevel {
//cap the new audio level to the threshold level
currentLevel = AudioChannel.thresholdLevel
}
if currentLevel > AudioChannel.maxInputLevelForAllChannels {
// store this as the new overall maximum input level
AudioChannel.maxInputLevelForAllChannels = currentLevel
}
}
}
}

AudioChannel構造界說了兩個存儲類型屬性。thresholdLevel界說了音頻所能到達的最高級級,對一切的AudoChannel實例而言,是個值為10的常量。當一個聲響旌旗燈號的值跨越10時,會被截斷為其阈值10。

第二個類型屬性是一個變量存儲屬性maxInputLevelForAllChannels。它保留了以後一切AudioChannel實例中所接收到聲響的最高級級,它被初始化為0。

構造還界說了一個存儲實例屬性currentLevel,表現以後的通道聲響品級。這個屬性應用didSet屬性不雅察者來檢測currentLevel的轉變。這個不雅察者履行兩道檢討:

假如currentlevel的新值比阈值thresholdLevel年夜,currentLevel將被設置成thresholdLevel
假如currentLevel的新值比一切AudioChannel實例之前接收到的最年夜聲響品級還要年夜,那末maxInputLevelForAllChannles將會被設置成cueentLevel年夜值。

留意

第一道檢討中,didSet為currentLevel設置了新值。這其實不會形成不雅察者再次被挪用

可以創立兩個AudioChannel實例,leftChannel和rightChannel,來表現一個平面聲體系:

var leftChannel = AudioChannel()
var rightChannel = AudioChannel()

假如設置左通道的currentLevel為7,它的類型屬性maxInputLevelForAllChannels將更新成為7:


leftChannel.currentLevel = 7
println(leftChannel.currentLevel)
// prints "7"
println(AudioChannel.maxInputLevelForAllChannels)
// prints "7”
 
假如像設置右通道的currentlevel為11,它的值將被截短成為10,並且maxInputLevelForAllChannels的值也將更新為10:
“rightChannel.currentLevel = 11
println(rightChannel.currentLevel)
// prints "10"
println(AudioChannel.maxInputLevelForAllChannels)
// prints "10"

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved