2016年4月30日 星期六

c 模擬 try-catch-throw 的運作

在 C 程式裡, 並沒有 try-catch-throw 的語法, 但透過 setjmp 與 longjmp 搭配 switch-case 語法便可以模擬出它的動作. setjmp( myjmp ) 需先設定好 jmp_buf 型別的變數 myjmp, 他作用是單純將下一個程式碼的位置存到變數中, 第一次結束後會傳回 0, 而 longjmp 則是利用剛剛 setjmp 的變數內容加上回傳碼(return code),透過 return 方式讓程式直接轉移到剛剛 setjmp 所處的地方. 但此時 setjmp 的傳回直是由 longjmp 所指定的, 先看以下程式碼:

            enum MyExcept {  TryBegin=0, e1,e2,e3    };
            int main() {
            jmp_buf myjump;      // 先宣告一個 jmp_buf 變數
        // try block setting buffer
       switch( setjmp(myjump)  ) { // 呼叫 setjmp 來保存 programm counter, 第一次結束後傳回 0
case TryBegin:  // 因此 setjmp 完, 開始執行, 此段區塊類似 try 內部的程式區塊
        printf("Try begin: \n");

// throw any exccep here, 在此區塊內(包含所呼叫的副程式也行), 可以任意丟出任何錯誤碼, 讓此區塊外部的 catch 區塊去執行
longjmp(myjump,e2); // longjmp 類似 Throw 指令, 設定 setjmp 回傳碼由第二個參數取代
                                // 在 try 區塊內如果有丟出任何錯誤碼, 這底下一直到 break間的程式碼是不會執行的
printf("It shuold not happen if throw executed\n");
break;
// catch block, 以後的每一 case 區塊, 就類似是一段 catch 區塊
case e1:printf("e1 happen\n");
break;

case e2:
printf("e2 happen\n");
break;

case e3:
printf("e3 happen\n");
break;

default:
printf("e? Not defined\n");
break;

} // Endof try-catch-throw, 這是  try 的最後一段程式碼

    return 0;
}

如果將上述區快改以 macro 來寫就變成:

  enum MyExcept {  TryBegin=0, e1,e2,e3    };
  #define  jmp_buf myjump; Try switch( setjmp(myjump)  )    {   case TryBegin:
  #define  Throw(e) longjmp(myjump, e)
  #define  Catch(e)       break; case (e):
  #define  Endtry           }
  main()
   {
        Try  {
printf("Macro begin:\n");
Throw(e3);
}Catch (e1) {
printf("Error %d\n",e1);
}Catch (e2) {
printf("Error %d\n",e2);
}Catch (e3) {
printf("Error %d\n",e3);
}Endtry

    return 0; 
}

// 程式輸出:
Macro begin:
Error 3


如果將 switch-case 改成用 if-else 來寫就變成:
 enum MyExcept {  TryBegin=0, e1,e2,e3    };
#define Try jmp_buf myjump;int my_a=setjmp(myjump);if( my_a==0 )
 #define Throw(e) longjmp(myjump, e)
 #define Catch(e)  else if( my_a==e     )

  main()
  {
Try {
printf("Macro2 begin:\n");
Throw(e2);
}Catch(e1) {
printf("Error %d\n",e1);
}Catch(e2) {
printf("Error %d\n",e2);
}Catch(e3) {
printf("Error %d\n",e3);
}

    return 0;
  }

// 程式輸出:
Macro2 begin:
Error 2


p.s. longjmp(myjump,value) 如果設定傳回值 value 是 0, 將會自動被改成 1, 避免造成無限循環






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











2016年4月11日 星期一

swift 學習筆記 4: Class and Structure

Class       : 抽象化分類型別, 實體化後稱作分類體
Structure : 抽象化結構型別, 實體化後稱作結構體

分類(class)及結構體(structure) 是程式建構中一般常用且很彈性的結構區塊,  裏面定義了屬性(property)及方法(method)用來運作分類及結構體的功能, 對於常數(constant),變數(variable)及程序(function) 它們都同樣使用相同的語法

