Swift 開發語法

文/Tuberose(簡書做者)
原文連接:http://www.jianshu.com/p/5e2d4c34f18e
著做權歸做者全部,轉載請聯繫做者得到受權,並標註「簡書做者」。


簡介

  • Swift 語言由蘋果公司在 2014 年推出,用來撰寫 OS X 和 iOS 應用程序
  • 2014 年,在 Apple WWDC 發佈

歷史

  • 2010 年 7 月,蘋果開發者工具部門總監 Chris Lattner(克里斯·拉特納) 開始着手 Swift 編程語言的設計工做
  • 用一年時間,完成基本架構
  • Swift 大約歷經 4 年的開發期,2014 年 6 月發表

    克里斯·拉特納
    )

特點

  • 蘋果宣稱 Swift 的特色是:快速、現代、安全、互動,並且明顯優於 Objective-C 語言
  • 可使用現有的 CocoaCocoa Touch 框架
  • Swift 取消了 Objective C 的指針及其餘不安全訪問的使用
  • 捨棄 Objective C 早期應用 Smalltalk 的語法,全面改成句點表示法
  • 提供了相似 Java 的名字空間(namespace)、泛型(generic)、運算對象重載(operator overloading)
  • Swift 被簡單的形容爲 「沒有 C 的 Objective-C」(Objective-C without the C)

現狀

  • 2015 年 2 月,蘋果同時推出 Xcode 6.2 Beta 5 和 6.3 Beta,在完善 Swift 1.1的同時,推出了 Swift 1.2 測試版
  • 2015 年 6 月,蘋果在 WWDC 發佈了 Swift 2.0 測試版,而且宣稱在年末開源
  • 從發佈至今,蘋果的每個舉措都彰顯其大力推廣 Swift 的決心
  • Swift 當前正式版本:1.2,測試版本是 2.0
  • 目前有些公司的新項目已經直接採用 Swift 開發
  • 目前不少公司已經在作 Swift 的人才儲備
  • 應聘時,會 Swift 開發 無疑會增長自身籌碼
  • 到 2015 年末,iOS 9.0 正式發佈的同時,Swift 勢必大行其道!

資源網站

  • 1.開發入門
    • 簡單體驗
var i = 10 print(i) i = 15 print(i) let j = 20 // 常量一經定義不能自改數值 // j = 25 print(j)
  • 建立對象
  • 從OC轉換爲Swift的規律: alloc initXXX --> (xxxx:)
    OC: [[UIView alloc] init] -- [[UIView alloc] initWithFrame:]
    Swift: UIView() -- UIView(frame: )
  • 分號
    • 在OC中每一條語句後面必須跟上一個;, Swift中若是一行只有一條語句, 那麼;能夠省略
  • 調用方法
    OC: [UIColor redColor];
    Swift: UIColor.redColor()
  • 枚舉編程

    OC: UIButtonTypeContactAdd
    Swift: UIButtonType.ContactAdd
  • 示例json

    // 1.建立UIView let customView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) customView.backgroundColor = UIColor.redColor() // 2.建立一個按鈕 let btn = UIButton(type: .ContactAdd) btn.center = CGPoint(x: 50, y: 50) // 3.將按鈕添加到UIView上 customView.addSubview(btn)

  • 2.常量和變量
// 定義變量 var i = 10 print(i) i = 15 print(i) let j = 20 // 常量一經定義不能自改數值 // j = 25 print(j)
  • 小結swift

    • var 定義變量,設置以後能夠修改
    • let 定義常量,設置以後不能夠修改
    • 語句末尾不用使用 ;
    • 在 Swift 中使用 print() 替代 OC 中的 NSLog
    • print 的性能更好
  • 定義 OC 對象數組

// 實例化視圖 let v = UIView(frame: CGRectMake(0, 0, 100, 100)) // 設置背景顏色 v.backgroundColor = UIColor.redColor() // 添加到根視圖 view.addSubview(v)
  • 小結
    • Swift 中要實例化一個對象可使用 類名() 的格式,與 OC 中的 alloc/init 等價
    • OC 中的 initWithXXXSwift 中一般可使用 類名(XXX: ) 找到對應的函數
    • OC 中的 [UIColor redColor] 類方法,在 Swift 中一般可使用 類名.XXX 找到對應的函數
    • 使用 let 修飾 v 而且賦值,表示 該常量的內存地址不容許修改,可是能夠修改其內部的屬性
    • 當前對象的屬性,不須要使用 self.

常量&變量的使用原則:儘可能先用 let,只有須要變的時候,再用 var,可以更加安全安全

  • 變量類型
let x = 10 let y = 10.5 let z: Double = 20 print(Double(x) + y) // 20.5 print(x + Int(y)) // 20 print(y + z) // 30.5
  • 小結
    • 初次接觸 Swift 中會由於簡單的 var let 誤覺得 Swift 中的類型很是鬆散
    • 其實全部變量的準確類型都是在賦值的同時自動推導的
    • Swift 是對類型要求很是嚴格的一門語言,一個值永遠不會被自動轉換成其餘類型
    • 若是要轉換,必須顯示轉換,Swift 中
      • 小數默認是 Double 類型
      • 整數默認是 Int 類型
    • 若是要顯式的指定變量的類型,能夠在定義是使用 var 變量名: 類型 = 值
      • 變量: var
      • 常量: let
      • 格式: 修飾符 變量/常量名稱: 數據類型 = 值
  • 數據類型
    • 只要將OC中的數據類型第一個字母轉換爲大寫, 就是Swift中的數據類型
  • 注意點:
    • 在Swift開發中, 通常狀況下先用let, 只要須要修改數據時才用var, 使用let的好處, 能夠避免數據被修改, 能夠保證數據安全性
var number: Int = 30 number = 50 let number2: Int = 88 //number2 = 55
  • 類型推導:
    • Swift中若是在定義變量/常量時進行初始化, 那麼數據類型能夠不用寫,系統會自動根據右邊的複製推導出變量/常量的類型
    • Swift開發中能不寫數據類型就不寫數據類型, 儘可能使用編譯器的自動推導
    • 只有當咱們須要明確的指定數據的長度, 獲取須要先定義再初始化時才明確的指定數據類型
    • 使用自動類型推導好處: 大大下降代碼中的冗餘代碼
let number3 = 10.10 var number4: Int number4 = 99
  • 類型轉換:
    • OC中有顯示轉換和隱式轉換 double value = 10.1 + 9
    • Swift中只有顯示轉換沒有隱式轉換, 也就是說只有相同類型的數據才能進行賦值和計算
// 只有相同數據類型才能賦值 let number5: Int = Int(55.5) // 50 // 只有相同數據類型才能進行運算 let number6 = 10 let number7 = 88.8 let sum = Double(number6) + number7 // 98.8 // CGFloat --> double let size = CGSize(width: 10, height: 10) let number8 = 10.1 let sum2 = size.width + CGFloat(number8) // 20.1

  • 3.元祖
let number1 = 10 let number2 = 10.1
  • 元祖閉包

    • 複合數據類型
    • 只要將多個相同或者不一樣的數據用()括起來就是元祖
    • 優勢: 在之前沒有元祖以前C和OC語言是經過傳入指針或者返回結構體的方式來返回多個值的,而有了元祖以後就能夠實現讓一個函數返回多個值
let number3: (Int, Double, Int, Double) = (10, 10.1, 9, 44.40) //(.0 10, .1 10.1, .2 9, .3 44.4) number3.0 // 10 number3.1 // 10.1 number3.2 // 9 number3.3 // 44.40 // 給元祖的元素起名稱 let person = (name: "gcy", age: 30, score: 100.0) // (.0 "gcy", .1 30, .2 100) person.name // "gcy" person.age // 30 person.score // 100 // 提取元祖的數據 let (name, age, score) = ("gcy", 30, 100.0) name age score

  • 4.分支
var i = 10 if i > 0 { print("OK") }
  • 小結架構

    • Swift 中沒有 C 語言中的非零即真概念
    • 在邏輯判斷時必須顯示地指明具體的判斷條件
    • if 語句條件的 () 能夠省略
    • 可是 {} 不能省略
  • ifapp

    • 大部分用於和OC中一致
    • Swif中條件語句能夠不用寫()
    • OC中若是if後面只有一條語句, 那麼{}能夠省略, 可是Swift不行
    • OC中條件語句能夠是任何數值, OC中非0即真, YES/NO
    • Swift中條件語句的取值必須是Bool類型,
    • 也就是說Swift中提供了真正的Bool類型, true/false
let number = 10 //if number = 10 // Swift有效的避免了這種問題 if number == 10 { print(number) } let age = 16 if age >= 18 { print("開網卡") }else { print("回家找媽媽") }
  • 三目運算符
var a = 10 var b = 50 var result = a > b ? a : b print(result) // 50
  • 小結
    • Swift 中的 三目 運算保持了和 OC 一致的風格
    • 大部分用法和OC同樣
    • 條件表達式只能是Bool值
print(age >= 18 ? "開網卡" : "回家找媽媽")
  • switch
    • 大部分用法和OC同樣
      Swift中條件語句能夠不用寫()
    • OC中default能夠省略, 而Swift中大部分狀況不能省略
    • OC中default的位置能夠隨便寫, 而Swift不能夠
    • OC中每一個case後面必須加上break, 不然會出現穿透, 而Swift不會穿透, 也就是說不用寫break
    • OC中要在case中間定義變量必須加上{}, 不然做用域混亂, 而Swift不用
    • 能夠判斷區間和元祖
let score = 100 switch score { case 59: print("不及格") var num = 100 case 100: print("滿分") default: print("Other") }
  • 區間
    • 閉區間: 0...10 , 取值範圍0~10, 包含頭包含尾
    • 半閉區間: 0..<10 取值範圍0~9, 包含頭不包含尾
// 判斷區間 switch score { case 0..<60: // 0~59 print("不及格") case 60..<80: // 60~79 print("良好") case 80..<100: // 80~99 print("優秀") default: print("滿分") } let point = (100, 50) // 判斷元祖 switch point { case (0, 0): print("原點") case (50, 50): print("中點") case (100, 100): print("右下角") default: print("Other") } // 取出元祖中的值 switch point { case (var x, var y) where x > y: print(x) print(y) default: print("Other") }

  • 5.可選類型

  • 什麼是可選類型: 一個變量能夠有值也能夠沒有值, 咱們就稱之爲可選類型

    • 在Swift中若是使用一個可選類型的變量/常量, 必須解包操做
      • 解包: 只須要在變量/常量後面加上 !
      • !含義: 表明告訴系統該變量/常量中必定有值, 若是強制解包一個沒有值的常量/變量,那麼會報錯
    • 優勢: 提升代碼閱讀性
    • 格式: 修飾符 變量名稱:Optional<數據類型> 修飾符 變量名稱: 數據類型?
    • 語法糖: 由於在Swift中可選類型用得很是很是多, 因此爲了簡化代碼, Swift提供了一個語法糖, 能夠用? 代替 Optional<數據類型>
  • 提示: 對於初學者來講 ? ! 是最爲頭疼的語法, 剛開始的時候建議利用Xocde的語法提示來解決? !的問題
//var number: Optional<Int> = nil //var number2: Int = nil let number: Optional<Int> = 10 print(number!) // 10 let number2 = 10 let sum = number! + number2 // 20 let number3: Int? = 55 print(number3) // "Optional(55)\n" /* 可選類型注意點: * 在開發中通常狀況下儘可能不要強制解包一個可選類型, 不然會引起錯誤 */ //let url = NSURL(string: "http://www.baidu.com") //print(url) //let request = NSURLRequest(URL: url!) // "Optional(http://www.baidu.com/)\n" let url = NSURL(string: "http://www.baidu.com/") print(url) //let request = NSURLRequest(URL: url!) if url != nil { let request = NSURLRequest(URL: url!) } // 可選綁定: 若是url不爲nil, 系統內部就會自動將解包以後的值賦值給temp, 而且只有temp有值時纔會執行{}中的代碼 // Swift開發中推薦這種寫法 if let temp = url { let request = NSURLRequest(URL: temp) }
  • 演練 1
let url = NSURL(string: "http://www.baidu.com/") if url != nil { NSURLSession.sharedSession().dataTaskWithURL(url!, completionHandler: { (data, _, _) -> Void in print(NSString(data: data!, encoding: NSUTF8StringEncoding)) }).resume() }
  • 小結

    • Swift 中,不是全部的對象實例化方法都會返回值,在實際開發中須要注意實例化函數的返回類型,例如:

      convenience init?(string URLString: String)
    • 若是有 ? 表示改方法有可能沒法實例化到正確的對象

    • 這種函數返回的對象,被稱爲 可選項,即有可能有值,也有可能沒有值
    • 實際開發時,須要針對這種對象加以判斷,而且在分支內部使用 !,指明改對象確實是存在的
    • 相比在 OC 的開發,尤爲在平常練習時,會給定一個可以運行的值,而在實際運行時,一旦條件不知足,會直接閃退,這樣用戶體驗會很是很差

Swift 的設計者考慮到由於對類型的強制要求,會讓代碼很難看,所以提供了一個變通的解決方案

  • 演練 2
if let url = NSURL(string: "http://baidu.com") { NSURLSession.sharedSession().dataTaskWithURL(url, completionHandler: { (data, _, _) -> Void in print(NSString(data: data!, encoding: NSUTF8StringEncoding)) }).resume() }
  • 小結

    • 使用 if let 常量 = 可選構造函數 的方式可以確保分支內部常量必定是有值的
    • 而且在分支內部再也不須要使用 !
    • 這是 Swift 代碼中的一個很是重要的使用技巧
  • 提示

    • 儘管 Swift 提供了類型校驗的手段,可是要寫出 優雅 的 Swift 代碼,仍是須要多加練習的,不然一不當心就會出現分支嵌套層次很深的代碼
    • 有關 ?! 的選擇,能夠藉助 Xcode 的輔助工具,可是強烈建議每次遇到提示時,要多加思考,反覆揣摩
  • 演練3

var name: String? // nil print(name?.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)) // "nil\n" //name = "gcy" print(name?.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)) // "nil\n" print((name?.lengthOfBytesUsingEncoding(NSUTF8StringEncoding) ?? 0)) //"0\n"
  • 小結
    • ?? 是一個很是有用的操做符,可以快速對 nil 進行判斷
    • 若是對象是 nil,則使用 ?? 後面的值代替前面的 nil 值參與計算
    • 在使用 ?? 時,整個部分須要使用 () 包裝
    • 這一技巧在 UITableView 的數據源方法中尤其重要

  • 6.循環
  • OC風格的 for
// 傳統寫法 for var i = 0; i < 10; i++ { print(i) // (10 times) }
  • Swift風格的 for
// 遍歷 0 ~ <10 for i in 0..<10 { print(i) // (10 times) } print("---") // "---\n" // 遍歷 0 ~ 10 for i in 0...10 { print(i) // (11 times) }
  • 小結

    • Swift 中使用 in 關鍵字標示循環的範圍
    • 0..<10 表示從0到9
    • 0...10 表示從0到10
    • 注意之間不能出現空格
  • 特殊寫法

for _ in 0...10 { print("hello") // (11 times) }
  • 小結

    • 若是不關心循環自己的索引,可使用 _ 忽略
    • 傳統for
      • 基本用法和OC一致
      • for後面的()能夠省略
      • for後面的{}不可用省略
      • Swift開發中不建議使用傳統for循環
for var i = 0; i < 10; i++ { print(i) // (10 times) } //死循環 for ;; { print("---") // (18657 times) }
// Swift開發中推薦的for循環格式 for i in 0..<10 { print(i) // (10 times) }
  • while
    • 基本用法和OC一致
var number = 0 while number < 10 { print(number) // (10 times) number++ }
  • do while
    • 基本用法和OC一致
    • Swift2.0開始dowhile循環中沒有do, 只有repeat, 由於do被做用異常處理
var index = 0 repeat{ print(index) // (10 times) index++ }while index < 10

  • 7.數組
    • 數組
      • 格式 var arr: Array<Int> / var arr: [Int]
      • 可變和不可變 var/let
  • 簡單體驗
let arr = ["zhangsan", "lisi"] print(arr) // "["zhangsan", "lisi"]\n" // 遍歷每個元素 for a in arr { print(a) // (2 times) } // 像 OC 同樣打印 print(arr as NSArray) // "(\n zhangsan,\n lisi\n)\n"
  • 數組中保存的對象類型
// 數組中保存的都是字符串 let arr = ["zhangsan", "lisi"] // 數組中保存的是 NSObject let arr1 = ["zhangsan", 1]
  • 小結

    • 數組使用 [] 定義,這一點與 OC 相同
    • 若是初始化時,全部內容類型一致,則數組中保存的是該類型的內容
    • 若是初始化時,全部內容類型不一致,則數組中保存的是 NSObject
  • 常見數組操做

// 定義只能保存字符串類型數組 var array: [String] // 初始化數組 array = ["zhangsan"] // 添加元素 array.append("lisi") print(array) // "["zhangsan", "lisi"]\n" // 刪除元素 array.removeAtIndex(1) // "lisi" print(array) // "["zhangsan"]\n" // 刪除全部元素 array.removeAll(keepCapacity: true) // [] print(array.capacity) // "2\n" // 注意數組容量的變化 for i in 0..<10 { array.append("\(i)") // (10 times) print("\(array) --- \(array.capacity)") // (10 times) } // 實例化新的數組 var array2 = [String]() // [] array2.append("1") //["1"] array2.append("2") // ["1", "2"] // 拼接數組 array += array2 // ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "1", "2"] print(array) // "["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "1", "2"]\n"
  • 小結
    • 若是定義數組時指定了保存對象的類型,擇不能向數組中添加其餘類型的內容
    • 可使用 [String]()
    • let 定義的數組是不可變的
    • var 定義的數組是可變的
// 定義數組 //var arr: Array<Int> //var arr: [Int] //arr = [1, 2] var arr = [1, 2] // 1.遍歷數組(取值) arr[0] for item in arr { print(item) // (2 times) } // 2.添加 arr.append(3) arr // 3.修改 arr[1] = 9 arr // 4.刪除 arr.removeAtIndex(0) // 1 arr // [9, 3] // 5.合併 var arr1 = [3, 5, 7] arr += arr1 // [9, 3, 3, 5, 7] arr // [9, 3, 3, 5, 7] // 6.Swift特殊 for item in arr[0..<2] // 0~1 { print(item) // (2 times) } //arr.removeRange(Range(start: 0, end: 2)) //arr // 經過觀察能夠發現Range其實就是一個半閉區間 arr.removeRange(0..<2) // [3, 5, 7] arr // [3, 5, 7] arr += arr1[0..<2] // [3, 5, 7, 3, 5]

  • 8.字典

    • 字典
      • 格式 var dict: Dictionary<String, String>
      • 注意: 將OC的{}換成了[]
      • 可變和不可變 var/let
//var dict: Dictionary<String, String> //var dict: [String: String] var dict = ["name": "gcy", "age": "30"] dict // 企業開發中字典使用得最多的類型就是 [String: NSObject]類型 var dict2 = ["name": "gcy", "age": 30, "score": 99.9] dict2 // 取值 dict2["name"] // 修改 dict2["name"] = "wy" dict2 // 增長 // 若是key存在就直接修改, 若是key不存在就會增長 dict2["rank"] = 1 dict2 // 刪除 dict2.removeValueForKey("name") dict2 // 遍歷 // OC寫法 for key in dict2.keys { print(dict2[key]) // (3 times) } // Swift寫法 // 系統會自動將字典中的key賦值給元祖中的第一個遍歷, 會自動將字典中的value賦值給元祖中的第二個遍歷 for (xx, oo) in dict2 { print(xx) // (3 times) print(oo) // (3 times) } // 合併 var dict3 = ["name": "gcy", "age": 30] var dict4 = ["score": 99.9] // 注意點不管是數組仍是字典, 只有相同類型才能賦值 for (key, value) in dict4 { dict3[key] = value // 99.9 } dict3 // ["score": 99.9, "age": 30, "name": "gcy"]

  • 9.字符串

    在 Swift 中絕大多數的狀況下,推薦使用 String 類型

  • 使用 String 的緣由

  • String 是一個結構體,性能更高

    • String 目前具備了絕大多數 NSString 的功能
    • String 支持直接遍歷
  • NSString 是一個 OC 對象,性能略差
  • Swift 提供了 StringNSString 之間的無縫轉換

  • 遍歷字符串

let str = "我要飛的更High" for s in str { print(s) }
  • 字符串拼接
let str1 = "zhangsan" let str2 = "lisi" let i = 10 print(str1 + str2) // "zhangsanlisi\n" print("\(str1) \(str2) \(i)") // "zhangsan lisi 10\n"
  • 小結
    • 直接在 "" 中使用 \(變量名) 的方式能夠快速拼接字符串
    • 小夥伴們不再要考慮 stringWithFormat
  • 格式化字符串
for _ in 0...10 { let str = String(format: "zhangsan - %04d", arguments: [arc4random_uniform(100)]) print(str) // (11 times) zhangsan - 0002 }
  • 小結

    • 在實際開發中,若是須要指定字符串格式,可使用 String(format:...) 的方式
    • 注意:後面的參數須要放在一個數組中
  • String & Range 的結合

如下是超級費勁的代碼

let str: String = "beixinke" var subStr = str.substringWithRange(Range<String.Index>(start: str.startIndex, end: str.endIndex)) print(subStr) // "beixinke\n"
  • 建議寫法
let str1: NSString = "beixinke" // "beixinke" print(str1.substringWithRange(NSMakeRange(0, 3))) // "bei\n"
  • 字符串
    • OC的字符串是NSString, Swift的字符串String
    • OC的字符串是一個對象, Swift字符串是一個結構體, 效率更高
    • OC中的字符串是一個\0結尾, Swift字符串不是以\0結尾
NSString *str = @"abc\0def"; NSLog("%@", str); // abc
  • Swift中的字符串支持遍歷
let str = "abc\0def" // "abc�def" print(str) // "abc�def\n" // 遍歷字符串 for c in str.characters { print(c) // (7 times) } // 字符串拼接 var str2 = "gcy" str2 += str str2 // "gcyabc�def" // 字符串格式化 // 可使用\()在字符串中插入任何數據 let name = "gcy" let age = 30 let res = "name = \(name), age = \(age)" res // "name = gcy, age = 30" // 2015-10-09 03:04 let str3 = String(format: "%d-%02d-%02d %02d:%02d", arguments: [2015, 10, 9, 3, 4]) // "2015-10-09 03:04" // 截取字符串 // 提示: 在Swift開發中, 咱們常常須要將Swift的字符串轉換爲OC的字符串來操做, 而且Swift自身也意識到了這一點, 因此在OC字符串和Swift的字符串之間轉換至關簡單 let str4 = "beixinke" //let str5: NSString = str4 //str5.substringToIndex(4) //str5.substringWithRange(NSMakeRange(4, 2)) // as 就是把什麼當作什麼 (str4 as NSString).substringWithRange(NSMakeRange(4, 2)) // "in"

  • 10.函數
  • 簡單演練
func sum(a: Int, b: Int) -> Int { return a + b }
  • 小結

    • 函數定義格式:func 函數名(參數: 參數類型...) -> 返回值 { // 代碼實現 }
    • 若是沒有返回值, -> 返回值 能夠省略
    • -> 是一個頗有意思的符號
    • 默認狀況下,在調用函數時,第一個參數名是省略的
    • Void == ()
  • 參數名的特殊處理

  • 強制要求參數名

func sum1(x a: Int, y b: Int) -> Int { return a + b }
  • 省略參數名
func sum2(a: Int, _ b: Int) -> Int { return a + b }
// 1.沒有參數沒有返回值 func say() -> Void { print("hi") // "hi\n" } say() // 若是沒有返回值能夠簡寫 func say1() -> () { print("hi") // "hi\n" } say1() func say2() { print("hi") // "hi\n" } say2() // 2.有參數沒有返回值 // Swift2.0開始, 會自動將形參列表的第二個參數名稱做爲標籤 // Swift2.0以前是沒有這個特性的, 在Swift2.0以前若是須要顯示標籤須要在形參名稱前面加上# func sum(num1: Int, num2: Int) { print(num1 + num2) // "30\n" } sum(10, num2: 20) // 3.沒有參數有返回值 func getNumber() -> Int { return 998 } print(getNumber()) // "998\n" // 4.有參數有返回值 func sum2(num1: Int, num2: Int) -> Int { return num1 + num2 } print(sum2(50, num2: 50)) // "100\n" // 內部和外部參數 /* * 默認狀況下全部形參都是內部參數, 也就是說只能在函數內部使用 * 從Swift2.0開始會自動將形參列表的第二個參數名稱做爲標籤, 也就是說從第二個參數開始, 參數的名稱既是內部參數又是外部參數 * 如何指定外部參數? */ func sum3(num1: Int, y num2: Int) { print("num1 = \(num1), num2 = \(num2)") // "num1 = 10, num2 = 20\n" print(num1 + num2) // "30\n" } //sum3(10, num2: 20) sum3(10, y: 20) // 默認參數 // 若是指定了默認值, 那麼在調用方法的時候就能夠不用傳遞數據, 若是不傳遞數據系統就會使用默認值, 若是傳遞了就會使用傳遞的值 // 在其它語言裏面, 默認值通常狀況只能是最後一個參數, 可是Swift能夠寫在任何位置 func joinString(str1: String, str2: String = "在", str3: String) -> String { return str1 + str2 + str3 // (2 times) } joinString("gcy", str2: "也在", str3: "bxk") // "gcy也在bxk" joinString("wy", str3: "bxk") // "wy在bxk" // 常量參數和變量參數以及inout參數 // 默認狀況下全部形參都是常量參數, 不能在函數中修改形參的值 // 若是想在函數中修改形參的值, 那麼必須把形參變爲變量參數 // 和OC同樣, 在函數中修改形參的值不會影響到外面實參的值 // 若是想在函數中修改形參以後影響實參, 那麼必須把形參變爲inout參數 //func swap(a: Int, b: Int) //{ // let temp = a // a = b // 不能修改常量參數 // b = temp //} //func swap(var a: Int, var b: Int) //{ // print("a = \(a), b = \(b)") // let temp = a // a = b // b = temp // print("a = \(a), b = \(b)") //} func swap(inout a: Int, inout b: Int) { print("a = \(a), b = \(b)") // "a = 10, b = 20\n" let temp = a // 10 a = b // 20 b = temp //10 print("a = \(a), b = \(b)") // "a = 20, b = 10\n" } var x = 10 var y = 20 print("x = \(x), y = \(y)") // "x = 10, y = 20\n" swap(&x, b: &y) print("x = \(x), y = \(y)") // "x = 20, y = 10\n" // 可變參數 // 只要參數是可變參數, 就能夠傳遞一個或多個值 // 在其它語言中通常狀況下可變參數只能是最後一個形參, 而Swift中能夠寫在任意位置, 可是爲了提升代碼的閱讀性, 仍是建議寫在最後 func sum4(nums: Int..., temp: Int) -> Int { var sum = 0 // 0 for i in nums { sum += i // (3 times) } return sum + temp // 16 } sum4(1, 2, 3, temp: 10) // 16 // 函數嵌套 // 將一個函數寫到另一個函數的函數體中, 外面稱之爲函數嵌套 // 1.被嵌套的函數只能在父函數內部訪問 // 2.被嵌套的函數能夠訪問外部的變量 // 應用場景: 兩個函數之間依賴較強, 或者一個函數就只給另一個函數使用 // 例如: 對數組排序是一個函數, 交換變量又是一個函數, 他們就可使用函數嵌套 let value = 55 // 55 func test() { let number = 10 // 10 func demo() { print("----\(number), \(value)") // "----10, 55\n" } demo() } test()

  • 11.構造函數
    • Swift中要求在建立一個類時必須給這個類中全部的屬性進行初始化
    • 若是不能在建立對象時給這個類中全部的屬性進行初始化, 那麼這些屬性必須是可選的
    • 若是已經在構造方法中對全部的屬性進行了初始化, 那麼這些屬性就能夠不是可選類型
    • 在給某一個類指定屬性的數據類型時, 若是該屬性是對象類型, 那麼能夠指定爲可選類型
    • 若是該屬性不是對象類型而是基本數據類型, 那麼建議直接賦值爲0
      • 調用 super.init()的目的主要是爲了給對象分配存儲空間
    • Swift中若是想在構造方法中使用KVC轉換模型, 必須先調用 super.init()
class Person: NSObject { var name: String? // 若是屬性是基本數據類型, 而且是可選類型, 系統不會自動分配存儲空間 var age: Int = 0 // var name: String // var age: Int // Person() override init() { // 注意: 在構造方法中必須先初始化本類再初始化父類 name = "gcy" age = 24 // 當咱們重寫一個類的構造方法時, 系統內部會悄悄的幫咱們調用super.init() super.init() } // 自定義構造方法 init(name: String, age: Int) { self.name = name self.age = age // 如下這一句代碼, 能不寫就不寫 // super.init() } init(dict: [String: AnyObject]) { // 注意:Swift中若是想在構造方法中使用KVC轉換模型, 必須先調用 super.init() super.init() setValuesForKeysWithDictionary(dict) } // Swift中打印對象會調用下面這個屬性 override var description: String { // return "name = \(name), age = \(age)" let property = ["name", "age"] let dict = dictionaryWithValuesForKeys(property) return "\(dict)" } }
  • 注意: Swift開發中通常狀況下不用導入頭文件, 由於只要全部的文件都在一個命名空間中那麼就能夠直接使用
  • 默認狀況下一個項目的命名空間就是項目名稱, 而在同一個項目下的全部文件都在同一個命名空間中
  • 若是自定義一個類, 而且沒有重寫構造方法, 那麼系統會提供默認的構造方法
  • 若是自定義一個類, 而且自定義了構造方法, 那麼系統不會提供默認的構造方法

  • 12.getter & setter
    • 自定義 Person 類
class Person: NSObject { var name: String? var age: Int? }
  • getter & setter
var _name: String? var name: String? { get { return _name } set { _name = newValue } }
  • Swift 中以上形式的 getter & setter 不多用

  • didSet

  • 在 OC 中,咱們一般但願在給某一個變量賦值以後,去作一些額外的操做

  • 最經典的應用就是在自定義 Cell 的時候,經過模型的設置方法完成 Cell 的填充
var length: Int? { didSet { timeStr = String(format: "%02d:%02d:%02d", arguments: [length! / 3600, (length! % 3600) / 60, length! % 60]) } } var timeStr: String?
var name: String? { // 在Swift開發中用如下兩個方法代替OC中的重寫setter方法 willSet{ print("賦值以前調用 \(newValue)") } didSet{ print("賦值以後調用 \(oldValue)") } }
  • 計算型屬性
var title: String { get { return "Mr " + (name ?? "") } }
  • 只實現 getter 方法的屬性被稱爲計算型屬性,等同於 OC 中的 ReadOnly 屬性
  • 計算型屬性自己不佔用內存空間
  • 不能夠給計算型屬性設置數值
  • 計算型屬性可使用如下代碼簡寫
var title: String { return "Mr " + (name ?? "") }
  • 構造函數
init(dict: [NSObject: AnyObject]) { name = dict["name"] as? String age = dict["age"] as? Int }
  • 析構函數
deinit { print("88") }

  • 13.閉包
    • OC: block相似於匿名函數, 用於封裝代碼塊, 在特定的時候執行
    • 執行一些耗時操做
    • 類型: 返回值類型(^block名稱)(形參列表)
值:
            ^(形參列表){
                須要執行的代碼
            }
  • Swift: 閉包是用於定義函數(Swift中函數就是閉包, 閉包就是一個特殊的函數)
    • 執行一些耗時操做
    • 類型: (形參列表)->返回值類型
      值:
      {
          (形參列表)->返回值類型
          in 須要執行的代碼
  • 閉包的幾種格式
    1. 完整寫法
loadData ({ () -> () in print("更新UI") })
  • 2.若是閉包沒有形參, 那麼in和in以前的代碼均可以省略
loadData ({
        print("更新UI") })
  • 3.若是閉包是函數的最後一個參數, 那麼閉包能夠寫在函數()的後面
loadData (){
        print("更新UI") }
  • 4.若是函數只有一個閉包參數, 那麼函數的()能夠省略
loadData {
        print("更新UI") }
func loadData(finished: ()->()) { dispatch_async(dispatch_get_global_queue(0, 0)) { () -> Void in print(NSThread.currentThread()) print("加載數據") dispatch_async(dispatch_get_main_queue(), { () -> Void in print(NSThread.currentThread()) finished() }) } }
  • 能用最簡單的就用最簡單的寫法

  • 閉包的參數和返回值

    示例:
    1.在控制器的View上添加一個UIScrollview, 而後在UIScrollview上添加15個按鈕, 讓按鈕平鋪 2.要求建立UIScrollview以及按鈕經過一個方法來建立 2.1而且按鈕的個數必須經過閉包來指定 2.2而且如何建立按鈕也必須經過閉包來指定(UIScrollview上面添加什麼控件經過閉包傳遞)
/* // 1.建立UIScrollview let sc = UIScrollView(frame: CGRect(x: 0, y: 200, width: UIScreen.mainScreen().bounds.width, height: 50)) sc.backgroundColor = UIColor.redColor() // 2.經過循環建立15個按鈕 let count = 15 let width:CGFloat = 80 let height = sc.bounds.height for i in 0..<count { let btn = UIButton() btn.setTitle("標題\(i)", forState: UIControlState.Normal) btn.frame = CGRect(x: CGFloat(i) * width, y: 0, width: width, height: height) btn.backgroundColor = UIColor.greenColor() // 3.將按鈕添加到UIScrollview上 sc.addSubview(btn) } sc.contentSize = CGSize(width: CGFloat(count) * width, height: height) // 4.將UIScrollview添加到控制器view上 view.addSubview(sc) */ let sc = createScrollview({ () -> Int in return 20 }) { (index) -> UIView in // let btn = UIButton() // btn.setTitle("標題\(index)", forState: UIControlState.Normal) // btn.backgroundColor = UIColor.greenColor() let label = UILabel() label.text = "標題\(index)!!!" label.backgroundColor = (index % 2 == 0) ? UIColor.greenColor() : UIColor.purpleColor() return label } view.addSubview(sc) } // 技巧: 在編寫閉包代碼時, 無論三七二十一先寫上 ()->(), 而後再修改 func createScrollview(getNumber: ()->Int, createView: (index: Int)->UIView) -> UIScrollView { // 1.建立UIScrollview let sc = UIScrollView(frame: CGRect(x: 0, y: 200, width: UIScreen.mainScreen().bounds.width, height: 50)) sc.backgroundColor = UIColor.redColor() // 2.經過循環建立15個按鈕 let count = getNumber() let width:CGFloat = 80 let height = sc.bounds.height for i in 0..<count { /* let btn = UIButton() btn.setTitle("標題\(i)", forState: UIControlState.Normal) btn.frame = CGRect(x: CGFloat(i) * width, y: 0, width: width, height: height) btn.backgroundColor = UIColor.greenColor() */ let subView = createView(index: i) subView.frame = CGRect(x: CGFloat(i) * width, y: 0, width: width, height: height) // 3.將按鈕添加到UIScrollview上 sc.addSubview(subView) } sc.contentSize = CGSize(width: CGFloat(count) * width, height: height) return sc } }

  • 閉包的循環引用
// 注意: 在設置閉包屬性是可選類型時必定更要用一個()括住閉包的全部的類型, 不然只是指定了閉包的返回值是可選的 // 錯誤寫法: var callback: ()->()? var callback: (()->())? override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) { // OC中如何解決: __weak typeof(self) weakSelf = self; // Swift中如何解決: weak var weakSelf = self // 對應關係: __weak == weak __unsafe_unretained == unowned // weak var weakSelf = self loadData { [unowned self] () -> () in print("被回調了") // 在Swift開發中, 能不寫self就不寫slef // 通常狀況下只有須要區分參數, 或者在閉包中使用 // self.view.backgroundColor = UIColor.greenColor() // weakSelf!.view.backgroundColor = UIColor.greenColor() self.view.backgroundColor = UIColor.greenColor() } } func loadData(finished: ()->()) { callback = finished // 1.加載數據 print("加載數據") // 2.執行回調 finished() } // deinit 至關於OC中的dealloc方法 // 只要一個對象釋放就會調用deinit方法 deinit { print("88") } }

  • 14.懶加載
  • 只有外界訪問到listData的時候纔會去執行閉包, 而後將閉包的返回值賦值給listData
  • 注意: 必定要記住閉包後面須要寫上(), 表明執行閉包
lazy var listData: [String]? = { ()->[String] in print("---") return ["gcy", "cyh", "wy", "lsl"] }() // 開發中這樣寫 lazy var listData2: [String]? = { print("---") return ["gcy", "cyh", "wy", "lsl"] }() lazy var listData3: [String]? = self.test() func test() -> [String] { print("+++") return ["gcy", "cyh", "wy", "lsl"] } override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) { print(listData3) print(listData3) print(listData3) print(listData3) }

  • 15.單例模式
  • OC中
+ (instancetype)shareNetworkTools
{
    static NetworkTools *_instance; // onceToken默認等於0, 只要執行一次以後就不等於0了, 原理是經過判斷onceToken是否等於0決定是否須要執行block static dispatch_once_t onceToken; NSLog(@"%ld", onceToken); dispatch_once(&onceToken, ^{ _instance = [[NetworkTools alloc] init]; }); return _instance; }
  • 若是在Swift中編寫單例, 推薦使用以下寫法
  • 並且蘋果有統一的命名規範,但凡是單例統一是用shareInstance

    /* static var onceToken: dispatch_once_t = 0; static var _instance: NetworkTools? class func shareNetworkTools() -> NetworkTools { print(onceToken) dispatch_once(&NetworkTools.onceToken, { _instance = NetworkTools() }) return _instance! } */ /* static let _instance: NetworkTools = NetworkTools() class func shareNetworkTools() -> NetworkTools { return _instance } override init() { print("++++++") } */ static let shareInstance: NetworkTools = NetworkTools()

  • 16.UITableView基本使用
// 1.在Swift中遵照協議直接利用逗號隔開 class ViewController: UIViewController { override func loadView() { let tableView = UITableView() tableView.dataSource = self tableView.delegate = self view = tableView } // MARK: - 懶加載 lazy var listData: [String]? = { return ["gcy", "cyh", "wy", "sz", "lsl", "fbk"] }() } // extension 至關於OC的 Category extension ViewController: UITableViewDataSource, UITableViewDelegate { // MARK: - UITableViewDataSource func numberOfSectionsInTableView(tableView: UITableView) -> Int { return 1 } func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { // TODO: 有問題, 開發中不該該這樣寫 return (listData?.count)! } func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { var cell = tableView.dequeueReusableCellWithIdentifier("cell") if cell == nil { cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "cell") } cell?.textLabel?.text = listData![indexPath.row] return cell! } // MARK: - UITableViewDelegate func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { print(listData![indexPath.row]) } }


17.變量類型推導

  • 在 swift 開發中,對象的類型是自動推導
  • 對象的準確類型由建立對象時的代碼決定
  • 若是定義變量時,右側代碼具備歧義,能夠在左側以 :類型 指定變量的類型,例如:

    // 我的推薦 let i: CGFloat = 10
  • 也能夠寫成

    var j = 10 as? CGFloat
  • 若是某些方法返回的數據類型是 AnyObject/AnyClass,則須要在右側使用 as 類型 代表類型,而且根據返回值是不是可選項,添加 ! 或者 ?,例如:

    let ns = NSBundle.mainBundle().infoDictionary!["CFBundleExecutable"] as! String
  • 若是某些方法返回類型是 AnyObject/AnyClass,可是對象類型是動態生成的,也就是說,編碼時一樣沒法肯定改對象的準確類型,能夠在左側使用 : AnyObject 或者 : AnyClass 告訴編譯器暫不處理,例如:

    let cls: AnyClass = NSClassFromString(ns + "." + vcName)!

提示:as?as! 是剛接觸 swift 最使人煩惱的語法之一,蘋果也在這個語法規則上屢次作過調整,在學習時建議:

  • 多用 option + click 查看對象信息
  • 藉助 Xcode 智能提示修改

  • 18.guard--守護者--是swift2.0推出的。這樣就不會有嵌套中還有嵌套,避免了強制解包和循環嵌套
  • 在Swift中, 若是想經過字符串建立一個類, 那麼必須加上命名空間
  • 動態獲取的命名空間是不包含.的, 因此須要咱們本身手動拼接
  • 將AnyClass類型轉換爲UIViewController類型
/* 蘋果也意識到了, Swift會不知不覺的造成多層嵌套, 然代碼變得很是醜陋 因此Swift2.0的時候專門推出了一個條件語句(guard)來解決這個問題 格式: guard 條件表達式 else { 須要執行的語句 return } 特色: 只要條件爲假纔會執行else中的代碼 做用: 用於過濾數據 對比if else 這哥們只有else{}沒有if{} */


19.try catch

  • 在OC中處理異常是經過傳入一個NSError的指針來保存錯誤
  • Swfit中提供了專門處理異常機制 throws -> AnyObject
  • Swift中提供 try catch, 將有可能發生錯誤的代碼放到do中, 若是真的發生了異常就會執行catch
    • try做用: 若是拋出(throws)異常, 那麼就會執行catch
    • try!做用: 告訴必定必定沒有錯誤, 不須要處理, 可是若是使用try!發生了錯誤, 那麼程序就會崩潰, 開發中不推薦使用
    • try?做用: 告訴系統可能有錯也可能沒有錯, 若是發生錯誤會返回一個nil, 若是沒有發生錯誤, 會將數據包裝成可選類型
  • 在開發中若是說你拋了一個異常以後,你不想進行任何處理,就能夠用try?這樣的話try?返回一個nil,程序也不會崩潰。對你就無關痛癢了。
  • 可是你若是用try!拋異常就會崩潰;
  • 可是若是你有try就必定要do...catch不然就會報錯

throw catchXcode 7.0 對錯誤處理的一個很是大的變化

  • 範例代碼

    // 2. 反序列化 // 1.獲取json文件路徑 let jsonPath = NSBundle.mainBundle().pathForResource("MainVCSettings.json", ofType: nil) // 2.加載json數據 let jsonData = NSData(contentsOfFile: jsonPath!) // 3.序列化json do{ // throw是Xcode7最明顯的一個變化, Xcode7以前都是經過傳入error指針捕獲異常, Xocode7開始經過try/catch捕獲異常 let dictArray = try NSJSONSerialization.JSONObjectWithData(jsonData!, options: NSJSONReadingOptions.MutableContainers) // 遍歷字典時候須要明確指明數組中的數據類型 for dict in dictArray as! [[String:String]] { // 因爲addChildVC方法參數不能爲nil, 可是字典中取出來的值多是nil, 因此須要加上! addChildViewController(dict["vcName"]!, title: dict["title"]!, imageName: dict["imageName"]!) } }catch{ print(error) addChildViewController("HomeTableViewController", title: "首頁", imageName: "tabbar_home") addChildViewController("MessageTableViewController", title: "消息", imageName: "tabbar_message_center") addChildViewController("DiscoverTableViewController", title: "發現", imageName: "tabbar_discover") addChildViewController("ProfileTableViewController", title: "我", imageName: "tabbar_profile") }
  • 若是能確保代碼執行正確,能夠強行 try!

let array = try! NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions.MutableContainers)

不過須要注意的是,一旦解析錯誤,程序會直接崩潰!



20.private

  • 在swift中,裏面的東西都是可以全局訪問的,若是你只想只在本文件訪問的話,就加上一個private
    • 若是在方法前面加上private, 表明這個方法只能在當前文件中訪問
    • 若是在屬性前面加上private, 表明這個屬性只能在當前文件中訪問
    • 若是在類前面加上private, 表明這個類只能在當前文件中訪問
  • private也有利於程序員之間的溝通,一看到就知道是和本文件有關,不提供外界使用,點擊事件加上@objc就能夠知道是關於點擊事件
  • 提升閱讀性,記得用// MARK: - 內容


21.便利構造器

  • 在Swift開發中, 若是想快速建立一個對象, 那麼能夠提供一個便利構造器(便利構造方法--用於快速建立對象)
  • 只要在普通構造方法前面加上一個convenience, 那麼這個構造方法就是一個便利構造方法
  • 注意: 若是定義一個便利構造器, 那麼必須在便利構造器中調用指定構造器(沒有加convenience單詞的構造方法)
    • 遍歷構造方法的參數不能指定默認參數
import UIKit extension UIButton { class func create(imageName: String, backImageName: String) -> UIButton { let btn = UIButton() // 1.設置背景圖片 btn.setBackgroundImage(UIImage(named: imageName), forState: UIControlState.Normal) btn.setBackgroundImage(UIImage(named: imageName + "highlighted"), forState: UIControlState.Highlighted) // 2.設置普通圖片 btn.setImage(UIImage(named:backImageName), forState: UIControlState.Normal) btn.setImage(UIImage(named: backImageName + "highlighted"), forState: UIControlState.Highlighted) btn.sizeToFit() return btn } // 雖然以上方法能夠快速建立一個UIButton對象, 可是Swift風格不是這樣寫的 // 在Swift開發中, 若是想快速建立一個對象, 那麼能夠提供一個便利構造器(便利構造方法--用於快速建立對象) // 只要在普通構造方法前面加上一個convenience, 那麼這個構造方法就是一個便利構造方法 // 注意: 若是定義一個便利構造器, 那麼必須在便利構造器中調用指定構造器(沒有加convenience單詞的構造方法) /* 定義便利構造器步驟: 1.編寫一個構造方法 2.在構造方法前面加上 convenience 3.在構造方法中調用當前類的其餘"非便利構造器"初始化對象 */ convenience init(imageName: String, backImageName: String) { self.init() // 1.設置背景圖片 setBackgroundImage(UIImage(named: imageName), forState: UIControlState.Normal) setBackgroundImage(UIImage(named: imageName + "highlighted"), forState: UIControlState.Highlighted) // 2.設置普通圖片 setImage(UIImage(named:backImageName), forState: UIControlState.Normal) setImage(UIImage(named: backImageName + "highlighted"), forState: UIControlState.Highlighted) sizeToFit() } }原文:http://www.jianshu.com/p/5e2d4c34f18e
相關文章
相關標籤/搜索