2016年2月9日 星期二

iOS筆記:Swift(4)

Methods

Methods are functions that are associated with a particular type. Classes, structures, and enumerations can all define instance methods, which encapsulate specific tasks and functionality for working with an instance of a given type. Classes, structures, and enumerations can also define type methods, which are associated with the type itself. Type methods are similar to class methods in Objective-C.

Structures and enumerations can define methods in Swift is a major difference from C and Objective-C. In Objective-C, classes are the only types that can define methods. In Swift, you can choose whether to define a class, structure, or enumeration, and still have the flexibility to define methods on the type you create.

Instance Methods

實例方法是屬於某個特定類別、結構或者列舉型別實例的方法。實例方法提供存取和修改實例屬性的方法或提供與實例目的相關的功能,並以此來支撐實例的功能。實例方法的語法與函式完全一致

    class Counter {
        var count = 0
        func increment() {
            ++count
        }
        func incrementBy(amount: Int) {
            count += amount
        }
        func reset() {
            count = 0
        }
    }

    let counter = Counter()
    // the initial counter value is 0
    counter.increment()
    // the counter's value is now 1
    counter.incrementBy(5)
    // the counter's value is now 6
    counter.reset()
    // the counter's value is now 0

The Counter class defines three instance methods:
* increment increments the counter by 1.
* incrementBy(amount: Int) increments the counter by a specified integer amount.
* reset resets the counter to zero.

Local and External Parameter Names for Methods

Function parameters can have both a local name (for use within the function’s body) and an external name (for use when calling the function), as described in Specifying External Parameter Names.

    class Counter {
        var count: Int = 0
        func incrementBy(amount: Int, numberOfTimes: Int) {
            count += amount * numberOfTimes
        }
    }

incrementBy(_:number Of Times:)方法有兩個參數: amount和numberOfTimes。默認情況下,Swift 只把amount當作一個局部名稱,但是把numberOfTimes即看作局部名稱又看作外部名稱。下面調用這個方法:

    let counter = Counter()
    counter.incrementBy(5, numberOfTimes: 3)
    // counter value is now 15

The self Property

型別的每一個實例都有一個隱含屬性叫做 self , self 完全等同於該實例本身。你可以在一個實例的實例方法中使用這個隱
含的 self 屬性來參考當前實例。

只要在一個方法中使用一個已知的屬性或者方法名稱,如果沒有明確的寫 self ,Swift 假定是指當前實例的屬性或者方法。

func increment() {
    self.count++
}

Modifying Value Types from Within Instance Methods

Structures and enumerations are value types. By default, the properties of a value type cannot be modified from within its instance methods.
但是,如果你確實需要在某個具體的方法中修改結構或者列舉的屬性,你可以選擇變異(mutating)這個方法,然後方法就可以從方法內部改變它的屬性;並且它做的任何改變在方法結束時還會保留在原始結構中。

要使用變異方法, 將關鍵字mutating 放到方法的func關鍵字之前就可以了:

struct Point {
  var x = 0.0, y = 0.0
  mutating func moveByX(deltaX: Double, y deltaY: Double) {
    x += deltaX
    y += deltaY
  }
}
var somePoint = Point(x: 1.0, y: 1.0)
somePoint.moveByX(2.0, y: 3.0)
println("The point is now at (\(somePoint.x), \(somePoint.y))")
// 輸出 "The point is now at (3.0, 4.0)"

Assigning to self Within a Mutating Method

變異方法能夠賦給隱含屬性self一個全新的實例。

enum TriStateSwitch {
  case Off, Low, High
  mutating func next() {
    switch self {
    case Off:
      self = Low
    case Low:
      self = High
    case High:
      self = Off
    }
  }
}
var ovenLight = TriStateSwitch.Low
ovenLight.next()
// ovenLight 現在等於 .High
ovenLight.next()
// ovenLight 現在等於 .Off

Inheritance

在 Swift 中,類別可以呼叫和存取超類別的方法,屬性和下標腳本(subscripts),並且可以重寫(override)這些方法,屬性和下標腳本來優化或修改它們的行為。

下面的範例定義了一個叫Vehicle的基類別。這個基類別宣告了兩個對所有車輛都通用的屬性(numberOfWheels和maxPassengers)。這些屬性在description方法中使用,這個方法回傳一個String型別的,對車輛特征的描述:

class Vehicle {
    var numberOfWheels: Int
    var maxPassengers: Int
    func description() -> String {
        return "\(numberOfWheels) wheels; up to \(maxPassengers) passengers"
    }
    init() {
        numberOfWheels = 0
        maxPassengers = 1
    }
}

如果要創建一個Vehicle類別的新實例,使用建構器語法呼叫上面的初始化器,即類別名後面跟一個空的小括號:

let someVehicle = Vehicle()

Subclassing

以下定義一個新的類別叫Bicycle,它繼承了Vehicle的特性:

class Bicycle: Vehicle {
    init() {
        super.init()
        numberOfWheels = 2
    }
}

Bicycle類別定義了一個建構器來設置它定制的特性(自行車只有2個輪子)。Bicycle的建構器呼叫了它父類別Vehicle的建構器 super.init(),以此確保在Bicycle類別試圖修改那些繼承來的屬性前Vehicle類別已經初始化過它們了。

不像 Objective-C,在 Swift 中,初始化器預設是不繼承的

Overriding

A subclass can provide its own custom implementation of an instance method, type method, instance property, type property, or subscript that it would otherwise inherit from a superclass. This is known as overriding.

如果要重寫某個特性,你需要在重寫定義的前面加上override關鍵字。
override關鍵字會提醒 Swift 編譯器去檢查該類別的超類別(或其中一個父類別)是否有匹配重寫版本的宣告。這個檢查可以確保你的重寫定義是正確的。任何缺少override關鍵字的重寫都會在編譯時被診斷為錯誤。

在合適的地方,你可以通過使用super前綴來存取超類別版本的方法,屬性或下標腳本:

  1. 在方法someMethod的重寫實作中,可以通過super.someMethod()來呼叫超類別版本的someMethod方法。
  2. 在屬性someProperty的 getter 或 setter 的重寫實作中,可以通過super.someProperty來存取超類別版本的someProperty屬性。
  3. 在下標腳本的重寫實作中,可以通過super[someIndex]來存取超類別版本中的相同下標腳本

Overriding Methods

You can override an inherited instance or type method to provide a tailored or alternative implementation of the method within your subclass.

下面的範例定義了Vehicle的一個新的子類別,叫Car,它重寫了從Vehicle類別繼承來的description方法:

class Car: Vehicle {
    var speed: Double = 0.0
    init() {
        super.init()
        maxPassengers = 5
        numberOfWheels = 4
    }
    override func description() -> String {
        return super.description() + "; "
            + "traveling at \(speed) mph"
    }
}

Car中的description方法並非完全自定義,而是通過super.description使用了超類別Vehicle中的description方法,然後再追加一些額外的資訊,比如汽車的當前速度。

Preventing Overrides

你可以通過把方法,屬性或下標腳本標記為final來防止它們被重寫,只需要在宣告關鍵字前加上@final特性即可。(例如:@final var, @final func, @final class func, 以及 @final subscript

2016年2月8日 星期一

Python筆記:Web crawler practice

最近練習了在Python3中如何使用爬蟲來抓取網頁的資料.
聽別人說爬蟲爬了半天終於找到時間可以做個簡單的練習. 並做了POST/GET的練習.

Urllib

一般網頁的原理如下圖所示, 當有使用者發出請求時, Server會響應請求並作出回應。

我們就是利用Python來模擬Client端發送請求給Server,然後將得到的html文件進行解析就能得到你想要的內容了.

enter image description here

第一步透過Chrome來觀察目標網頁是走POST 或是 GET

enter image description here

第二步

確認From data

enter image description here

第三步

透過Urllib這個library來模擬request.

urlopen方法是用來打開一個網頁的(其實是發送一個請求), 他也支援多個參數.
urllib.request.Request功能為封裝一個請求。你可以給該請求添加提交的數據、頭(header)和指定提交方法等。這在登錄某些網站時是必須的。

urllib.request.Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None) 

header則是請求信息的頭。有點像文章的標題,服務器一看頭就知道你的身份和目的。瀏覽器有時會以header中的User-Agent一項來判斷是不是爬​​蟲.

urllib.parse.urlencode主要將data轉換成url所接受的格式

ex. 下圖為抓取高鐵網站的範例

import urllib.request

url = "http://www.thsrc.com.tw/tw/TimeTable/SearchResult"
request = urllib.request.Request(url)
request.add_header("User-Agent","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36")

form_data = {
    "StartStation": "977abb69-413a-4ccf-a109-0272c24fd490",
    "EndStation": "f2519629-5973-4d08-913b-479cce78a356",
    "SearchDate": "2016/02/05",
    "SearchTime": "17:00",
    "SearchWay":"DepartureInMandarin",
    "RestTime":"",
    "EarlyOrLater":""
}
form_data = urllib.parse.urlencode(form_data)
form_data = form_data.encode('utf-8')
response = urllib.request.urlopen(request,data=form_data)

在urllib.request.urlopen中, 如果沒有傳送data的話, 就會被默認是使用GET的方式傳送資料.

這裡的重點就是: url = url + "?" + form_data

url = "http://www.showtimes.com.tw/timetable.asp"
form_data = {
    'date':'2016-02-04',
    'sid':'8'
}
form_data = urllib.parse.urlencode(form_data)

url = url + "?" + form_data
request = urllib.request.Request(url)
response = urllib.request.urlopen(request)

分析HTML

得到網頁資訊之後接下來就是分析了, 以下我會利用beautifulSoup來做介紹.

BeautifulSoup

功能強大的HTML Parser. Beautiful Soup將復雜HTML文檔轉換成一個複雜的樹形結構,每個節點都是Python對象,所有對象可以歸納為4種:
1. Tag
簡單來說title a 等等 HTML 標籤加上里麵包括的內容就是 Tag.

<title>The Dormouse's story</title>

Tag,它有兩個重要的屬性,是 name 和 attrs

print soup.p.attrs
#{'class': ['title'], 'name': 'dromouse'}
  1. NavigableString
    用 .string 即可獲得標籤內的資料.

  2. BeautifulSoup

  3. Comment

安裝

$ pip install beautifulsoup4

使用

from bs4 import BeautifulSoup

soup = BeautifulSoup(response, 'html.parser')

for time in soup.find_all('table', attrs={'class':"touch_table"}):
    print(time.text)

參考:
1. http://www.showtimes.com.tw/timetable.asp
2. http://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html#find-all
3. https://docs.python.org/3/library/urllib.html
4. http://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html#find-all
5. http://cuiqingcai.com/1319.html

2016年1月17日 星期日

iOS筆記:UICollectionViewCell的一些問題

遇到的問題:[UICollectionViewCell imageCell]: unrecognized selector sent to instance

當客製化自己的CollectionViewCell時一定要記得修改viewDidLoad底下的cell名稱.

- (void)viewDidLoad {
    [super viewDidLoad];

    // Register cell classes, 自己客制的cell要記得改這裡!!
    [self.collectionView registerClass:[yourCollectionViewCell class] forCellWithReuseIdentifier:reuseIdentifier];
}

問題:Storyboard CollectionView segue not being triggered

明明Cell有拉Segue但是怎麼點都不會前進到下一個View.

Root cause: Cannot create segues directly from cells in a storyboard because the CollectionView is populated dynamically through the data source.

Solution: Should use the collectionView:didSelectItemAtIndexPath: and perform the segue programatically using performSegueWithIdentifier:sender:.

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
    [self performSegueWithIdentifier:@"MySegueIdentifier" sender:self];
}

2016年1月16日 星期六

iOS筆記:Parse and Facebook SDK

這部分的流程會根據iOS的版本而有誤, 建議還是按照Parse跟Facebook官方的文件來做即可.

目的:使用 Parse 的 Facebook 登入功能.

enter image description here

a. 利用cocoapods來管理Framework. 在新開的專案中新增“Podfile”, 接著執行 pod install.

ParseFacebookUtilsV4 會選用1.8.5是因為我在使用最新版本的時候遇到了一些編譯上的問題, 但這問題在這一版不存在.

platform :ios, '9.0'

pod 'Parse'
pod 'FBSDKCoreKit'
pod 'FBSDKLoginKit'
pod 'FBSDKShareKit'
pod 'ParseFacebookUtilsV4', "~> 1.8.5"

b. 設定Parse
先去Parse官網自訂一個APP名稱.
enter image description here

接著照著以下的設定步驟:
https://parse.com/apps/quickstart#parse_data/mobile/ios/native/new

AppDelegate.m中的application:didFinishLaunchingWithOptions:中新增, 並且#import <Parse.h>.

[Parse setApplicationId:@"eEUFWMCQjmRTAFXXXXXXXSI0JYJ"
                  clientKey:@"BKko60sluQisKwSvyEyKOFrXXXXXXXXXXXX"];

c. Setting up Facebook
使用 Facebook 登入功能,基本上分成三步:

  1. https://developers.facebook.com 創立一個新應用。

enter image description here

只要照著步驟設定即可.
enter image description here

記得在Setting中輸入mail去enable APP.

enter image description here

  1. 透過 CocoaPods 把 Facebook 相關的Library加入專案裡。
  2. 最後就是把登入功能加到 ViewController 裡面。
    Parse provides an easy way to integrate Facebook with your application.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    //...

    [PFFacebookUtils initializeFacebookWithApplicationLaunchOptions:launchOptions];

    [[FBSDKApplicationDelegate sharedInstance] application:application
                             didFinishLaunchingWithOptions:launchOptions];

    return YES;
}

- (BOOL)application:(UIApplication *)application
            openURL:(NSURL *)url
  sourceApplication:(NSString *)sourceApplication
         annotation:(id)annotation {
    return [[FBSDKApplicationDelegate sharedInstance] application:application
                                                          openURL:url
                                                sourceApplication:sourceApplication
                                                       annotation:annotation];
}

PFUser

The PFUser class is a local representation of a user persisted to the Parse Data.

- (void)updateUserInformation
{
    if ([FBSDKAccessToken currentAccessToken]) {
        [[[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{@"fields" : @"name,first_name,gender,birthday,location"}]
//從Facebook那讀取上述的使用者資料

         startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) {
             if (!error) {
                 NSDictionary *userDictionary = (NSDictionary *)result;

                 //Create URL
                 NSString *facebookID = userDictionary[@"id"];
                 NSURL *pictureURL = [NSURL URLWithString:[NSString stringWithFormat:@"https://graph.facebook.com/%@/picture?type=large&return_ssl_resources=1", facebookID]];

                 NSLog(@"%@",userDictionary);

                 NSMutableDictionary *userProfile = [[NSMutableDictionary alloc] initWithCapacity:7];

                 if (userDictionary[@"name"]) {
                     userProfile[PFUserProfileNameKey] = userDictionary[@"name"];
                 }
                 if (userDictionary[@"first_name"]) {
                     userProfile[PFUserProfileFirstNameKey] = userDictionary[@"first_name"];
                 }
                 if (userDictionary[@"location"][@"name"]) {
                     userProfile[PFUserProfileLocationKey] = userDictionary[@"location"][@"name"];
                 }
                 if (userDictionary[@"gender"]) {
                     userProfile[PFUserProfileGenderKey] = userDictionary[@"gender"];
                 }
                 if (userDictionary[@"birthday"]) {
                     userProfile[PFUserProfileBirthdayKey] = userDictionary[@"birthday"];
                 }
                 if ([pictureURL absoluteString]) {
                     userProfile[PFUserProfilePictureURL] = [pictureURL absoluteString];
                 }

                 [[PFUser currentUser] setObject:userProfile forKey:PFUserProfileKey];
                 [[PFUser currentUser] saveInBackground];

                 [self requestImage];
             }
             else {
                 NSLog(@"Error in Facebook Request %@", error);
             }

         }];
    }
}

如何抓取使用者的照片並呈現出來

主要是使用NSURLSession的NSURLSessionDownloadTask來顯示image.

- (void)uploadPFFileToParse:(UIImage *)image
{
    NSData *imageData = UIImageJPEGRepresentation(image, 0.8);

    if (!imageData) {
        NSLog(@"imageData was not found");
        return;
    }

    PFFile *photoFile = [PFFile fileWithData:imageData];

    [photoFile saveInBackgroundWithBlock:^(BOOL succeeded, NSError * _Nullable error) {
        if (succeeded) {
            PFObject *photo = [PFObject objectWithClassName:PFPhotoClassKey];
            [photo setObject:[PFUser currentUser] forKey:PFPhotoUserKey];
            [photo setObject:photoFile forKey:PFPhotoPictureKey];
            [photo saveInBackgroundWithBlock:^(BOOL succeeded, NSError * _Nullable error) {
                NSLog(@"Photo save successfully");
            }];
        }
    }];
}

- (void)requestImage
{
    PFQuery *query = [PFQuery queryWithClassName:PFPhotoClassKey];
    [query whereKey:PFPhotoUserKey equalTo:[PFUser currentUser]];
    [query countObjectsInBackgroundWithBlock:^(int number, NSError * _Nullable error) {
        if (number == 0) {
            PFUser *user = [PFUser currentUser];

            self.imageData = [[NSMutableData alloc] init];

            NSURL *profilePictureURL = [NSURL URLWithString:user[PFUserProfileKey][PFUserProfilePictureURL]];

            NSURLSession *session = [NSURLSession sharedSession];

            NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:profilePictureURL completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
                UIImage *downloadedImage = nil;
                if (!error) {
                    downloadedImage = [UIImage imageWithData:[NSData dataWithContentsOfURL:location]];
                } else {
                    NSLog(@"downloadTaskWithRequest failed: %@", error);
                    return;
                }

                dispatch_async(dispatch_get_main_queue(), ^ {
                    UIImage *profileImage = downloadedImage;
                    [self uploadPFFileToParse:profileImage];
                });
            }];
            [downloadTask resume];
        }
    }];
}

遭遇過的問題如下:
1.Could not build module ‘FBSDKCoreKit’
http://stackoverflow.com/questions/29466683/could-not-build-module-fbsdkcorekit-for-facebooksdk-4/29466739#29466739

2.Duplicate symbols for architecture x86_64
http://stackoverflow.com/questions/29466683/could-not-build-module-fbsdkcorekit-for-facebooksdk-4

3.Facebook Friend List Save As Array to Parse
http://stackoverflow.com/questions/32951433/facebook-friend-list-save-as-array-to-parse-swift

4.In developer.facebook.com, APP’s show “Not available to all users because your app is not live.”
http://stackoverflow.com/questions/21329250/the-developers-of-this-app-have-not-set-up-this-app-properly-for-facebook-login

5.How to get user email address from FBSDKProfile in Facebook SDK 4.0.
http://stackoverflow.com/questions/31410793/ios-facebook-sdk-login-doesnt-return-email-despite-permissions-granted/31503463#31503463

http://stackoverflow.com/questions/29323244/facebook-ios-sdk-4-0how-to-get-user-email-address-from-fbsdkprofile

參考網址:
1. https://developers.facebook.com/
2. https://www.parse.com/apps
3. http://blog.winwu.today/2014/05/ios-facebooksdk-facebook.html
4. https://swiftlyios.com/swift-parse-login-signup/
5. https://swiftlyios.com/parse-facebook-login-register/
6. https://www.parse.com/docs/ios/guide

2016年1月2日 星期六

Git指令:Review

Git with GitHub

Upload to Github

git init
git add README.md
git commit -m "first commit"
git remote add origin git@github.com:username/XXX.git
git push -u origin master

常用的指令

$ git status
$ git diff
$ git log  //看下過的commit
$ git tag "tag name" //幫一長串的commit改ID
$ git checkout "commit" //變回"commit(一串id)" or Head
$ git rm "..." //刪除"..."
$ git mv "..." //移動檔案

Branch

$ git checkout -b [name_of_your_new_branch]
 //把新建的分支取名為branchname並移動到分支上
$ git branch [name_of_your_new_branch]
 // 新建一個branch叫
$ git checkout [name_of_your_new_branch]
 //移動到branch上
$ git branch -d [name_of_your_new_branch]
 //刪除
$ git branch //列出所有的branch
$ git branch -v //查看各個分支最後一個提交物件的資訊
$ git branch --merged //哪些分支是當前分支的直接上游

Push the branch on github :

$ git push origin [name_of_your_new_branch]

$ git push origin :[name_of_your_new_branch] //刪除遠端分支
$ git remote show origin  //監看遠端儲存庫

Stash

git stash //把目前工作區的東西丟到暫存區裡,等之後在回來拿他
git stash list //列出所有暫存區的資料
git stash pop //取出最新的一筆, 並移除.
git stash apply //取出最新的一筆 stash 暫存資料. 但是 stash 資料不移除
git stash clear //把 stash 都清掉

Merge & Rebase

Merge a branch back.

//回到 master 分支,利用 git merge 命令指定要合併進來的分支
$ git checkout master
$ git merge [name_of_your_new_branch]

Rebase概念請參考:https://git-scm.com/book/zh-tw/v1/Git-%E5%88%86%E6%94%AF-%E5%88%86%E6%94%AF%E7%9A%84%E8%A1%8D%E5%90%88

雖然最後整合得到的結果跟merge沒有區別,但rebase能產生一個更為整潔的提交歷史。

2015年12月19日 星期六

Node.js筆記:Raspberry Pi 安裝Node.js

Node.js

要在 Raspberry Pi 中安裝 Node.js,我是利用官方網站上下載原始碼來編譯並安裝.
執行步驟如下

$ wget https://nodejs.org/dist/v5.3.0/node-v5.3.0-linux-armv6l.tar.gz 
$ tar -xvf node-v5.3.0-linux-armv6l.tar.gz 
$ cd node-v5.3.0-linux-armv6l.tar.gz

and the last step is Copy to /usr/local.

sudo cp -R * /usr/local/

To check Node.js is properly install and you have the right version, run the command node -v.

參考:
1. http://blog.gtwang.org/iot/raspberry-pi-install-node-js-and-mongodb-database/
2. http://blog.wia.io/installing-node-js-v4-0-0-on-a-raspberry-pi/

2015年12月16日 星期三

iOS筆記:Swift(3)

Closures

Swift 中的Closures與C 和Objective-C 中的代碼塊(blocks)以及其他一些編程語言中的匿名函數比較相似。

Swift’s closure expressions have a clean, clear style, with optimizations that encourage brief, clutter-free syntax in common scenarios. These optimizations include:

  • Inferring parameter and return value types from context
  • Implicit returns from single-expression closures
  • Shorthand argument names
  • Trailing closure syntax

Closure Expressions

利用簡潔語法構建內聯閉包的方式。

The Sort Method:
Swift 標準庫提供了名為sort的方法,會根據您提供的用於排序的閉包函數將已知類型數組中的值進行排序。一旦排序完成,sort(_:)方法會返回一個與原數組大小相同,包含同類型元素且元素已正確排序的新數組。原數組不會被sort(_:)方法修改。

ex. 使用sort(_:)方法對一個String類型的數組進行字母逆序排序.
The sorting closure needs to return true if the first value should appear before the second value, and false otherwise.

        let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]

    func backwards(s1: String, _ s2: String) -> Bool {
        return s1 > s2
    }
    var reversed = names.sort(backwards)
    // reversed is equal to ["Ewa", "Daniella", "Chris", "Barry", "Alex"]

Closure Expression Syntax

Closure expression syntax has the following general form:

    { (parameters) -> return type in
        statements
    }

Closure expression syntax可以使用常量、變量和inout類型作為參數,不能提供默認值。也可以在參數列表的最後使用可變參數。Tuples也可以作為參數和返回值。

The example below shows a closure expression version of the backwards(_:_:) function from earlier:

    reversed = names.sort({ (s1: String, s2: String) -> Bool in
        return s1 > s2
    })

The start of the closure’s body is introduced by the in keyword. This keyword indicates that the definition of the closure’s parameters and return type has finished, and the body of the closure is about to begin.

上述範例也可以修改成如下形式

        reversed = names.sort( { (s1: String, s2: String) -> Bool in return s1 > s2 } )

Operator Functions

There’s actually an even shorter way to write the closure expression above.
Swift 的String類型定義了關於大於(>)的字符串實現,其作為一個函數接受兩個String類型的參數並返回Bool類型的值。而這正好與sort(_:)方法的類型一樣,所以可以改寫成如下的表達方式。

        reversed = names.sort(>)

Trailing Closures

    func someFunctionThatTakesAClosure(closure: () -> Void) {
        // function body goes here
    }

    // here's how you call this function without using a trailing closure:   
    someFunctionThatTakesAClosure({
        // closure's body goes here
    })

    // here's how you call this function with a trailing closure instead: 
    someFunctionThatTakesAClosure() {
        // trailing closure's body goes here
    }

Here’s how you can use themap(_:) method with a trailing closure to convert an array of Int values into an array of String values.

    let digitNames = [
        0: "Zero", 1: "One", 2: "Two",   3: "Three", 4: "Four",
        5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
    ]
    let numbers = [16, 58, 510]

如上創建了一個數字和他們英文版本名字相映射的字典。同時還定義了一個準備轉換為字符串數組的整數數組。

You can now use the numbers array to create an array of String values, by passing a closure expression to the array’s map(_:)method as a trailing closure:

    let strings = numbers.map {
        (var number) -> String in
        var output = ""
        while number > 0 {
            output = digitNames[number % 10]! + output
            number /= 10
        }
        return output
    }
    // strings is inferred to be of type [String]
    // its value is ["OneSix", "FiveEight", "FiveOneZero"]

The call to the digitNames dictionary’s subscript is followed by an exclamation mark (!), because dictionary subscripts return an optional value to indicate that the dictionary lookup can fail if the key does not exist. In the example above, it is guaranteed that number % 10 will always be a valid subscript key for the digitNames dictionary, and so an exclamation mark is used to force-unwrap the String value stored in the subscript’s optional return value.

Capturing Values

A closure can capture constants and variables from the surrounding context in which it is defined. The closure can then refer to and modify the values of those constants and variables from within its body, even if the original scope that defined the constants and variables no longer exists.

In Swift, the simplest form of a closure that can capture values is a nested function.
ex.

    func makeIncrementer(forIncrement amount: Int) -> () -> Int {
        var runningTotal = 0
        func incrementer() -> Int {
            runningTotal += amount
            return runningTotal
        }
        return incrementer
    }

The return type of makeIncrementer is () -> Int. This means that it returns a function, rather than a simple value.
The makeIncrementer(forIncrement:) function has a single Int parameter with an external name of forIncrement, and a local name of amount. The argument value passed to this parameter specifies how much runningTotal should be incremented by each time the returned incrementer function is called.

Here’s an example of makeIncrementer and calling the function multiple times shows this behavior in action:

        let incrementByTen = makeIncrementer(forIncrement: 10)

    incrementByTen()
    // returns a value of 10
    incrementByTen()
    // returns a value of 20
    incrementByTen()
    // returns a value of 30

If you create a second incrementer, it will have its own stored reference to a new, separate runningTotal variable, calling the original incrementer (incrementByTen) again continues to increment its own runningTotal variable, and does not affect the variable captured by incrementBySeven:

    let incrementBySeven = makeIncrementer(forIncrement: 7)
    incrementBySeven()
    // returns a value of 7

    incrementByTen()
    // returns a value of 40

Closures Are Reference Types

上面的例子中,incrementBySeven和incrementByTen是常量,但是這些常量指向的Closure仍然可以增加其捕獲的變量的值。這是因為函數和Closure都是引用類型。
無論您將函數或閉包賦值給一個常量還是變量,您實際上都是將常量或變量的值設置為對應函數或Closure的引用。上面的例子中,指向Closure的引用incrementByTen是一個常量,而並非Closure內容本身。

Nonescaping Closures

A closure is said to escape a function when the closure is passed as an argument to the function, but is called after the function returns.

When you declare a function that takes a closure as one of its parameters, you can write @noescape before the parameter name to indicate that the closure is not allowed to escape.
Marking a closure with @noescape lets the compiler make more aggressive optimizations because it knows more information about the closure’s lifespan.

    func someFunctionWithNoescapeClosure(@noescape closure: () -> Void) {
        closure()
    }

一種能使Closure“逃逸”出函數的方法是,將這個Closure保存在一個函數外部定義的變量中。
As an example, many functions that start an asynchronous operation take a closure argument as a completion handler. The function returns after it starts the operation, but the closure isn’t called until the operation is completed—the closure needs to escape, to be called later.

    var completionHandlers: [() -> Void] = []
    func someFunctionWithEscapingClosure(completionHandler: () -> Void) {
        completionHandlers.append(completionHandler)
    }

If you tried to mark the parameter of above function with @noescape, you would get a compiler error.

Autoclosures

Autoclosures是一種自動創建的Closure,用於包裝傳遞給函數作為參數的表達式。這種閉包不接受任何參數,當它被調用的時候,會返回被包裝在其中的表達式的值。

Even though the first element of the customersInLine array is removed by the code inside the closure, the array element isn’t removed until the closure is actually called. If the closure is never called, the expression inside the closure is never evaluated, which means the array element is never removed.

    var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
    print(customersInLine.count)
    // prints "5"

    let customerProvider = { customersInLine.removeAtIndex(0) }
    print(customersInLine.count)
    // prints "5"

    print("Now serving \(customerProvider())!")
    // prints "Now serving Chris!"
    print(customersInLine.count)
    // prints "4"

You get the same behavior of delayed evaluation when you pass a closure as an argument to a function.

    // customersInLine is ["Alex", "Ewa", "Barry", "Daniella"]
    func serveCustomer(customerProvider: () -> String) {
        print("Now serving \(customerProvider())!")
    }
    serveCustomer( { customersInLine.removeAtIndex(0) } )
    // prints "Now serving Alex!"

It takes an autoclosure by marking its parameter with the @autoclosure attribute.

    // customersInLine is ["Ewa", "Barry", "Daniella"]
    func serveCustomer(@autoclosure customerProvider: () -> String) {
        print("Now serving \(customerProvider())!")
    }
    serveCustomer(customersInLine.removeAtIndex(0))
    // prints "Now serving Ewa!"

The @autoclosure attribute implies the @noescape attribute. If you want an autoclosure that is allowed to escape, use the @autoclosure(escaping) form of the attribute.

    // customersInLine is ["Barry", "Daniella"]
    var customerProviders: [() -> String] = []
    func collectCustomerProviders(@autoclosure(escaping) customerProvider: () -> String) {
        customerProviders.append(customerProvider)
    }
    collectCustomerProviders(customersInLine.removeAtIndex(0))
    collectCustomerProviders(customersInLine.removeAtIndex(0))

    print("Collected \(customerProviders.count) closures.")
    // prints "Collected 2 closures."
    for customerProvider in customerProviders {
        print("Now serving \(customerProvider())!")
    }
    // prints "Now serving Barry!"
    // prints "Now serving Daniella!"

In the code above, instead of calling the closure passed to it as its customer argument, the collectCustomerProviders(_:) function appends the closure to the customerProviders array. The array is declared outside the scope of the function, which means the closures in the array can be executed after the function returns. As a result, the value of the customer argument must be allowed to escape the function’s scope.

Enumerations

An enumeration defines a common type for a group of related values and enables you to work with those values in a type-safe way within your code.

Enumeration Syntax

You introduce enumerations with the enum keyword and place their entire definition within a pair of braces:

    enum SomeEnumeration {
        // enumeration definition goes here
    }

An example for the four main points of a compass:

    enum CompassPoint {
        case North
        case South
        case East
        case West
    }

Multiple cases can appear on a single line, separated by commas:

    enum Planet {
        case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
    }

The type of directionToHead is inferred when it is initialized with one of the possible values of CompassPoint. Once directionToHead is declared as a CompassPoint, you can set it to a different CompassPoint value using a shorter dot syntax:

    var directionToHead = CompassPoint.West

        directionToHead = .East

Matching Enumeration Values with a Switch Statement

You can match individual enumeration values with a switch statement:

    directionToHead = .South
    switch directionToHead {
    case .North:
        print("Lots of planets have a north")
    case .South:
        print("Watch out for penguins")
    case .East:
        print("Where the sun rises")
    case .West:
        print("Where the skies are blue")
    }
    // prints "Watch out for penguins"

如果忽略了.West這種情況,上面那段代碼將無法通過編譯,因為它沒有考慮到CompassPoint的全部成員。強制窮舉確保了Enumerations成員不會被意外遺漏。

當不需要匹配每個Enumerations成員的時候,你可以提供一個default分支來涵蓋所有未明確處理的枚舉成員:

    let somePlanet = Planet.Earth
    switch somePlanet {
    case .Earth:
        print("Mostly harmless")
    default:
        print("Not a safe place for humans")
    }
    // prints "Mostly harmless"

Associated Values

You can define Swift enumerations to store associated values of any given type, and the value types can be different for each case of the enumeration if needed.

In Swift, an enumeration to define product barcodes of either type might look like this:

    enum Barcode {
        case UPCA(Int, Int, Int, Int)
        case QRCode(String)
    }

This can be read as:
“定義一個名為Barcode的enumeration type,它的一個成員值是具有(Int,Int,Int,Int)類型關聯值的UPCA,另一個成員值是具有String類型關聯值的QRCode。”

這裡的定義不提供任何Int或String類型的關聯值,它只是定義了,當Barcode常量和變量等於Barcode.UPCA或Barcode.QRCode時,可以存儲的關聯值的類型。

        var productBarcode = Barcode.UPCA(8, 85909, 51226, 3)
        productBarcode = .QRCode("ABCDEFGHIJKLMNOP")

可以使用一個switch 語句來檢查不同的條形碼類型。關聯值可以被提取出來作為switch 語句的一部分。
You can place a single var or let annotation before the case name, for brevity:

    switch productBarcode {
    case .UPCA(let numberSystem, let manufacturer, let product, let check):
        print("UPC-A: \(numberSystem), \(manufacturer), \(product), \(check).")
    case .QRCode(let productCode):
        print("QR code: \(productCode).")
    }
    // prints "QR code: ABCDEFGHIJKLMNOP."

如果一個enumeration成員的所有關聯值都被提取為常量,或者都被提取為變量,為了簡潔,你可以只在成員名稱前標註一個let或者var:

    switch productBarcode {
    case let .UPCA(numberSystem, manufacturer, product, check):
        print("UPC-A: \(numberSystem), \(manufacturer), \(product), \(check).")
    case let .QRCode(productCode):
        print("QR code: \(productCode).")
    }
    // prints "QR code: ABCDEFGHIJKLMNOP."

Raw Values

As an alternative to associated values, enumeration cases can come prepopulated with default values (called raw values), which are all of the same type.

Here’s an example that stores raw ASCII values alongside named enumeration cases:

    enum ASCIIControlCharacter: Character {
        case Tab = "\t"
        case LineFeed = "\n"
        case CarriageReturn = "\r"
    }

Enumeration類型ASCII Control Character的原始值類型被定義為Character,並設置了一些比較常見的ASCII 控制字符。 Character的描述詳見字符串和字符部分。

Raw values are not the same as associated values. Raw values are set to prepopulated values when you first define the enumeration in your code, like the three ASCII codes above. The raw value for a particular enumeration case is always the same. Associated values are set when you create a new constant or variable based on one of the enumeration’s cases, and can be different each time you do so.

Implicitly Assigned Raw Values

When you’re working with enumerations that store integer or string raw values, you don’t have to explicitly assign a raw value for each case. When you don’t, Swift will automatically assign the values for you.

        enum Planet: Int {
            case Mercury = 1, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
        }

In the example above, Planet.Mercury has an explicit raw value of 1, Planet.Venus has an implicit raw value of 2, and so on(以此類推, 等等).

    let sunsetDirection = CompassPoint.West.rawValue
    // sunsetDirection is "West"

Initializing from a Raw Value

If you define an enumeration with a raw-value type, the enumeration automatically receives an initializer that takes a value of the raw value’s type and returns either an enumeration case or nil. You can use this initializer to try to create a new instance of the enumeration.

    let possiblePlanet = Planet(rawValue: 7)
    // possiblePlanet is of type `Planet?` and equals Planet.Uranus

If you try to find a planet with a position of 9, the optional Planet value returned by the raw value initializer will be nil:

    let positionToFind = 9
    if let somePlanet = Planet(rawValue: positionToFind) {
        switch somePlanet {
        case .Earth:
            print("Mostly harmless")
        default:
            print("Not a safe place for humans")
        }
    } else {
        print("There isn't a planet at position \(positionToFind)")
    }
    // prints "There isn't a planet at position 9"

Recursive Enumerations

遞歸枚舉(recursive enumeration)是一種枚舉類型,它有一個或多個枚舉成員使用該枚舉類型的實例作為關聯值。使用遞歸枚舉時,編譯器會插入一個間接層。你可以在枚舉成員前加上indirect來表示該成員可遞歸。

    indirect enum ArithmeticExpression {
        case Number(Int)
        case Addition(ArithmeticExpression, ArithmeticExpression)
        case Multiplication(ArithmeticExpression, ArithmeticExpression)
    }

A recursive function is a straightforward way to work with data that has a recursive structure. For example, here’s a function that evaluates an arithmetic expression:

    func evaluate(expression: ArithmeticExpression) -> Int {
        switch expression {
        case .Number(let value):
            return value
        case .Addition(let left, let right):
            return evaluate(left) + evaluate(right)
        case .Multiplication(let left, let right):
            return evaluate(left) * evaluate(right)
        }
    }

    // evaluate (5 + 4) * 2
    let five = ArithmeticExpression.Number(5)
    let four = ArithmeticExpression.Number(4)
    let sum = ArithmeticExpression.Addition(five, four)
    let product = ArithmeticExpression.Multiplication(sum, ArithmeticExpression.Number(2))
    print(evaluate(product))
    // prints "18"

Classes and Structures

Classes and structures are general-purpose, flexible constructs that become the building blocks of your program’s code.

Note
An instance of a class is traditionally known as an object. However, Swift classes and structures are much closer in functionality than in other languages, and much of this chapter describes functionality that can apply to instances of either a class or a structure type. Because of this, the more general term instance is used.

Comparing Classes and Structures

Classes and structures in Swift have many things in common. Both can:
* Define properties to store values
* Define methods to provide functionality
* Define subscripts to provide access to their values using subscript syntax
* Define initializers to set up their initial state
* Be extended to expand their functionality beyond a default implementation
* Conform to protocols to provide standard functionality of a certain kind

Classes have additional capabilities that structures do not:
* Inheritance enables one class to inherit the characteristics of another.
* Type casting enables you to check and interpret the type of a class instance at runtime.
* Deinitializers enable an instance of a class to free up any resources it has assigned.
* Reference counting allows more than one reference to a class instance.

Definition Syntax

Classes and structures have a similar definition syntax. You introduce classes with the class keyword and structures with the struct keyword.

    class SomeClass {
        // class definition goes here
    }
    struct SomeStructure {
        // structure definition goes here
    }

Note
Whenever you define a new class or structure, you effectively define a brand new Swift type. Give types UpperCamelCase names (such as SomeClass and SomeStructure here) to match the capitalization of standard Swift types (such as String, Int, and Bool).
Conversely, always give properties and methods lowerCamelCase names (such as frameRate and incrementCount) to differentiate them from type names.

ex.

    struct Resolution {
        var width = 0
        var height = 0
    }
    class VideoMode {
        var resolution = Resolution()
        var interlaced = false
        var frameRate = 0.0
        var name: String?
    }

