2016年4月24日 星期日

使用 openssl 編解碼小試

使用 RSA 產生的 public key 是用來編碼用的, 而 private key 則是用來解碼用的. 通常使用在短的詞句編解碼上, 例如密碼等, 寫一個  Makefile 方便參考使用:
PRIKEY=key.pem
PUBKEY=pub$(PRIKEY)
BITS=2048
# Use PUBKEY to encrypt, but use PRIKEY to decrypt
test="This is a test string"

genkey:
openssl genrsa -out $(PRIKEY) $(BITS)
openssl rsa -pubout -in $(PRIKEY)  -out $(PUBKEY)

testrsa:
echo -n $(test) | openssl rsautl -encrypt -inkey $(PUBKEY) -pubin  | openssl rsautl -decrypt  -inkey $(PRIKEY)

testaes: echo -n $(test) | openssl aes-256-cbc -a -k password | openssl aes-256-cbc -a -k password -d

clean:
rm -f  $(PRIKEY) $(PUBKEY) *.pem
直接輸入 make 就會產生 key.pem (private key) 及 pubkey.pem (public key)檔
輸入 make testrsa 或 make testaes 會測試編解碼程序是否將語句正確還原, make clean 會刪除所有 *.pem 檔
echo -n 是不要讓 echo 自動在語句上添加換行符號(\n), 接著將輸出傳給編碼程序,最後再傳給解碼程序, 還原出原始語句顯示在螢幕上

另外 openssl 可以使用 AES 將大的檔案編解碼
編碼:
           openssl aes-256-cbc  -a   -k  password  -in inputfile -out encryptfile
解碼:
           openssl aes-256-cbc   -a  -k  password  -d -in encryptfile -out orgfile
aes-256-cbc 使用 aes 256 byte 編/解碼
-a    是使用 base64 編/解碼, 方便瀏覽
-k     password 可以輸入任何易記碼
-d    是開啟解碼程序-in   輸入所要的編/解碼檔案
-out 產生所要的輸出檔案

2016年4月18日 星期一

swift 學習筆記 10. closure 及 @autoclosure

有時候我們可以將一段 swift 的敘述 (closure) 傳給副程式 (function), 讓它去處理, 就可以在副程式中宣告將傳進來的變數名稱型別隸屬於 closure 型別也就是  ()
   func my(ac: () -> Int)   {
         // After evaluation by using the () , following code will be executed
         switch ac() { Void -> Int in
            case 1  : print("expression is: 1")
            case 2  : print("expression is: 2")
            default : print("expression known")
         }
   }
   my( {1} )
   my( {2} )
   my( {4} )
甚至因為該 closure 位在最後一個參數, 因此呼叫時其實可以將小括號 () 省略
  my {1}
  my {2}
  my {3}
另外你也可以讓function 裏面的 closure 延後執行, 只要注意將返回值傳回正確的型別:
  my { Void -> Int {  // TYPE as defined by the closure
          print("Do something here")
          return 1   // Evaluate here, closure should return a Int value accordingly.
        }

如果說將 closure 冠上屬性 @autoclosure, 也就是讓 swift自動打包(自動將敘述加上closure{}), 之後呼叫該程序就不用再加上大括號了.
   func my(@autoclosure ac: () -> Int)   {
         switch ac() {
            case 1 : print("expression is:  1")
            case 2 : print("expression is:  2")
            default  : print("expression known")
         }
   }
   my(1)
   my(2)
   my(4)
雖說 closure 位於 function 裏最後一個參數, 但此時如果省略掉小括號 (), swift 會無法判斷該敘述而產生錯誤. @autoclosure 似乎只能運用在一些需要將運算符(operator)修改的作用上. 還不是很明瞭他的詳細運作, 需要再深入 study ...

2016年4月15日 星期五

swift 學習筆記 9. 讓 swift 和 c 交朋友

多數 linux 程式是用 c 寫的, 要讓 swift 程式可以呼叫 c 所寫的程式運作, 便需要透過 module 來做中間人. module 包含兩個重要檔案:  module.modulemap, 及 Package.swift(但該檔案的內容通常是空的, 但卻必須存在, 可以透過 touch Package.swift 來產生就可), 其他程式碼也都在此同一目錄下. 透過用 clang 來將 c 原始檔 compile 成目標檔, 再用 ar 封裝成程式庫(library)後放到 /usr/local/lib 下(但在OSX要放在 /usr/lib 底下). 最後交由 swift 讓他連結後產生執行檔.

編譯  c 程式可以寫一個 Makefile, 方便下指令 make 讓電腦自動去執行:

MYLIB = libmyfunction.a

$(MYLIB): myfunction.o
ar rcs $@ $^

myfunction.o: myfunction.c
clang -c $< -o $@

clean:
rm -f *.o *.a $(MYLIB)

install:    
sudo cp $(MYLIB) /usr/local/lib/$(MYLIB)


上述中 myfunction.c 及 myfunction.h 就是用 c 寫的程式碼. 最後如果要安裝到 /usr/local/lib 會需要用到 sudo 指令, 因此 user 必須要有寫入權限才可以, 對於將字串參數傳遞進 c 程式, 我試的結果要宣告程 const char * 才可以, 否則在 swift 編譯連結時會有錯誤訊息

一旦將程式除錯完畢, 就要準備好 swift 與 c 溝通的中間檔 module.modulemap,  範例內容如下:
         module Name [system] {
                header  "mymodule.h"
                link       "myfunction"
                export    *
         }
上述 header 指示要到那去尋找標頭檔 , 如果有多個標頭檔時, 可以另外寫一個標頭檔,裏面用 #include  指示將其它標頭檔包進來, 並將 module.modulemap 的 header 改成 umbrella header. 若是不想寫這種標頭檔, 那就把所有要包進來的檔放在一個目錄, 並將 module.modulemap 的標頭指示改成 umbrella 並提供該目錄名稱(最後一個字要加上/),
link 指示需要連結哪些程式庫(library)
最後全部需 export 出來讓 linker 可以找到裏面的程序符號, 以便相連結.
最開頭的 Name 就是將來寫 swift 程式要 import 進來的程式庫名稱, 他是可以自行命名的. 上述程序都完成後, 接著要將該 module所在的目錄用版本控制程式讓 git 來追蹤(因 swift 的 package manager 會用 git clone 指令來複製該模組), 底下列出一些常用的 git 指令, 需視需要加以添加
    git init
    git config user.name "YourName"
    git config user.email "name@somewhere"
    git add Package.swift module.modulemap Makefile
    git add example.c example.h
    git commit -m "This is an example"
    git tag 0.0.1

最後便是寫一個 swift 專案來測試程式, 該專案主目錄下的 Package.swift 需要作一些指示, 才能使用上述的 module, 範例內容如下:
         import PackageDescription
         let package = Package(
                     dependencies: [
                              .package(url:"to/module/path", version: Version(0,0,1) ..< Version(2,0,0) 
                       ]
              )
上述其實是 swift 語言的寫法, 他是說要使用到內部 PackageDescription 模組,將他含括進來運作,讓 package 初始化相依值( dependencices )  讓他成員的位置 (url)指向模組所在的目錄, 並且版本編號指定用 0.0.1 與 2.0.0 之間的檔案, 確定後把它下載到該專案下面(通常是目錄 Packages 內 )

而專案要寫的程式碼就在該專案下建一個目錄名稱, 定名為 Sources或 Source或 src 或 srcs 都可以, 將全部的原始碼放到該目錄下面. 最後下一個指令:
          swift  build
如果一切正常, 將會看到執行檔放在 .build/debug/ 目錄下



對於 c 常用的指標, 在 swift 語言定義比較嚴格, 對於參數的指標(&) , 僅可以在要傳給程序時才能使用, 而指標的宣告型別是 UnsafePointer<TYPE>,  UnsafeMutablePointer<TYPE>, 如果宣告成UnsafePointer<TYPE>, 在程序內就不得對它做寫入的動作. 而要傳進去的指標也只能是變數屬性, 如果傳進常數屬性指標將會產生錯誤(因常數屬性不得被修改).

   func show(ptr:UnsafePointer<Int>) {
       print("Pointer is:\(ptr)\n")
   }

   func inc(ptr: UnsafeMutablePointer<Int>) {
       ptr.memory += 1
   }
   var a=10
   show(&a)  // don't use constant property, show the address of the stored property
   inc(&a)     // don't use  constant property, increase 1 in the stored property

如果要對屬性指標直接處理, 也可以用 withUnsafePointer(&var_name) {  $0.memory }及 withUnsafeMutablePointer(&var_name) { $0.memory }, 如果使用 withUnsafePointer(&var_name) { $0.memory }則在 closure 內就不得對 var_name 做寫入的動作. 而要傳進去的指標也只能是變數屬性, 如果傳進常數屬性指標將會產生錯誤(因常數屬性不得被修改). 另外還要注意的是 closure 所傳回值的型別必須與 var_name 的型別相同

   var a=10
   let c=withUnsafeMutablePointer(&a) { (a) -> Int in
             a.memory += 1   // increase a
             return 1               // but return 1, return TYPE must be same as a
         }
   // now a=11, c=1

最後要提到的是如果用 c 寫的副程式使用了 variadic parameter 時, 像類似printf (const char *str, ...) 在最後的宣告有3個句點的副程式, 它是不能被 swift 程式直接呼叫的, 要解決這問題, 只能再寫一個 c 程式碼來橋接 swift 的程式, 這個程式是要給 swift 呼叫用的, 他必須要有明確數量的參數, 由該橋接副程式再去呼叫有 variadic parameter 的 C 副程式就可以了, 例如:

           int Myprintf(const char *fmt) // this function can be call by swift program
           {
                 // call a C function which need variadic parameters
                 if( fmt!=NULL )  printf(fmt);
           }


後記:

1. 實際上 C 與 Swift 都是將 Variadic parameters 一個一個塞到陣列裡去, 再將該陣列的指標傳過去的. 因此處理整數或符點數或字元時要注意兩邊的型別要一致才行

2. https://developer.apple.com/library/tvos/documentation/Swift/Reference/Swift_CVarArgType_Protocol/index.html
提到可將 c 所寫的副程式的 variadic parameters, 不要宣告成 int f( int, ...)的形式, 而是改用 int f(int, va_list arguments)的形式宣告, 這樣就可以直接接收不定參數陣列的指標, 之後再來加以運用.詳見下例:
         // C function
         #include "stdarg.h"      
         int SumOf(int count,va_list arglist)
         {
            int sum;
            for( sum=0;count>0;count--) sum += va_arg(arglist,int);
            return sum;
         }


         // Swift
  1. func swiftF(x: CInt, arglist: CVarArgType...) -> CInt {
  2. return withVaList(arglist) { SumOf(x, $0) }
  3. }
swiftF(10,1,2,3,4,5,6,7,8,9,10) // sum of 1...10, count=10
swiftF(5,1,2,3,4,5) // sum of 1..5, count=5

3. Swift 其實已內建許多 C 標準函式庫, 需要時需要將Foundation 及 Glibc 給 import 進來, 而 C 的字串型別在 swift 裡是 [CChar], 也就是字元陣列,要初始它可以利用它的建構式:
            var buf = [CChar](count:1024, repeatedValue:0)
上述 buf 定義了 1024 個字元陣列, 內容全是 0, 如果要將 buf 指標傳給 C 所寫的副程式, 用 &buf 就可以了.

4. 如果要將 C 的字串轉成 swift 的 String 型別時使用 String.fromCString(cstr) 來轉換, 他所轉初的字串型別是 String?, 也就是有可能是 nil 的字串型別.









 

2016年4月14日 星期四

swift 學習筆記 8: Initialization 及Deinitialization

Initialization: 初始化
Deinitialization: 消滅
stored property: 儲存式屬性, 像用 var 所定義出來的變數屬性或是 let 所定義出來的常數屬性等
computed property: 運算式屬性, 用程式運算出來的結果, 像是跑完 closure 的回傳值等

struc 及 class 的 stored property 必須要初始化,初始化 stored property  的方式可以直接給定一個初始值, 或是用初始化程序一一設定.如果自製型別裡有 stored property, 而它允許是 nil 值, 則應該將它設為 optionals. 就是在型別名後加問號'?'  因此以下敘述是合法的, 實際上 a 會被指定等於 nil
   class my {
          var a:String?
   }
但以下敘述是不合法的, 因為 stored property 必須要初始化
   class my {
          var a:String
   }// the above declaraction is not allowed
應該改成
   class my {
         var a:String=""
   }
或是改成用 init 程序來初始化它
  class my {
         var a:String
         init() { a=""  }
   }
let b=my()

Customizing Initialization: 客制化初始值是透果參數傳遞方式去給定, 這些參數稱做起始參數(Initialization parameters), 而起始參數的語法與 func 裏面的傳遞參數的語法是相同的,起始程序可以用不同形式但用相同 init 的名稱來初始化, 即使在型別裡設定為常數屬性, 仍然可以重新初始化, 例如

        class my {
               let name:String
               init(name:String) {  self.name=name }
               init()                     {  self.name=""  }    
        }
        let a=my()
        let a=my(name:"new")

Default Initializers: 就是 stored property 被宣告初始值時的預設初始程序
Memberwise Initializers for Structure Types: 對於結構 (struc)而言, 即使沒有自訂初始化程序, 但當客制化成員呼叫時, 他仍會按照成員一一初始化內部它的儲存式屬性(stored property) .因為結構是 value type, 如果沒用自訂初始化程序,當屬性宣告成常數時, 他會變成不可修改的,因此要再用客制初始它會變成不合法, 一旦自行定義 init 程序, 那 Memberwise Initializers 將會失效如同被覆蓋(override)一樣, 那在自行定義的初始化程序中,不管常數或變數, 即使有初始值, 仍然可以再重新客制化過, 像上面的 class 如果將它他改成 struct 也是可行的.
      struct my {
                let name="constName"
       } 
// it is not legal to assign the constant property in structure if not define the init() method
// let b=my(name:"New")  // illegal statement
因此要能再客制化的話, 只要將它改成變數就可以了
       struct my  {
                var name="varName"
        }
       let b=my(name:"New") // it is fine when name was declared as a variable

Initializer delegation for value type: 委託初始化數值型別, 代辦初始化數值型別, 就是初始化程序再去呼叫別的初始化程序就叫作 Initializer delegation. struc 及 enum 屬 value type, 不能繼承, 其代辦初始化可以較簡單, 但 class 就不同, 因為繼承到別人的屬性, 因此必須要適當的初始化 所有的儲存式屬性(stored property)


Designate Initializers: 指定特別的初始程序, 是隸屬該 class 內的主要初始化程序, 並且傳遞(chain)呼叫上層 class 的初始化程序, 因此 class 通常只有一個 designate Initializerr
Convenience Initializers:  方便的初始程序, 他最後會呼叫 designate Initializer 或別的 Convenience Initializers, 如果不需要也可以不用寫, 它必須用關鍵字 convenience 加在 init() 之前

消滅程序使用deinit() 來定義, 只有 class 可以定義消滅程序


2016年4月12日 星期二

swift 學習筆記 7: Subscripts 及 class

Subscripts: 指標程序, 索引, 子件, 子程序
分類(class),結構(struct)及列舉(enum)等所生生的型別可以用來定義出子程序,而這些型別所產生的個體,可以透過類似中括號內索引的方式取得個體的成員, 他的語法定義與實體程序(instance method是相同的, 子程序有兩個預設成員 get 及 set(newValue), 該程序與運算式屬性(computed property)語法也是相同的,如果定義多個子程序, 就可以讓它根據不同的輸入型別採取不同的動作, 這種稱為子程序過載 subscript overloading, 使用關鍵字 subscript 來定義子程序.例如
class newClass {
       subscript(a:Int) -> Int{
             get {
             }
             set(newValue) {
             }
       }
}

就像唯讀型運算式屬性那樣, 子程序如果沒有 set 成員時, 同樣可以移除 get 關鍵字, 形成最簡單的子程序語法,而它就稱為唯讀型子程序, 例如下面最簡單的子程序過載
class newClass {
       subscript(i:Int) -> Int {
         return i
        }
       subscript(a:String) -> String {
         return i
       }
}
let a=newClass() // a[i] = i

子程序可以輸入任何型別任何數量參數, 參數也可以是不定長度的, 也可以傳回任何型別, 但不能用 in-out 參數, 或是使用預設參數值

多參數的子程序運用在二維方陣上就是一個很好的例子

class: 分類
分類與其他型別最大不同就是可以繼承,被繼承者稱為父類別(superclass), 繼承者稱為子類別(subclass), 沒有繼承任何類別者稱為基礎類別(baseclass). 當定義類別時, 在新類別名稱後面加上 ":父類別" 便可以繼承來自父類別的各種屬性及方法, 在swift 裡只可以繼承一個類別但可以引用多個協定, 而父類別要擺在第一個, 其它協定則以逗號相互隔開跟隨其後. 如果要用父類別的屬性或方法使用 super加句點語法直接取用, 如要覆蓋父類別的屬性或方法, 使用關鍵字 override 加諸在父子都有的名稱之前.

對於所繼承的屬性如果是唯讀屬性的話, 可以透過 override 並提供新的 get 及 set 變成可讀寫的屬性,但不能將繼承而來的可讀寫屬性變成唯讀屬性. 透過覆蓋屬性(override property)可以替屬性適當的添加屬性觀察者, 但不能對所繼承的常數儲存式屬性(constant stored property)或是唯讀型運算式屬性(read-only computed property)添加屬性觀察者, 因為這些繼承來的屬性都不能被修改. 另外對於所繼承的屬性也不可將它的屬性觀察者(property observer)及設定者(settert)
同時覆蓋. 為了避免被覆蓋, 利用關鍵字 final 加諸再前, 可以避免被繼承. 另外將關鍵字 required 加在 func 前面時將會強制繼承者必須實現該方法.

新型別的分類名稱尾隨加上左右雙括號()便可形成新個體, 稱為 class instance



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














           








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