2016年4月12日 星期二

swift 學習筆記 6: method

method: 方法
簡單來說將 func (程序) 放到一些型別像是分類(class),結構(struc),列舉(enum)等裡面去定義它, 該程序(function)就叫作 method

instance: 實例, 個體, 實體, 物件
將型別像是分類(class),結構(struc),列舉(enum)等實體化指定給一個個體, 該個體就叫作intstance

instance method: 實例方法,實體方法, 實體程序
將型別像是分類(class),結構(struc),列舉(enum)等實體化指定給一個個體, 該個體內部的程序(function)就叫作 instance method

type method: 型別方法
將 func (程序) 放到一些型別像是分類(class),結構(struc),列舉(enum)等裡面去定義它, 該程序(function)在分類裏面用 class 加到 func 關鍵字前面, 或者將程序(function)放在結構(struc),列舉(enum)裡面用 static 加到 func 關鍵字前面, 這類的程序就都叫作 type method, 如底下例子:

class newClass{
        class func typeMethod() {}
}
struct newStruct{
        static func typeMethod() {}
}
enum newEnum{
        static func typeMethod() {}
}

local and external parameter name for method: 方法的區域參數名稱與外部參數名稱,區域參數名稱是在程序裏面使用, 而外部參數名稱是給該程序外部程式呼叫時用的.

方法(method)或程序(function)宣告的方式:
  func functionName(externalName1 pName1:TYPE1,externalName2 pName2:TYPE2,...)
         {  // here is function body ...
          }
其中 externalName? 是外部名稱修飾子, 它是給該程序外部呼叫時用的, 而 pName? 是區域參數名稱, 是給該程序內部使用的, 它們都可以視需要而加以命名.

swift 對於程序裡第一個參數預設視為區域參數名, 而第二個以後的參數是區域參數名同時也是外部參數名.

通常程序不會去定義外部參數名稱, 但如果有命名時,當呼叫該程序時也必須用該名稱當修飾子, 中間用冒號 ':' 隔開, 接著再放需要的傳遞參數
      funcName(externalName: localName,...)

對於程序呼叫的方式,如果第一個參數在程序定義時沒指定外部名稱修飾子, 那呼叫該程序時第一個參數就不能加上名稱修飾子,只要將區域數值當傳遞參數放進去就可,但之後的參數雖沒指定名稱修飾子,仍然必須指定名稱修飾子. 此名稱修飾子必須對應到程序裡所宣告的區域參數名, 兩者名稱必須相同, 例如:
      func methodName(first:Int, second: Int) { print("\(first):\(second)")}
      methodName(3, second :4)  //Don't call methodName(first:3,second:4). it cause Error

如果定義程序時,第二個之後參數的外部參數名稱是用底線 '_' 的話, 那呼叫該程序時就不可用外部名稱修飾子. 而直接將區域數值當傳遞參數放進去就可, 例如
      func methodName(first: Int, _ second: Int) {print("\(first):\(second)")}         
      methodName(3,4) // Don;t call methodName(3,second:4). it cause Error

因此第一個參數如果不啟用外部參數名稱的話, 等同於強制將第一個參數使用區域參數名,也就是 swift 所採取的預設行為, 程序定義類似像 func fname(first: ...) 其實等同於 func fname(_ first: ...)

定義程序的參數時, 透過在第一個參數前冠上外部名稱或對二個之後的參數前冠上底線'_'都可以改變 swift 對待參數的預設處理行為, 底線 '_' 一般解釋是當作不需要的意思. 這可以讓程式寫作更加彈性與自由

Modify values type from within instance method:在個體程序中修改數值的型別
由於結構(struc)及列舉(enum)預設是屬於數值型別(value type)的關係, 一旦個體產生後, 數值型別的屬性是無法用實體程序來改變的, 但如果在方法的 func 前面加上關鍵字 mutating 後, 那這個實體方法就可以修改結構體內部的屬性了,利用該程序在結束前將修改的結果寫入該結構體的屬性,就能達到修改的目的, 甚至當它指定寫回 self 本體時, 那將會得到一個全新的個體.