不像其它程式語言, swift 並不需要將自訂的分類及結構分離成介面(interface)與實現(implementation)兩個檔案, 而是將分類及結構體定義在一個檔案裡, 其他程式便可以用其外部介面直接使用

分類的實體(instance)傳統上認知它就是一個物件. 但在 swift 裏面, 它更像是其他程式語言裡的程序, 本章大多數所敘述的程序都可以指定給實體, 因此 instance 被當成作是更廣泛的用語

分類與結構體的比較
相同點:
1. 定義屬性來保存數值(value)
2. 定義方法提供運作
3. 定義下標索引(subscript)方法,透過下標語法來取用內部數值
4. 定義起始程序設定初始狀態
5. 可以被擴展(extended)將預設的運用加以擴充
6. 可以與協定搭配來提供特定功能的標準運作

分類另外有著與結構體所不同的能力:
1. 分類可以繼承其它分類的特性
2. 型別標示(type casting)讓運行時可以檢視及解釋物件(class instance)的型別
3. 消滅程序(deinitalizer)讓物件可以釋放它所指定的資源
4. 參考計數(Reference counting)允許超過一個以上的物件來引用

宣告的語法
分類及結構體有相同的語法來宣告, 分別採是用關鍵字  class 及 structure , 且整個定義內容都是放在大括號 {} 裡頭, 例如

struct MystructDefinition {
// define propertys and methods
var width=0;
}

class   MyclassDefinition {
// define propertys and methods
var st =MystructDefinition()
var name:String?
}

let myInstance=MyclassDefinition()
let myInstance.name="A new instance"

當定義了一個分類及結構體後, 實際上是定義了一個全新的型別, 標準的 swift 型別名稱是將首字頭大寫相連接(NewClassName), 而屬性及方法的名稱則是用第一個字小寫(myPropertyOrMethod)命名,有別於型別名稱作區分, 可以參考上述的內容.

其中 MystructDefinition() 會將新的結構體實體化, 而 name 賦予了一個字串型別的 nil (表示空字串), 他是一個可無(optinonal)宣告
而 MyclassDefinition() 則是將分類允以實體化成物件, 如要取用物件屬性或方法等成員時, 使用句點語法 '.' 直接取用, 參考下面範例:

Class MyNewClass{
var name:String?
}
let a=MyNewClass()          // a is a constant of  new Class, so it can't be assignd in the next time.
let c=MyNewClass()          //  c is different from with a
a.name="My Name"         // but a.name is a variable defined in the class.
//  a class instance is a reference type, even if it is a constant reference, it's property can also be assigned a new value.
// but a structure instance is a value type, if it is a constant value, it's propety can't be assigned even if it was declared as var in structure.
c.name="Youe Name"     //  c also has a name property
let b=a                                    //  b refer to the same class as a
b.name="New Name"       //  a.name will be modified,too. but c.name doesn't

在上述例子中,新分類後面接了小括號(),就是將class實體化成一個新instance,該instance稱做class instance.
如果新結構後面接了小括號(),也是將struct實體化成一個新instance,該instance稱做structure instance.

所有 swift 的基本型別像是整數, 浮點數, 邏輯, 字串, 陣列, 字典等型別都是數值型別(value types), 它們是以結構體在幕後實現的.

在 swift 裡面,所有結構體及列舉體也都是數值型別, 這意味著當這些結構體及列舉體被傳遞(指定)時,將會整個複製過去,因此他並不會影響到別的實體

但分類體卻是一個參考型別(reference type),因此當結構體被實體化後,指定給一個常數或變數時,它其實是指向該分類體. 所有的參考到該分類體的instance都可以改變其內容, 且改的都是同一個實體

因此將 class instance 指定給一個新變數, 除非是實體化成一個新的分類體(如上述的 c), 否則只是將指標傳遞過去而已(如上述 let b=a), 並非將整個分類體複製過去

因於分類是屬於參照型別(reference type), 有別於數值型別(value type), 有可能在幕後好幾個常數或變數參考到相同的分類體(有別於結構體與列舉體,他們是將整個物件複製過去), 有時候要找出兩個常數或變數是否確實參考到相同的實體,可以透過用等效運算子( ===  或  !== )來檢視, 須注意的是, 等效並不是相等的意思,  等效運算指的是有無參照到相同的分類體


定義客制資料型態可以採用分類或結構當成是程式碼的架構區塊, 然而結構體總是傳值,而分類體總是傳參,這意味著他們適合不同的工作,對於要決定一個專案的資料結構及功能,需要考慮的是到底分類體好還是結構體好,底下有一些準則可以考慮使用結構
1.結構主要功用是將相當少量及簡單的資料數值加以打包
2.可以合理的預期打包的數值將會被複製而不是去參照
3.由結構體所儲存的任何屬性,他們本身是數值型別,同時預期這些東西將會被複製而不是去參照
4. 結構體不需要繼承別的已存在類型的屬性或行為

一些候選使用結構體的例子包括
1. 幾合型態的尺寸,譬如長寬都用精密(Double)型別
 2. 一系列範圍參考方式,譬如起點及長度都用整數(Int)型別
 3. 三維座標系統的點, 譬如x,y,z 屬性都用精密(Double)型別

在 swiftt 裡, 基本資料型別像 Sting, Array, Dictionary 是使用結構體來實現,這意味著當它被賦予後指定給一個新常數或變數時,將會被整個複製過去, 或者當他們傳給程序(function)或方法(method)時也是如此(實際上, swift 在幕後, 只有當需要複製時才會執行搬運的動作. 他會處理所有數值的複製工作.以保證效能最佳化,你並不應該為了搶佔效能而去避免指定工作)
但在 Foundation 裡 NSString, NSArray, NSDitionary 是用分類而非結構來實現的, 與上述行為並不相同, 因此再 Foundation 裡的字串,陣列,及字典等總是將已存在本尊的指標傳過去,而非將本尊整個複製過去



解決 geany 無法使用 gcin 的問題

在 linux mint 上已經裝了 gcin, geany, 但發現在 geany 無法使用 gcin, 他真正的問題是因為快捷鍵 ctrl-space 相衝, 解決的辦法是將它改掉就可以了.

1. 將主選單的  求助(H) -> 快速鍵(K) 叫出來, 往下拉到編輯器,再往下找有個項目 "補完字詞" 所設定的按鍵, 他正好是 Ctrl+Space, 點選 Edit 按鈕

2. 接著會跳出快速鍵變更的選單, 往下拉到編輯器,再往下找有個項目 "補完字詞" 已經反白, 直接點下去再按 變更(H) 按鈕

3. 接著它會跳出視窗訊息, 跟著訊息做, 直接敲下別的組合鍵,讓他不再是 Ctrl-Space 就好. 當然也不要跟別的功能鍵互相衝突, 敲完組合鍵, 他會顯示你所敲的組合鍵,確認後再按 OK 按鈕,變更設定完成

4. 關掉重新打開 geany, 應該就可用 gcin 來切換輸入法了 


swift 學習筆記 3: Dictionary

字典型別是用成對 key:value 元素所組成的集合, 各元素以逗點分開, 使用 [:,:,...] 組成集合, 它其實也沒有順序可言, 但很重要的特性是 key 的值絕對不能有重複, 字典的型別宣告使用Dictionary<TYPE of key:TYPE of value> 來敘述, 簡單的也可用 [TYPE of key:TYPE of value] 來敘述, 例如:
let mydic:[Int:String]=[3:"a",2:"b",1:"CC"]

當然 swift 也可自動推斷出上述的型別, 因此 TYPE 其實也可以省略不寫,下面字典宣告也是符合語法的
var vardic=[2:"a",1:"b",3:"CC"]

字典型別可以透過 key 當索引值, 直接取用 value, 例如
vardic[1]="SDSD"  // 直接更改 key=1 的值, 現在 vardic 將變成是  [2:"a",1:"SDSD",3:"CC"]

如果要清空字典. 可用 [:] 來直接賦予便可, 前提當然該 instance 必須之前就已宣告成變數
var dic=[2:"DD",4:"ww",5:"DDD"] // 宣告字典變數並實體化 instance
dic=[:] // 清空字典內容

透過 key 來取用字典內其所對應的值(value)時, 有可能該 key 並不存在, 因此可運用 optional binding (if let) 來運作較保險


如果要刪除字典內的成員(成對元素), 可以透過將 nil 值賦予字典 key 所對應的成員, 例如
vardic[3]=nil   // 刪除 key=3 的成員


如要列出字典內所有 key, 可以用 for-in 迴圈搭配字典的 .keys 來一一列出, 例如
var vardic=[2:"a",1:"b",3:"CC"]
for i in vardic.keys {
   print("Key:\(i)")
}

如要列出字典內所有 value, 可以用 for-in 迴圈搭配字典的 .values 來一一列出, 例如
var vardic=[2:"a",1:"b",3:"CC"]
for i in vardic.values {
   print("Value:\(i)")
}


字典的屬性及工具:
isEmpty: 驗證是否是空字典
updateValue(,forkey:)
removeValueForKey()



swift 學習筆記 2: Set

集合(Set)型別適合用在秩序並不是很重要的搜集(collection)上, 它最重要的特性是其成員都是唯一, 並不會重複, 如有重複的值, 將會被自動剔除掉只剩一個. Character,  StringIntDouble, and Bool等型態的元素等都可當作集合內的元素(value), 由於 Set 無簡單的宣告形式, 只能用 :Set<TYPE>的方式來宣告集合的類型. 例如字串的集合:
let myset:Set<String> =["a", "bb", "ccc"]

集合不同於 Array, 他並沒有順序, 但可以利用 sort() 來排序. 降羃排序用 sort(>) , 升羃排序(預設由小到大排序)用 sort(<) 因此 sort()等同於 sort(<)例如:
myset.sort(>)

Set 重要的工具(method)及屬性(property):
count : 計錄內部成員個數
isEmpty: 當集合內無成員時,其值為 true
insert() : 插入新成員, 如新成員已存在, 將會忽略此運作
remove(): 移除舊成員, 避免成員不存在可以使用 optional binding ( if let ) 來運作.
contains(): 檢視成員是否存在

集合的算術運算:
intersect(): 兩者交集後產生新集合
union(): 聯集後產生新集合
exclusiveOr(): 聯集並移除兩者的交集後產生新集合
subtract(): 相減後產生新集合

集合的邏輯運算
== : 比較兩個集合是否相同
isSubsetOf(): 判斷是否是括符內集合的子集合
isSupersetOf(): 判斷是否是括符內集合的父集合
isStrictSubsetOf()
isDisjointWith()


var c:Set<Int>=[1,6,5,6,1,6,6,7]    // in fact, c will be {5,6,7,1}
if let c.remove(9) {
      printf("Found and remove it")
} else {
      printf("Member is not exist")
}

c.insert(8) // c will be {5,6,7,1,8 }








2016年4月10日 星期日

swift 學習筆記 1: enum

enum 是用來自訂"列舉型別", 列舉成員屬性用 case 來宣告. 如下所述:

enum Myenum {
case head, body, leg, hand
}

宣告變數隸屬 Mynum 型別, 初始值為 head:
var a: Myenum = Myenum.head

上述型別(:Myenum)其實 swift 可以推論出來, 可以不寫, 同樣合語法.
var a=Myenum.head
// 一旦 type 確認後, 修改值直接用具句號取值就可
a=.body

變數 a 經上述型別設定後, 則只能賦予 Myenum.head Myenum.body, Myenum.leg, Myenum.hand 等四種值. 而將變數 a 列印出來會發現, 它只可能是集合 { head, body, leg, hand } 中的成員之一.

利用協議(protocol) 來定義自體特異功能 (Mutating Method)

protocol Myprotocol {
mutating func toggle()
}

也些時候 instance (實體, 實例, ...?) 的自體內容需要被改變, 它可透過自我 method (方法, 工具, 功能, 運功, 特異功能 ...?) 來改變. 如要強制用此種方式修改就稱作協定, 而型別如果運用(遵循)此協議的必須將它實現, 由於它可能會動用到自體內容, 因此該 method 必須加上 mutating 的前置字眼

底下定義了一個列舉型別 Myswitch{Off, On}, 須遵循 Myprotocol, Myprotocol 裏面只有一個用來切換 (toggle) 的功能需要被實現, 而該切換的動作會改變到本身(instance self), 故此方法必須標明是 mutating, 否則 compile 會報出錯誤訊息

  1. enum Myswitch: Myprotocol {
  2. case Off, On
  3. mutating func toggle() {
  4. switch self {
  5. case Off:
  6. self = On
  7. case On:
  8. self = Off
  9. }
  10. }
  11. }
  12. var sw=Myswitch.On         // instantiate sw 被實體法, 它隸屬 Myswich 型別
  13. print("Now sw state is:\(sw)") // sw=On
  1. sw.toggle() // 使用特異功能來切換 sw 的值
  2. print("Now sw state is:\(sw)") // sw=Off
  3. // mutating func --> 我把它翻譯成特異功能, 因為它會改變 self.







2016年4月9日 星期六

玩玩 swift

swift 是開發 iOS 程式重要的語言工具之一, 目前是 open source,

官方網站:  https://swift.org/

首先在 linux 下需架好環境:

參考網址
https://swift.org/getting-started/#installing-swift
https://linuxmint.tumblr.com/post/135280204927/how-to-use-swift-programming-language-in-ubuntu


1.  swift 官方下載: https://swift.org/download/#releases
    https://swift.org/builds/development/ubuntu1510/swift-DEVELOPMENT-SNAPSHOT-2016-01-25-a/swift-DEVELOPMENT-SNAPSHOT-2016-01-25-a-ubuntu15.10.tar.gz.sig
https://swift.org/builds/development/ubuntu1404/swift-DEVELOPMENT-SNAPSHOT-2016-01-25-a/swift-DEVELOPMENT-SNAPSHOT-2016-01-25-a-ubuntu14.04.tar.gz
     p.s. 因 release 版未將 swift-build 放入, 最好下載上述開發版
2. 使用 tar 解出swift 套件:  tar -zxvf  xxxx-xxx.xz
3. 設定好環境變數 PATH 指向swift 編譯程式的 bin 目錄: export PATH= ....:$PATH
4. 安裝開發 swift 所需要的相關套件:   apt-get install clang   libicu-dev
5. 驗證是否安裝成功:  swift -version
6. swift 交談命令程式(REPL), 直接執行 swift 就可看到:  swift
     在 REPL 中鍵入 :q 離開 swift 交談視窗
7. 建立編譯環境
     a. 建立一個目錄當專案:  mkdir Hello
     b. 進入該專案目錄:  cd Hello
     c. 建立 Package.swift 檔案: touch Package.swift
     d. 建立原始碼目錄: mkdir Sources
     e. 建立原始碼主檔: touch Sources/main.swift
     f. 編輯主檔: gedit Source/main.swift
         將程式碼 print("Hello World") 鍵入主檔(main.swift)中
     g. 開始編譯: swift build

         編譯程式將 Source/main.swift 加以編譯程可執行檔,使用 Package.swift 所在的目錄名稱來命名可執行檔, 編譯好的執行檔將會放在 .build/debug 目錄內

     h. 執行編譯過的程式:      .build/debug/Hello

基礎知識:
常數宣告 let  a=...
變數宣告 var a=...

常數型別(TYPE)宣告   let a:TYPE=...
變數型別(TYPE)宣告  var a:TYPE=...
其中 TYPE 可以是: Int, Uint, Float, Double, String
(整數, 浮點數, 更精密浮點數, 字串)

許多型別, 其實swift 會自行推論出正確的型別,
例如 var a=1   ---> a 是整數
         var b="sasds"  --->  b 是字串
         var c=1.23       --->  c 是 Double
...等等, 因此上述的 a,b,c 其實不需宣告型別(TYPE) 也可以


何謂 Tuples: 將各種型別組合成一個集合, 例如 let a=("1",2,3.1415)
其中 a.0 等於字串 "1", a.1  等於整數 2, a.2 等於浮點數 3.1415
將Tuple 值用括號可以分離出各元素:
例如  let a=("1",2,3.1415);  let  (a0,_,a2)=a , 其中 a0="1", 第二個使用底線忽略, a2=3.1415

陣列型別使用相同型別的元素用 [,,] 來構成, 例如 let a=[1,2,3]
字典型別用key:value成對元素陣列所構成 let dic=["a":"dsd","2":"kiki"]; let b=dic["a"];
其中 key 與 value 型別必須分別統一使用相同型別  let dic=[1:"dsd",2:"kiki"]; let b=dic[1];

何謂 optionals (可有可無)變數, 例如宣告 var a:Int? =404, 其中後面 '?' 就已代表 a 宣告成可有可無的整數型別, 可有可無意味著:變數未指定也可, 此例 a=nil, 或已指定完, 此例 a=404
但不指定 optionals 的變數, 則不能付與 nil 值

當一個可有可無變數要強制取用其值時,可以再後面加上驚嘆號 '!',強制取用,但一旦此變數若未指定.則程式可能會當掉
因此可以透過 optional binding 敘述來檢查 , 避免錯誤
if let  constantname = someOptionalvalue {
//   ...
}

錯誤處理程式
func catThrowAnError() throws {
 // ..程式可能會丟出依些錯誤訊息
}


do {
          try catThrowAnError()
       
          // ... No error, continue
     }   catch  Error1.xxx {        
          // ...
     } catch  Error2.xxx {        
          // ...
      }
自訂驗證程序宣告, 當 condition 有錯誤時, 則停止程式, 並丟出錯誤訊息:
   assert(condition,"Error message");

字串可以相加  "Hello" + "World"
餘數  (a%b)
範圍 1...5     指的是 1 到 5
範圍 0..<10 指的是 0 到 9 ( 從0 開始, 小於 10)

the nil coalescing operator ??
let a:Int?=nil      // define an optional Interger constant, assign it to nil
// if a is not nil return a , else return 0
let b= ( a != nil ?  a!: 0 )
// same as expression    a ?? 0
let b=( a ?? 0 )

Type casting: 成型

字串的method
1. characters.count
2. append
3. insert

var a="SDSD"
a.characters.map {$0} // list all the character

單一字元(single character)並不等於單一字元的字串(a string with single character), 在於它是不同型別, 字元用 Character 來宣告, 字串用 String 來宣告, 使用前後雙引號時將自動判斷為字串型別

swift 語言有3種主要的收集型別(collection type): 陣列(Array),  集合(Set), 字典(Dictionary, 這些行別都是 mutable, 可修改的. 但若宣告程常數就不得修改了

在 swift 裡的條列式流程 switch .. case, 在case 執行完後將會自動跳離, 不像 c 語言要加入 bread 來跳開. 但可以用 fallthrough 讓它往下繼續執行, switch 可以用 tuple 當成多變數條件式, 如果無關係的條件, 可以用底線 '_' 讓它忽略該條件

switch (a,b) {
    case (_,0) :
                      ...
    case (0,_) :
                       ...
    default:
                       ...
}

value binding: 數值綁定
在switch-case 的條件式裡可以用 let 語法來綁定數值(value binding)讓他可以在該條件成立下取用,例如:
var a=(2,3)
// ...
 switch  a {
   case (let b,_) :
       print("\(b)")
   case (_,let c) :
       print("\(c)")
   default:
      print("OK")
 }

label: 標籤可以用名稱加帽號 lableName: 加註在程式迴圈上, 之後用 break lableName, 或 continue lableName 來控制程式流程, break lableName 用來結束迴圈內程序而跳到所標註迴圈外再往下執行. 而 continue lableName 除了放棄迴圈程序外, 並重新跳到所標註迴圈開頭地方再執行

guard  指令類似 if 的語法, 但 guide 必須要有 else 指令附加在後面, 而且他通常是用在func裏面,最後要有 return, 它預期條件式是對的,一旦不對才執行後面 else 內的指令,可以減少在對的條件成立時再寫指令
func my(b:Int) ->  {
     guard  b>3 else { print("OK"); return; }  
}
my(4)  // OK
my(3)  //

初始化消滅:當抽象型別像分類(class),結構(struc)及列舉(enum)要實體化成個體時, 他會執行該型別的的初始化程序叫 init(). 只有分類所產生的個體, 當要消滅時才會執行消滅程序叫作 deinit(), 對於儲存式屬性, 可以在型別宣告時就直接給初始值, 也可以透過初始化參數設定該屬性.初始化參數的語法與 func 裏面的參數語法是相同的. 對於常數型態的屬性仍然可以透過上述初始化程序重新設定它, 之後常數就不得再更改了. 對於分類所產生的個體而言, 常數屬性不能被子類別的繼承者所修改









改用 Tor browser 匿名瀏覽, 真正隱私不被追蹤的瀏覽器

只要身處在網際網路的世界, 其實行蹤早已暴露, 只要有心人士要追蹤,其實是逃不掉的,最近看到一個瀏覽器宣稱可以防止這樣的事情,當用的人愈多,其實行蹤愈安全,他的封包是透過
Tor節點來加密保護, 算是相當安全的連線. 可有效防止像 "traffic analysis" 分析軟體解析出瀏覽者的網路使用行為
參考 Tor 的官方文章: https://www.torproject.org/about/overview.html.en
他闡述了 Tor 的運作方式,且它本身是一個 open source, 由一群自願者所開發,值得推荐使用
官方下載區:
https://www.torproject.org/projects/torbrowser.html.en#linux

2016年4月5日 星期二

玩玩 NDK

NDK 是用 c 或 c++ 來寫 android  app.
很容易用來開發手機上使用的執行檔, 像是簡單的 hello world 等.
下載程式:  http://developer.android.com/ndk/downloads/index.html
我目前試用的是 r11c linux x86 64 bit 版 android-ndk-r11c-linux-x86_64.zip
但該網站建議大家還是用 SDK 開發 android app 較適合.

目錄 helloworld 內容共含 3 個檔案
main.cpp          --- 主程式
Android.mk      ---  給 ndk compliler 用的 makefile
Application.mk --- 給 ndk 產生一些平台要用的指示檔


main.cpp 主程式內容:
// begin of main.cpp
#include "iostream"
#include "stdio.h"
using namespace std;
int main(int argc, char **argv)
{
int i;
   cout << "Hello world" <<  endl;
   printf("Hi printf\n");
   for(i=0;i
<argc;i++)   {
       printf("argv[%d]:%s\n",i,argv[i]);
    }
    return 0;
}
// end of main.cpp 

Android.mk 檔案內容:
# begin of Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := main.out
LOCAL_C_INCLUDES +=
LOCAL_SRC_FILES := main.cpp
include $(BUILD_EXECUTABLE)
# end of  Android.mk

Application.mk 檔案內容:
#  begin of Application.mk
APP_STL := stlport_static
APP_PLATFORM := android-19
APP_BUILD_SCRIPT := Android.mk
#  end of Application.mk


使用 bash 執行以下 2 行 shell script 來設定好環境變數, 假設 ndk 掛在 /mnt/rw/ndk 上
export PATH=/mnt/rw/ndk/android-ndk-r11c:$PATH
export NDK_PROJECT_PATH=.

cd 到 helloworld 目錄內, 使用 bash 執行以下 shell script 來編譯程式:
ndk-build NDK_APPLICATION_MK=./Application.mk
NDK 會把輸出檔  main.out 放在 libs\armeabi 目錄下, 將他上傳到手機上或模擬器上設成可執行檔, 並開啟 terminal 執行即可.
 
假設手機的 ip address 為 192.168.0.5,且手機要開啟 adbd, listen port 預設為 5555.  底下使用 adb 來連接手機並上傳檔案到手機上讓 shell 去執行:
adb connect 192.168.0.5:5555
adb push libs/armeabi/main.out /data/local
adb shell  chmod 755 /data/local/main.out
adb shell /data/local/main.out 1 2 3

輸出:
Hello World
Hi printf
argv[0]:/data/local/main.out
argv[1]:1
argv[2]:2
argv[3]:3