Class and Structure Instances

The Resolution structure definition and the VideoMode class definition only describe what a Resolution or VideoMode will look like.
They themselves do not describe a specific resolution or video mode. To do that, you need to create an instance of the structure or class.

    let someResolution = Resolution()
    let someVideoMode = VideoMode()

通過這種方式所創建的類或者結構體實例,其屬性均會被初始化為默認值。

Accessing Properties

You can access the properties of an instance using dot syntax. In dot syntax, you write the property name immediately after the instance name, separated by a period (.), without any spaces:

    print("The width of someResolution is \(someResolution.width)")
    // prints "The width of someResolution is 0"

Unlike structures, class instances do not receive a default memberwise initializer.

Memberwise Initializers for Structure Types

All structures have an automatically-generated memberwise initializer, which you can use to initialize the member properties of new structure instances. Initial values for the properties of the new instance can be passed to the memberwise initializer by name:

        let vga = Resolution(width: 640, height: 480)

Structures and Enumerations Are Value Types

A value type is a type whose value is copied when it is assigned to a variable or constant, or when it is passed to a function.

        let hd = Resolution(width: 1920, height: 1080)
        var cinema = hd

    cinema.width = 2048
    print("cinema is now \(cinema.width) pixels wide")
    // prints "cinema is now 2048 pixels wide"

    print("hd is still \(hd.width) pixels wide")
    // prints "hd is still 1920 pixels wide"

在將hd賦予給cinema的時候,實際上是將hd中所存儲的值進行拷貝,然後將拷貝的數據存儲到新的cinema實例中。結果就是兩個完全獨立的實例碰巧包含有相同的數值。由於兩者相互獨立,因此將cinema的width修改為2048並不會影響hd中的width的值。

Classes Are Reference Types

Unlike value types, reference types are not copied when they are assigned to a variable or constant, or when they are passed to a function.

    let tenEighty = VideoMode()
    tenEighty.resolution = hd
    tenEighty.interlaced = true
    tenEighty.name = "1080i"
    tenEighty.frameRate = 25.0

    //Assigned to a new constant
    let alsoTenEighty = tenEighty
    alsoTenEighty.frameRate = 30.0

        print("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
        // 输出 "The frameRate property of theEighty is now 30.0"

因為類是Reference Type,所以tenEight和alsoTenEight實際上引用的是相同的VideoMode實例。換句話說,它們是同一個實例的兩種叫法。

Note that tenEighty and alsoTenEighty are declared as constants, rather than variables. However, you can still change tenEighty.frameRate and alsoTenEighty.frameRate because the values of the tenEighty and alsoTenEighty constants themselves do not actually change. tenEighty and alsoTenEighty themselves do not “store” the VideoMode instance—instead, they both refer to a VideoMode instance behind the scenes. It is the frameRate property of the underlying VideoMode that is changed, not the values of the constant references to that VideoMode.

Properties

Properties associate values with a particular class, structure, or enumeration.

Stored Properties

In its simplest form, a stored property is a constant or variable that is stored as part of an instance of a particular class or structure. Stored properties can be either variable stored properties (introduced by the var keyword) or constant stored properties (introduced by the let keyword).

    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

Stored Properties of Constant Structure Instances

如果創建了一個結構體的實例並將其賦值給一個常量,則無法修改該實例的任何屬性,即使定義了變量存儲屬性.

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

Because rangeOfFourItems is declared as a constant (with the let keyword), it is not possible to change its firstValue property, even though firstValue is a variable property.

Lazy Stored Properties

A lazy stored property is a property whose initial value is not calculated until the first time it is used.

Note
You must always declare a lazy property as a variable (with the var keyword), because its initial value might not be retrieved until after instance initialization completes. Constant properties must always have a value before initialization completes, and therefore cannot be declared as lazy.

Lazy properties are useful when the initial value for a property is dependent on outside factors whose values are not known until after an instance’s initialization is complete. Lazy properties are also useful when the initial value for a property requires complex or computationally expensive setup that should not be performed unless or until it is needed.

The example below uses a lazy stored property to avoid unnecessary initialization of a complex class.

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

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

If a property marked with the lazy modifier is accessed by multiple threads simultaneously and the property has not yet been initialized, there is no guarantee that the property will be initialized only once.

Computed Properties

In addition to stored properties, classes, structures, and enumerations can define computed properties, which do not actually store a value. Instead, they provide a getter and an optional setter to retrieve and set other properties and values indirectly.

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

這個例子定義了3 個結構體來描述幾何形狀:
* Point封裝了一個(x, y)的坐標
* Size封裝了一個width和一個height
* Rect表示一個有原點和尺寸的矩形

The self Property

Every instance of a type has an implicit property called self, which is exactly equivalent to the instance itself.
可以在一個實例的實例方法中使用這個隱含的self屬性來引用當前實例。

        lass Counter {
            var count = 0
            func increment() {
                ++count
            }
            func incrementBy(amount: Int) {
                count += amount
            }
            func reset() {
                count = 0
            }
        }

    func increment() {
            self.count++
        }

Here, self disambiguates between a method parameter called x and an instance property that is also called x:

    struct Point {
        var x = 0.0, y = 0.0
        func isToTheRightOfX(x: Double) -> Bool {
            return self.x > x
        }
    }
    let somePoint = Point(x: 4.0, y: 5.0)
    if somePoint.isToTheRightOfX(1.0) {
        print("This point is to the right of the line where x == 1.0")
    }
    // prints "This point is to the right of the line where x == 1.0"

Without the self prefix, Swift would assume that both uses of x referred to the method parameter called x.

參考資料:
1. https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/index.html#//apple_ref/doc/uid/TP40014097-CH3-ID0
2. http://wiki.jikexueyuan.com/project/swift/