struct aStruct {
    var  name:String="O";
    mutating func newName(inName:String ) { self=aStruct(name:inName) }
}
var b=aStruct()  
// it must be defined as an variable to be mutable, now  b.name init to "O"
b.newName("N")
// now b.name become "N". In fact, b is a completely new instance different from old one
// -------------------------------------------------------
// Above instance method will mutatae type itself
// create a new instance with  a new property pass to name, init this instance a name (in InName)
// aStruct is a structure, but not a function. It will call init() to initialize it's property.
// name is one of the properties
// newName accept a sting stored in inName, which will mutate instance type itself in the end.
// at the end of method, assign it to self to change self property, that is type itself


closure: 區塊代碼, 程式區塊. 代碼包, 在 swift 裡, 一段用大括弧包起來的程式碼, 不需要命名, 他可以傳入參數, 也可以傳回數值, 它就叫作 closure, 它的語法是:
       {  (輸入參數宣告) -> 傳回參數的型別 in
           程式碼
       }
甚至無任何輸入或輸出時, 將程式碼用大括號括起來, 就是最簡單的 closure, 當一個程序的最後一個參數屬於程序型別的話, 當要呼叫他時, 不用將程序傳進去而是將區塊代碼緊隨在後, 當沒有其他參數要傳進去時, 甚至可以將所呼叫的程序小括號移除, 此區塊代碼稱為 trailing closure,  例如陣列型別有一個method叫作map,會將它的內容掃一遍. 它的參數屬於程序型別又是最後一個, 因此可以用 closure 傳給它
            var num=[5,3,6]
            let a=num.map() { (i) -> Int in
                         return 2*i
                  }
上述呼叫map時除了程序當參數外, 沒有其他參數, 因此上述的 () 也可移除不用寫, 同樣合語法:
            let a=num.map { (i) -> Int in
                     return 2*i
                }
上述區塊代碼功能是將輸入參數乘以2並回傳. 因此 a 會等於一個通通乘以2結果的陣列

autoclosure: 乍看之下, 會以為是自動程式區塊, 但其實是 "自動打包程式區塊"
將一段程式碼用大括號打包起來並指定給一個程序變數或程序常數, 打包程式區塊就叫作 autoclosure. 但所打包的程式碼並不會執行, 一旦該程序變數(或常數)加上用小括號讓他實體化程序時, 該程序才會執行, 見下例:
var num=[1,2,3]
let ac={ num.removeAtIndex(0) }
let  a=num.map() { (i) -> > Int in
                   return 2*i
           }
ac()  // call the function removeAtIndex(0) to remove 1st element in the array
let  b=num.map() { (i) -> > Int in
                   return 2*i
           }
程式碼說明:上述 removeAtIndex(0) 是一個陣列的方法, 用來移除陣列裡的元素, 傳進去0 代表要移除第1 個元素 首先將 autoclosure 指定給 ac, 接著第一個 map 執行, 此時 num 仍維持原樣, 當 ac() 執行後, num 將會少一個元素, 再執行一次 map 就可看出結果了


@noescape closure:
當一個程式區塊當作參數傳給另一程序時, 該程式區塊並不會執行(就把它當成是一個 autoclosure), 但到了程序裡時, 他就可以被呼叫. 在定義程序時應該使用 @noescape 加在 closure 參數前面,  要讓 swift 或 compiler 知道來最佳化他的生命期, 並且不讓他脫離該程式的範疇.
但對於像 callback 功能, 他要過些時候才會去執行, 而目前僅需將它儲存起來(autoclosure)時,那就不需要用 @noescape 來修飾該 closure. 見以下範例


 func noEscape(@noescape justgo: () ->  Void) {
         justgo() // the lifespan of x will be keep in this function, it's a  none-escaping closure
  }
  var ac : [ () -> Void ] = []
  func addClosure( handle: () ->  Void)  {
         ac.append(handle)
// autoclosure stored to ac, it's not necessary to have @noescape add before handle
  }
  class my {
   var x=10
       func doit() {      
          addClosure { self.x=100  } // append the closure to ac only, autoclosure
           noEscape { self.x=200 }   // the closure will be executed in  noEscape
}
   }
  let a = my()
  a.doit()
  print(a.x)
  // Prints "200"
  ac.first?()         // doit has stored 1 closure, do it
  print(a.x)
  // Prints "100"

另外 closure 可以用 $0, $1, $2 ... 來作為他輸入參數位置名稱, swift 會自動辨識他的型別. 例如
a=[1,2,3]
let b=a.map { return $0 }

如果 closure 後面加上左右雙括號 (), 該程序將會背直接執行, 例如
let a={ return  0}  // assign a closure to a
a()                         // execute  it, it return 0














           








沒有留言: