2016年4月12日 星期二

swift 學習筆記 5: Property

property: 屬性, 特性

屬性是將數值與特殊的分類,結構,列舉等相互關聯.
有兩種常見的屬性:儲存式屬性(Stored property )及運算式的屬性(computed property)
儲存式屬性:記錄著實體的常數及變數, 僅能提供給 class 及 struct 使用
運算式屬性:則是計算出數值,可以提供給 class, struct, enum 使用
上述兩種屬性通常與特殊型別的實體相互關連. 甚至屬性也可與自我型別的實體相關連, 這種屬性稱做型別屬性(type property)

此外你也可以定義屬性觀察者用來監視屬性的變化,將它當作回應客戶端的動作,屬性觀察者可以放在你自行定義的儲存式屬性上,,屬性觀察者也可以加在一個從父分類所繼承的屬性上.

儲存式屬性:最簡單的型式是他儲存著特殊分類體或結構體裏面的常數及變數, 儲存式常數屬性用關鍵字let指定初始值, 儲存式變數屬性則是用關鍵字var指定初始值.正如它定義所示,在定義儲存式屬性時就要給個預設值, 且初始化期間,也可以設定或修改它的初始值. 即使對於儲存式常數屬性, 在初始化時仍然可以透過參數傳遞作初始化,
結構體若宣告是常數時,它的屬性即使宣告成變數仍是無法被修改, 因為結構體是屬於數值型別(value type)而非參照型別(reference type), 但分類就不同, 它是參照型別, 你仍然可以對分類體宣告成常數的屬性加以修改

緩慢儲存式屬性(lazy stored property)是直到它被第一次使用時才會生出來, 在宣告前面使用 lazy 修正子用以指示它是緩慢儲存式屬性,通常他是變數(var). 常數並不能宣告成 lazy.
另外須注意的是:如果多執行序同時去取用宣告為 lazy 的變數, 則該變數有可能會被初始化很多次, 參考底下範例:

class MyData {
var name="data"
}

class MyManager {
lazy var Indata=MyData()
var data = ["My"]    //   define data as a String Array
}        
let my=MyManager()
// 因為 Indata 宣告成緩慢儲存型變數,故此時 Indata 尚未生出來, Indata.storage=nil,但 data 確已經生出來等於 ["My"] 字串陣列.my.data.append("new data")
my.data.append("more data")
print(my.Indata.name)          
 // 上述 my.data.append 增加字串時, Indata 仍尚未生出來
 // 最後 Indata 第一次被引用,  只有這時, Indata 才會被分配將name給生出字串 "data" 來

運算式屬性: 分類,結構,列舉,除了定義儲存式屬性外, 也可定義運算式屬性, 他並不會儲存數值, 而是提供 get 作間接讀取,也可加上可選的 set 來設定屬性及數值. 當沒有 set 方法時, 他稱為唯讀的計算式屬性(read-only computed property), 但即便是唯讀屬性,它也應該宣告成變數, 因為他的值是計算得到的, 參考範例:

struct Location {
var x=0.0
var y=0.0
}

struct newLocation {
var origin = Location()
    var position  = Location()
var center: Location {   // This is a computed property, which type is Location
 // method of getter
get{
          let cx = (origin.x+position.x)/2
          let cy = (origin.y+position.y)/2
return Location(x:cx,y:cy)
}
// method of setter,  if newCenter is not given or leave it empty,  the default name will be newValue
set(newCenter) {
origin.x=newCenter.x
origin.y=newCenter.y
}
     } // End of computed property
}
var a=newLocation(origin:Location(x:5,y:5),position:Location(x:15,y:15))
// a.center will be computed as (10,10)

運算式的屬性可以簡單到將 get 關鍵字直接移除, 僅留下計算區塊並回傳唯讀屬性的結果 , 如下例面積 area 所述:
struct Area {
var width=0.0
var height=0.0
var area: Double {  return width*height }  // area is a computed propery, its type is double
}

property observer 屬性觀察者, 每次屬性有設定時, 即使設定值不變, 它都會被呼叫,  他是用來觀察及回應屬性的變化,對於儲存式的屬性(stored property)都可加入屬性觀察者(willSet 及didSet)
willSet 是當屬性的值被儲存前被呼叫, 且會傳進一個新屬性常數值, 如果不給定的話, 預設名稱叫作 newValue
didSet 是當屬性的值被儲存後立刻被呼叫, 且會傳進一個舊屬性常數值, 如果不給定的話, 預設名稱叫作 oldValue

class observerClass {
 var s: Int = 0 { // s is a a stored property, because it was stored an vialue 0
    willSet(new){print("willset: new=\(new)") } //is called just before the value is stored.
    didSet(old)   {print("didset : old =\(old)" )  } //is called immediately after the new value is stored.
   }
}
var c = observerClass()
c.s = 200

In-Out Parameters: ???

整體變數及區域變數也可以有計算式及觀察者的屬性

型別屬性(type property):
實體的屬性是當每次新生成某種型別的實體時, 會產生自己的屬性,與其它的實體是分離的.
另外也可以定義屬於自己型別的屬性, 是任何實體所沒有的屬性. 不管那種型別新生成多少實體出來,仍然只有唯一這種屬性的副本.這種屬性就稱為型別屬性

型別屬性對於定義那些通用於所有特殊型別實體的數值很有用, 比如所有實體都可以使用的常數屬性, 亦或是一個變數屬性,它儲存了一個數值,而他是通用於那種型別的所有實體

儲存式屬性可以是常數或變數, 運算式屬性只能宣告成變數,運算式實體屬性也是如此
不像儲存式實體屬性那樣,儲存式別屬性必須給予一個預設值,那是因為初始時,型別本身沒有也一個初始化程序可以指定數值給儲存式別屬性

型別屬性的語法
使用關鍵字 static 來定義型別屬性, 對於分類型別而言, 計算式型別屬性使用關鍵字 class 允許子分類可以覆蓋父類所實現的功能,
宣告成型別屬性後, 他只能透過型別名稱加上句點語法來取用, 所有隸屬該型別的實體並不能修改它, 因為 static 的作用範圍只有在該型別內有效, 可以透過特殊的 method 來讀取它,在type裏面的 type method 可以直接取用 type property, type method 與 type property 都是用static來宣告  
參考以下範例:

class MyClass {
           static var stored="A string"                // stored type property
           static var computed: Int{ return 0}    //  computed type property
           class  var overComputed: Int { return 1 }  // override computed type property
}
MyClass.stored="New string"

// 型別屬性的取用是用型別名稱加上句點語法來運作
// 再看下面例子
class Counter {
      static var c=3
      func Counterget() -> Int {
               return Counter.c
      }
}
let a=Counter()         // c=3
let b=Counter()         // c=3

Counter.c=10            // modify type property
a.Counterget()            // c=10
b.Counterget()            // c=10
//  all the instance belong to the class will only one c











沒有留言: