學習一門新的程式語言要多久?答案是隨著你的經驗的增加,學習花費的時間越來越短。當然,這也和新語言的不斷演變進化有關係。
我利用周末兩天時間,把《Swift Programming Language》中文版整整的細看了一遍,然後為了總結提取 Swift 的主要語言特性,又把這本書快速過了第二遍。
根據我的番茄鍾粗略統計,我看書學習第一遍大約花了 5 小時(包括邊學,邊在 XCode 試驗的時間),第二遍主要是快速過一遍主要特性,將主要示例代碼提取出來,大約花了 3.5 小時。
連我自己都覺得有點難以置信,總共只需要 8 小時,就可以基本入門一門新語言了。
很多時候,我們想學習某個東西,但是卻一直停留在想一想的狀態,遲遲都未動手。究其原因,大概是如下幾個原因吧:
恐懼未知。對未知的東西沒有把握,怕太難,怕需要太長時間學,所以能拖就拖。
注意力不能集中。連續玩幾個小時遊戲一點不累,看半小時書就感覺身心俱疲。
番茄工作法在這個時候就起作用了,告訴自己,不想太多,開始一個番茄鍾試試,在這 25 分鐘內,只關注這 25 分鐘內要看的內容。然後,很自然的,障礙被逐個擊破,番茄鍾一個接著一個。
以下是我學習 Swift 的代碼總結,可用於之後速查。
代碼: https://github.com/coderzh/CodeTips/blob/master/swift.swift
/* swift.swift: Swift 速學速查速用代碼手冊 Source: github.com/coderzh/CodeTips/blob/master/swift.swift Author: coderzh(github.com/coderzh) Blog: http://blog.coderzh.com 參考:《Swift Programming Language》 */import Cocoa// 0. 注釋/*塊注釋行尾分號可不用*/// 1. Hello Worldprint("Hello Swift")// 2. 常量變量類型let constValue = 3.14var variable = 18variable += 1// 指定類型let age: Int = 18// 多重賦值var (a, b) = (1, 2)// 匿名佔位符(_, b) = (3, 4)// 類型會自動推導let name = "It's a string"let gravity = 0.98 // 默認 double// 永遠不做隱式轉換,必須自己強轉let sum = Double(age) + gravity// 運算:+, -, *, /, % 求餘// 比較:<, >, ==, >=, <=, !=// === 恆等,是否為同一個對象// !== 不恆等// 位運算:~x 取反,& 與,| 或,^ 異或,<< 左移動,>> 右移// 溢出運算符 &+ &- &* &/ &% ,這樣溢出部分就會丟掉,不會出錯,比如:var willOverflow = UInt8.maxwillOverflow = willOverflow &+ 1 // = 0// 類型let b1 = truelet i32: Int32 = 6let f64: Float64 = 3.1415// 字符串let str = "swift"// 字符串連接,使用\()var hello = "hello \(str)"let count = str.characters.countlet c = hello[hello.startIndex]for i in hello.characters.indices { print("\(hello[i])")}hello.insert("!", atIndex: hello.endIndex)hello.insertContentsOf(" there".characters, at: hello.endIndex.predecessor())hello.removeAtIndex(hello.endIndex.predecessor())hello.hasPrefix("hello")hello.hasSuffix("swift")let unicode = "你好 swift"unicode.characters.count // 8for codeUnit in unicode.utf8 { print("\(codeUnit)", terminator: "")}if hello == unicode{}// 數組var shoppingList = ["test", "book", "bike"]shoppingList[2] = "joke"shoppingList.append("bus")shoppingList.insert("foo", atIndex: 0)shoppingList.removeAtIndex(0)// 類似 sliceshoppingList[0..<2] // ["test", "book"]shoppingList[0...2] // ["test", "book", "joke"]for item in shoppingList { print(item)}for (index, value) in shoppingList.enumerate() { print("\(index): \(value)")}let emptyArray = [String]()var someInts = [Int]()var someInts2 = [Int](count: 3, repeatedValue: 8) // [8, 8, 8]var someInts3 = [Int](count: 3, repeatedValue: 2)// 任意類型數組var anyArray = [Any]()anyArray.append(1)anyArray.append("book")someInts = someInts2 + someInts3 // [8, 8, 8, 2, 2, 2]// 元組let httpResponse = (404, "Not Found")print(httpResponse.0)// 集合 Setsvar setBooks: Set<String> = ["book1", "book2"]// 自動推導var setBooks2: Set = ["book1", "book2", "book3"]setBooks.intersect(setBooks2) // 交集setBooks.exclusiveOr(setBooks2) // 非交集setBooks.union(setBooks2) // 併集setBooks.subtract(setBooks2) // 減集setBooks.isSubsetOf(setBooks2)setBooks2.isSupersetOf(setBooks)setBooks.isStrictSubsetOf(setBooks2) // 被包含且不相等// 字典var map = [ "Malcolm": "hehe", "Keylee": 123,]map["Keylee"] = 166var namesOfInt = [Int: String]()namesOfInt[10] = "ten"if let oldValue = namesOfInt.updateValue("Ten", forKey: 10) { print("\(oldValue)")}if let name = namesOfInt[8] { print("\(name)")} else { print("not exist 8")}for (intKey, strValue) in namesOfInt { print("\(intKey):\(strValue)")}// namsOfInt.valuesfor intKeys in namesOfInt.keys {}let intKeys = [Int](namesOfInt.keys)// 可空變量,用 ?var optionalString: String? = nilif let name = optionalString { print("hello \(name)")}// 3. 流程控制// 循環// [0, 4)for i in 0..<4 { print("print \(i)") // 4 times}// [0, 4]for i in 0...4 { print("print \(i)") // 5 times}var i = 0while i < 2 { print("\(i)") i += 1}repeat { print("\(i)") i += 1} while i < 5// 判斷if i < 5 {} else if i < 10 {} else {}// 強大的 switch// 不需要 breakswitch i {case 1, 2, 3: print("123")case 5: print("5")case 6..<10: print("6-9")default: print("default")}let somePoint = (1, 1)switch somePoint {case (0, 0): print("0, 0")case (_, 1): print("y is 1")case (-2...2, -2...2): // 區間 print("from (-2,-2) to (2, 2)")case (let x, 0): // 值綁定 print("\(x)")case let (x, y) where x == y: // where print("x == y")case (10, 11): fallthrough // 貫穿,繼續向下default: print("default")}// 控制轉移// continue break fallthrough retrun throw// 帶標籤i = 0gameLoop: while i > -1 { i = i + 1 if i > 3 { break gameLoop }}// 提前退出(提前返回)func greet(person: [String:String]) { guard let name = person["name"] else { return } print("\(name)")}greet(["age":"18"])// 4. 函數func greet(name: String, day: String) { print("Hello \(name), today is \(day)")}// 第二個參數默認需要指定名稱greet("tom", day: "2016")func sum(a: Int, b: Int) -> Int { return a + b}sum(1, b: 2)// 多重返回值func minMax(array: [Int]) -> (min: Int, max: Int) { // ... return (0, 1)}// 可選返回值加 ?func minMax2(array: [Int]) -> (min: Int, max: Int)? { if array.isEmpty { return nil } return (0, 1)}// 指定外部參數名func sayHello(to person: String, and anotherPerson: String) { print("Hello \(person) and \(anotherPerson)")}sayHello(to: "coderzh", and: "tom")// 忽略外部參數名,使用 _func sayHello2(person: String, _ anotherPerson: String) { print("Hello \(person) and \(anotherPerson)")}sayHello2("coderzh", "jack")// 默認參數func someFunction(p: Int = 10) { print("\(p)")}someFunction()// 可變參數func sum(numbers: Int...) -> Int { var total = 0 for n in numbers { total += n } return total}sum(1, 2, 3, 4, 5)// 參數默認是常量類型,如需指定變量類型,前面加 var(swift 3 將移除 var)func alignRight(var string: String, totalLength: Int, pad: Character) -> String { string = string + "!" return string}// 傳入傳出參數 inoutfunc swap(inout a: Int, inout _ b: Int) { let temp = a a = b b = temp}var someInt = 7var anotherInt = 8// inout 參數必須加 &swap(&someInt, &anotherInt)// 函數類型,函數變量var sumFunc: (Int, Int) -> Int = sumsumFunc(1, 2)// 函數可做參數func doSum(handler:(Int, Int) -> Int, _ a: Int, _ b: Int) { handler(a, b)}// 函數可做返回值func getSum() -> (Int, Int) -> Int { // 函數可嵌套 func someFunc(a: Int, b: Int) -> Int { return a + b } return someFunc}doSum(sum, 2, 3)// 閉包// 閉包是引用類型let reversed2 = shoppingList.sort({a, b in a < b})let r = shoppingList.sort({ $0 < $1 })let r2 = shoppingList.sort(<)let r3 = shoppingList.sort{ $0 < $1 }// 非逃逸閉包(noescape closure)// 閉包只能在函數內執行,不能「逃逸」出去func someClosure(@noescape closure: () -> Void) { closure()}// 自動閉包(這樣不用寫花括號了?)func autoClosure(@autoclosure provider: () -> String) { provider()}autoClosure(shoppingList.removeAtIndex(0))// 5. 枚舉(一等公民,十分強大)// 值類型enum Rank: Int { case Ace = 1 case Two, Three}var ace = Rank.Ace // AceRank.Ace.rawValue // 1let ace1 = Rank(rawValue: 1) // Aceace = .Twoenum ServerResponse { case Result(String, String) case Error(String)}// 可失敗構造器enum TemperatureUnit { case Kelvin, Celsius, Fahrenheit init?(symbol: Character) { switch symbol { case "K": self = .Kelvin case "C": self = .Celsius case "F": self = .Fahrenheit default: return nil } }}let success = ServerResponse.Result("6:00am", "6:00pm")let failure = ServerResponse.Error("Out of cheese")switch success {case let .Result(sunrise, sunset): let serverResponse = "sunrise at \(sunrise), sunset at \(sunset)"case let .Error(error): let serverResponse = "Error \(error)"}// 枚舉遞歸...enum ArithmeticExpression { case Number(Int) indirect case Addition(ArithmeticExpression, ArithmeticExpression) indirect case Multiplication(ArithmeticExpression, ArithmeticExpression)}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) }}// 計算 (5 + 4) * 2let five = ArithmeticExpression.Number(5)let four = ArithmeticExpression.Number(4)let sum2 = ArithmeticExpression.Addition(five, four)let product = ArithmeticExpression.Multiplication(sum2, ArithmeticExpression.Number(2))print(evaluate(product)) // 輸出 "18」// 6. 類和結構體// 結構體是值類型,類是引用類型class SomeClass { var width = 0 var height = 0 // class 不需要 mutating func incrementWidth() { self.width += 1 } // 下標操作: [n] subscript(index: Int) -> Int { get { return 0 } set(newValue) { // set from newValue } }}let s1 = SomeClass()struct SomeStruct { static var someValue = 100 static func staticFunc() -> Int { return 1 } var x = 0 var y = 0 var doubleX: Int { get { return x * 2 } set { x = newValue / 2 } } // readonly var doubleY: Int { return y * 2 } var total: Int { willSet(newTotal) { print("will set \(newTotal)") } didSet { print("old: \(oldValue), new: \(total)") } } // 如果會改變類成員,比如聲明 mutating mutating func incrementX() { self.x += 1 }}var s2 = SomeStruct(x: 2, y: 3, total: 10)s2.doubleX = 10 // s2.x == 5s2.total = 5// 繼承class Animal { // 構造函數 init() { } // 必要構造器,子類必須實現,而且聲明為 required required init(name: String, age: Int) { } func makeNoise() { print("wowowo") }}class Cat: Animal { var name: String = "" var nickName: String = "" init(name: String) { super.init() self.name = name } init(fromNickName nickName: String) { super.init(name: nickName, age: 18) self.nickName = nickName } // 便利構造器:必須調用其他構造器 convenience override init() { self.init(name: "UnKnown") } // 可失敗構造器 init?(age: Int) { super.init(name: "UnKnown", age: age) if age < 0 { return nil } } required init(name: String, age: Int) { self.name = name super.init(name: name, age: age) } // 析構,默認會先調用父類的析構 deinit { } override func makeNoise() { print("miaomiaomiao") }}final class CannotInheirt {}// 7. 自動引用計數 ARC// weak 弱引用// unowned 無主引用// 8. 可空鏈式調用/* For example:if let johnsStreet = john.residence?.address?.street { print("John's street name is \(johnsStreet).")} else { print("Unable to retrieve the address.")}*/// 如果確定有值,使用!// let roomCount = john.residence!.numberOfRooms// 9. 錯誤處理enum CustomError : ErrorType { case Invalid case OutOfRange}func makeASandwich() throws -> String { throw CustomError.Invalid}do { try makeASandwich()} catch CustomError.Invalid { print("Invalid")}// 可空let x = try? makeASandwich()// 使錯誤傳遞失效,肯定不throw,否則 assert// let y = try! makeASandwich()// defer 和 Golang 裡的 defer 一樣,用來退出清理/*func processFile(filename: String) throws { if exists(filename) { let file = open(filename) defer { close(file) } while let line = try file.readline() { // 處理文件 } // 在這裡,作用域的最後調用 close(file) }}*/// 10. 類型轉換: is as (和 CSharp 類似)// 任意類型:// 1. AnyObject 任何 class 類型實例// 2. Any 任何類型// 11. 擴展(extension,類似 CSharp 裡的擴展方法,但是貌似更強大)// 比如:擴展內置類型 Double// 幾乎一切都可擴展,用到時再查用法吧extension Double { var km: Double { return self * 1_000.0 } var m : Double { return self } var cm: Double { return self / 100.0 } var mm: Double { return self / 1_000.0 } var ft: Double { return self / 3.28084 }}// 12. 協議(類似接口的東西)// 當同時有繼承時,先寫繼承,再寫協議,協議可以有多個protocol FullyNamed { var fullName: String { get }}// 協議也可繼承protocol SubFullyNamed: FullyNamed { var nickName: String { get }}struct Person: FullyNamed{ var fullName: String}// 專屬協議,指定只能用在 class 上protocol ClassOnlyProtocol: class, FullyNamed {}protocol Aged { var age: Int { get set }}// 協議合成func foo(pro: protocol<FullyNamed, Aged>, base: Any) { // 協議判斷 if let p = base as? Aged { print(p.age) }}// 可選協議(既然是協議,還可選,醉了)// @objc protocol// 13. 泛型func swapTwoValues<T>(inout a: T, inout _ b: T) { let temporaryA = a a = b b = temporaryA}// 泛型約束func someFunction<T: SomeClass, U: FullyNamed>(someT: T, someU: U) { // 這裡是函數主體}// Where/*func allItemsMatch< C1: Container, C2: Container where C1.ItemType == C2.ItemType, C1.ItemType: Equatable> (someContainer: C1, anotherContainer: C2) -> Bool {}*/// 14. 訪問控制// public internal(默認) private// 加在 class var 等前