Swift 2.2 - 介紹與基礎語法

Swift 介紹

簡介

  • Swift 語言由蘋果公司在 2014 年推出,用來撰寫 OS X 和 iOS 應用程序
  • 2014 年,在 Apple WWDC 發布
    • 幾家歡喜,幾家愁
    • 愁者:只學Object-C的人
    • 歡喜者:之前做過java/python/js語言的人

歷史

  • 2010 年 7 月,蘋果開發者工具部門總監 Chris Lattner 開始著手 Swift 編程語言的設計工作
  • 用一年時間,完成基本架構
  • Swift 大約歷經 4 年的開發期,2014 年 6 月發表
  • 克里斯·拉特納何許人?LLVM 項目的主要發起人與作者之一Clang 編譯器的作者蘋果公司『開發者工具』部門的主管領導Xcode、Instruments等編譯器團隊Swift的大部分基礎架構均由他1人完成評價:大神中的大神牛逼中的牛逼

特點

  • 特點
    • 從它的語法中能看到Objective-C、JavaScript、C#、Python等語言的影子
    • 語法簡單、代碼簡潔、使用方便
    • 可與Objective-C混合使用(相互調用)
    • 提供了類似 Java 的名字空間(namespace)、泛型(generic)、運算對象重載(operator overloading)
  • 為什么設計Swift語言
    • 讓應用開發更簡單、更快、更穩定
    • 確保最終應用有著更好的質量

重要性

  • 蘋果目前在大力推廣Swift
  • 斯坦福大學的公開課目前也是使用Swift在授課.因為以后Swift必將代替OC
  • 題外話:我們同學去面試,面試官問是否會Swift,如果會,我們下個項目直接用Swift來寫.你可以教我們Swift.
  • 個人建議:
    • 先掌握Swift最基本的語法
    • 高級/特殊的功能隨著學習的深入再深入研究
    • 千萬不要浮躁(前面班級經驗)
      • Swift并不難
      • 但是語法和OC區別非常非常大
      • 如果是一個聽一聽,聽不懂就算了的心態.一定是學不好的
      • 如果想要學習,就認真聽講,好好練習

資源網站

Swift初體驗

  • Playground是什么?
    • 從Xcode6開始出現(Swift開始出現)
    • 翻譯為:操場/游樂場
    • 對于學習Swift基本語法非常方便
      • 所見即所得(快速查看結果)
      • 語法特性發生改變時,可以快速查看.
  • Swift最基本的語法變化
    • 導入框架 import UIKit
    • 定義標識符時,必須聲明該標識符是變量還是常量
      • 聲明標識符的格式:變量/常量關鍵字 名稱 : 數據類型
    • 語句結束時不需要加;
      • 如果同一行有多個語句,則依然需要加
      • 但是不建議一行多條語句
    • Swift中的打印語句:print(打印的內容)

常量&變量

什么是常量和變量

  • 在Swift中規定:在定義一個標識符時必須明確說明該標識符是一個常量還是變量
  • 使用let來定義常量,定義之后不可以修改
  • 使用var來定義變量,定義之后可以修改

變量的基本使用

import UIKit

let a : Int = 10
// 錯誤寫法,當一個字段定義為常量時是不可以修改的
// a = 20

var b : Int = 20
// 因為b定義為變量,因此是可以修改的
b = 30

常量和變量的使用注意:

  • 注意:

    • 在真實使用過程中,建議先定義常量,如果需要修改再修改為變量(更加安全)
    • 是指向的對象不可以再進行修改.但是可以通過指針獲得對象后,修改對象內部的屬性
      // 注意:聲明為常量不可以修改的意思是指針不可以再指向其他對象.但是可以通過指針拿到對象,修改其中的屬性
      // view : UIView = [[UIView alloc] init];
      // Swift對象中不需要*
      var view : UIView = UIView()
      view = UIView()
    
      let view1 : UIView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
      view1.backgroundColor = UIColor.redColor()
    
      // 枚舉類型的用法:類型.枚舉的值
      let btn : UIButton = UIButton(type: UIButtonType.Custom)
      btn.backgroundColor = UIColor.blueColor()
      btn.setTitle("按鈕", forState: UIControlState.Normal)
      btn.frame = CGRect(x: 20, y: 20, width: 60, height: 30)
      view1.addSubview(btn)
    

Swift中數據類型

Swift類型的介紹

  • Swift中的數據類型也有:整型/浮點型/對象類型/結構體類型等等

  • 先了解整型和浮點型

  • 整型

    • 有符號
      • Int8 : 有符號8位整型
      • Int16 : 有符號16位整型
      • Int32 : 有符號32位整型
      • Int64 : 有符號64位整型
      • Int : 和平臺相關(默認,相當于OC的NSInteger)
    • 無符號
      • UInt8 : 無符號8位整型
      • UInt16 : 無符號16位整型
      • UInt32 : 無符號32位整型
      • UInt64 : 無符號64位整型
      • UInt : 和平臺相關(常用,相當于OC的NSUInteger)(默認)
  • 浮點型

    • Float : 32位浮點型
    • Double : 64浮點型(默認)
    // 定義一個Int類型的變量m,并且賦值為10
    var m : Int = 10
    // 定義一個Double類型的常量n,并且賦值為3.14
    let n : Double = 3.14
    

Swift中的類型推導

  • Swift是強類型的語言

  • Swift中任何一個標識符都有明確的類型

  • 注意:

    • 如果定義一個標識符時有直接進行賦值,那么標識符后面的類型可以省略.
    • 因為Swift有類型推導,會自動根據后面的賦值來決定前面的標識符的數據類型
    • 可以通過option+鼠標左鍵來查看變量的數據類型
      // 定義變量時沒有指定明確的類型,但是因為賦值給i一個20.20為整型.因此i為整型
      var i = 20
      // 錯誤寫法:如果之后賦值給i一個浮點型數值,則會報錯
      // i = 30.5
    
      // 正確寫法
      var j = 3.33
      j = 6.66
    

Swift中基本運算

  • Swift中在進行基本運算時必須保證類型一致,否則會出錯

    • 相同類型之間才可以進行運算
    • 因為Swift中沒有隱式轉換
  • 數據類型的轉化

    • Int類型轉成Double類型:Double(標識符)
    • Double類型轉成Int類型:Int(標識符)
      let a = 10
      let b = 3.14
    
      // 錯誤寫法
      // let c = a + b
      // let c = a * b
    
      // 正確寫法
      let c = Double(a) + b
      let d = a + Int(b)
    

邏輯分支

一. 分支的介紹

  • 分支即if/switch/三目運算符等判斷語句
  • 通過分支語句可以控制程序的執行流程

二. if分支語句

  • 和OC中if語句有一定的區別

    • 判斷句可以不加()
    • 在Swift的判斷句中必須有明確的真假
      • 不再有非0即真
      • 必須有明確的Bool值
      • Bool有兩個取值:false/true
        // 演練一:
        let a = 10
    
        // 錯誤寫法:
        //if a {
        //    print("a")
        //}
    
        // 正確寫法
        if a > 9 {
            print(a)
        }
    
        // 演練二:
        let score = 87
    
        if score < 60 {
            print("不及格")
        } else if score <= 70 {
            print("及格")
        } else if score <= 80 {
            print("良好")
        } else if score <= 90 {
            print("優秀")
        } else {
            print("完美")
        }
    
        // 演練三:
        // 這個是可選類型,因為只有聲明成可選類型后,才可以判斷是否為空
        // 可選類型會在后續講解,可先了解即可
        let view : UIView? = UIView()
    
        // 判斷如果view有值,則設置背景
        // 錯誤寫法
        //if view {
        //    view.backgroundColor = UIColor.redColor()
        //}
    
        if view != nil {
            view!.backgroundColor = UIColor.redColor()
        }
    

三. 三目運算符

  • Swift 中的 三目 運算保持了和 OC 一致的風格

    var a = 10
    var b = 50
    
    var result = a > b ? a : b
    println(result)
    

四.guard的使用

  • guard是Swift2.0新增的語法

  • 它與if語句非常類似,它設計的目的是提高程序的可讀性

  • guard語句必須帶有else語句,它的語法如下:

    • 當條件表達式為true時候跳過else語句中的內容,執行語句組內容
      guard 條件表達式 else {
          // 條換語句
          break
      }
      語句組
    
  • 例子

      var age = 18
    
      func online(age : Int) -> Void {
          guard age >= 18 else {
              print("回家去")
              return
          }
      
          print("可以上網")
      }
      
      online(age)
    

四.switch分支

switch的介紹
  • Switch作為選擇結構中必不可少的語句也被加入到了Swift中
  • 只要有過編程經驗的人對Switch語句都不會感到陌生
  • 但蘋果對Switch進行了大大的增強,使其擁有其他語言中沒有的特性
switch的簡單使用
  • 基本用法和OC用法一致

  • 不同之處:

    • switch后可以不跟()
    • case后可以不跟break(默認會有break)
  • 例子:

      let sex = 0
    
      switch sex {
      case 0 :
          print("男")
      case 1 :
          print("女")
      default :
          print("其他")
      }
    
  • 簡單使用補充:

    • 一個case判斷中,可以判斷多個值
    • 多個值以,隔開
      let sex = 0
    
      switch sex {
      case 0, 1:
          print("正常人")
      default:
          print("其他")
      }
    
  • 簡單使用補充:

    • 如果希望出現之前的case穿透,則可以使用關鍵字fallthrough
      let sex = 0
    
      switch sex {
      case 0:
          fallthrough
      case 1:
          print("正常人")
      default:
          print("其他")
      }
    
Switch支持多種數據類型
  • 浮點型的switch判斷

      let f = 3.14
      switch f {
      case 3.14:
          print("π")
      default:
          print("not π")
      }
    
  • 支持字符串類型

    • 字符串的使用后面會詳細講解
      let m = 5
      let n = 10
      var result = 0
    
      let opration = "+"
    
      switch opration {
          case "+":
              result = m + n
          case "-":
              result = m - n
          case "*":
              result = m * n
          case "/":
              result = m / n
      default:
          result = 0
      }
    
      print(result)
    
switch支持區間判斷
  • 什么是區間?

    • 通常我們指的是數字區間:010,100200
  • swift中的區間常見有兩種

    • 半開半閉區間:0..<10 表示:0~9,不包括10
    • 閉區間:0...10 表示:0~10
      let score = 88
    
      switch score {
      case 0..<60:
          print("不及格")
      case 60..<80:
          print("幾個")
      case 80..<90:
          print("良好")
      case 90..<100:
          print("優秀")
      default:
          print("滿分")
      }
    

循環的介紹

  • 在開發中經常會需要循環
  • 常見的循環有:for/while/do while.
  • 這里我們只介紹for/while,因為for/while最常見

for循環的寫法

  • 最常規寫法

      // 傳統寫法
      for var i = 0; i < 10; i++ {
          print(i)
      }
    
  • 區間for循環

      for i in 0..<10 {
          print(i)
      }
      
      for i in 0...10 {
          print(i)
      }
    
  • 特殊寫法

    - 如果在for循環中不需要用到下標i
      for _ in 0..<10 {
          print("hello")
      }
    

while和do while循環

  • while循環

    • while的判斷句必須有正確的真假,沒有非0即真
    • while后面的()可以省略
      var a = 0
      while a < 10 {
          a++
      }
    
  • do while循環

    • 使用repeat關鍵字來代替了do
      let b = 0
      repeat {
          print(b)
          b++
      } while b < 20
    

字符串的介紹

  • 字符串在任何的開發中使用都是非常頻繁的
  • OC和Swift中字符串的區別
    • 在OC中字符串類型時NSString,在Swift中字符串類型是String
    • OC中字符串@"",Swift中字符串""
  • 使用 String 的原因String 是一個結構體,性能更高NSString 是一個 OC 對象,性能略差String 支持直接遍歷Swift 提供了 StringNSString 之間的無縫轉換

字符的定義

  • 定義不可變字符串

      let str = "hello Objective-C"
    
  • 定義可變字符串

      var str = "hello Swift"
    

字符串的使用

獲取字符串的長度
  • 獲取字符集合,再獲取集合的count屬性

      let count = str.characters.count
    
遍歷字符串
    // 字符串遍歷
    var str = "Hello, Swift"
    for c in str.characters {
        print(c)
    }
字符串拼接
  • 兩個字符串的拼接

      let str1 = "Hello"
      let str2 = "World"
      let str3 = str1 + str2
    
  • 字符串和其他數據類型的拼接

      let name = "why"
      let age = 18
      
      let info = "my name is \(name), age is \(age)"
    
  • 字符串的格式化

    • 比如時間:03:04
      let min = 3
      let second = 4
    
      let time = String(format: "%02d:%02d", arguments: [min, second])
    
字符串的截取
  • Swift中提供了特殊的截取方式

    • 該方式非常麻煩
    • Index創建較為麻煩
  • 簡單的方式是將String轉成NSString來使用

    • 在標識符后加:as NSString即可
      let myStr = "www.baidu.com"
      var subStr = (myStr as NSString).substringFromIndex(4)
      subStr = (myStr as NSString).substringToIndex(3)
      subStr = (myStr as NSString).substringWithRange(NSRange(location: 4, length: 5))
    
  • swift截取方式

      // 1.定義字符串
      let str = "www.baidu.com"
      
      // 2.截取開始位置
      let fromIndex = str.startIndex.advancedBy(3)
      let header = str.substringFromIndex(fromIndex)
      
      // 3.截取結束位置
      let toIndex = str.endIndex.advancedBy(-3)
      let footer = str.substringToIndex(toIndex)
      
      // 4.截取中間的字符串
      let range = Range(start: str.startIndex.advancedBy(4), end: str.endIndex.advancedBy(-4))
      let middle = str.substringWithRange(range)
    

數組

數組的介紹

  • 數組(Array)是一串有序的由相同類型元素構成的集合
  • 數組中的集合元素是有序的,可以重復出現
  • Swift中的數組
    • swift數組類型是Array,是一個泛型集合

數組的初始化

  • 數組分成:可變數組和不可變數組

    • 使用let修飾的數組是不可變數組
    • 使用var修飾的數組是可變數組
      // 定義一個可變數組,必須初始化才能使用
      var array1 : [String] = [String]()
    
      // 定義一個不可變數組
      let array2 : [NSObject] = ["why", 18]
    
  • 在聲明一個Array類型的時候可以使用下列的語句之一

      var stuArray1:Array<String>
      var stuArray2: [String]
    
  • 聲明的數組需要進行初始化才能使用,數組類型往往是在聲明的同時進行初始化的

      // 定義時直接初始化
      var array = ["why", "lnj", "lmj"]
      // 先定義,后初始化
      var array : Array<String>
      array = ["why", "lnj", "lmj"]
    

對數組的基本操作

    // 添加數據
    array.append("yz")
    
    // 刪除元素
    array.removeFirst()
    
    // 修改元素
    array[0] = "why"
    
    // 取值
    array[1]

數組的遍歷

    // 遍歷數組
    for i in 0..<array.count {
        print(array[i])
    }
    
    // forin方式
    for item in array {
        print(item)
    }
    
    // 設置遍歷的區間
    for item in array[0..<2] {
        print(item)
    }
    
    // 遍歷數組的同時獲取下標值
    let names = ["why", "yz", "lnj", "lmj"]
    for (index, name) in names.enumerate() {
        print(index)
        print(name)
    }

數組的合并

    // 數組合并
    // 注意:只有相同類型的數組才能合并
    var array = ["why", "lmj","lnj"]
    var array1 = ["yz", "wsz"]
    var array2 = array + array1;
    
    // 不建議一個數組中存放多種類型的數據
    var array3 = [2, 3, "why"]
    var array4 = ["yz", 23]
    array3 + array4

字典

字典的介紹

  • 字典允許按照某個鍵來訪問元素
  • 字典是由兩部分集合構成的,一個是鍵(key)集合,一個是值(value)集合
  • 鍵集合是不能有重復元素的,而值集合是可以重復的,鍵和值是成對出現的
  • Swift中的字典
    • Swift字典類型是Dictionary,也是一個泛型集合

字典的初始化

  • Swift中的可變和不可變字典

    • 使用let修飾的數組是不可變字典
    • 使用var修飾的數組是可變字典
      // 定義一個可變字典
      var dict1 : [String : NSObject] = [String : NSObject]()
    
      // 定義一個不可變字典
      let dict2 = ["name" : "why", "age" : 18]
    
  • 在聲明一個Dictionary類型的時候可以使用下面的語句之一

      var dict1: Dictionary<Int, String>
      var dict2: [Int: String]
    
  • 聲明的字典需要進行初始化才能使用,字典類型往往是在聲明的同時進行初始化的

      // 定時字典的同時,進行初始化
      var dict = ["name" : "why", "age" : 18]
    

// swift中任意對象,通常不使用NSObject,使用AnyObject
var dict : Dictionary<String, AnyObject>
dict = ["name" : "why", "age" : 18]
?```

字典的基本操作

    // 添加數據
    dict["height"] = 1.88
    dict["weight"] = 70.0
    dict
    
    // 刪除字段
    dict.removeValueForKey("height")
    dict
    
    // 修改字典
    dict["name"] = "lmj"
    dict.updateValue("lmj", forKey: "name")
    dict
    
    // 查詢字典
    dict["name"]

字典的遍歷

    // 遍歷字典中所有的值
    for value in dict.values {
        print(value)
    }
    // 遍歷字典中所有的鍵
    for key in dict.keys {
        print(key)
    }
    
    // 遍歷所有的鍵值對
    for (key, value) in dict {
        print(key)
        print(value)
    }

字典的合并

    // 字典的合并
    var dict1 = ["name" : "yz", "age" : 20]
    var dict2 = ["height" : 1.87, "phoneNum" : "+86 110"]
    // 字典不可以相加合并
    for (key, value) in dict1 {
        dict2[key] = value
    }

元組

元組的介紹

  • 元組是Swift中特有的,OC中并沒有相關類型
  • 它是什么呢?
    • 它是一種數據結構,在數學中應用廣泛
    • 類似于數組或者字典
    • 可以用于定義一組數據
    • 組成元組類型的數據可以稱為“元素”

元組的定義

  • 元組的常見寫法

      // 使用元組描述一個人的信息
      ("1001", "張三", 30, 90)
      // 給元素加上元素名稱,之后可以通過元素名稱訪問元素
      (id:"1001", name:"張三", english_score:30, chinese_score:90)
    

元組的簡單使用

  • 用元組來描述一個HTTP的錯誤信息

      // 元組:HTTP錯誤
      // let array = [404, "Not Found"]
      // 寫法一:
      let error = (404, "Not Found")
      print(error.0)
      print(error.1)
      
      // 寫法二:
      let error = (errorCode : 404, errorInfo : "Not Found")
      print(error.errorCode)
      print(error.errorInfo)
      
      // 寫法三:
      let (errorCode, errorIno) = (404, "Not Found")
      print(errorCode)
      print(errorIno)
    

可選類型

可選類型的介紹

  • 注意:
    • 可選類型時swift中較理解的一個知識點
    • 暫時先了解,多利用Xcode的提示來使用
    • 隨著學習的深入,慢慢理解其中的原理和好處
  • 概念:
    • 在OC開發中,如果一個變量暫停不使用,可以賦值為0(基本屬性類型)或者賦值為空(對象類型)
    • 在swift開發中,nil也是一個特殊的類型.因為和真實的類型不匹配是不能賦值的(swift是強類型語言)
    • 但是開發中賦值nil,在所難免.因此推出了可選類型
  • 可選類型的取值:
    • 空值
    • 有值

定義可選類型

  • 定義一個可選類型有兩種寫法

    • 最基本的寫法
    • 語法糖(常用)
      // 錯誤寫法
      // let string : String = nil
      // 正確寫法:
      // 注意:name的類型是一個可選類型,但是該可選類型中可以存放字符串.
      // 寫法一:定義可選類型
      let name : Optional<String> = nil
    
      // 寫法二:定義可選類型,語法糖(常用)
      let name : String? = nil
    

可選類型的使用

    // 演練一:給可選類型賦值
    // 定義可選類型
    var string : Optional<String> = nil
    
    // 給可選類型賦值
    // 錯誤寫法:因此該可選類型中只能存放字符串
    string = 123
    // 正確寫法:
    string = "Hello world"
    
    // 打印結果
    print(string)
    // 結果:Optional("Hello world")\n
    // 因為打印出來的是可選類型,所有會帶Optional


    // 演練二:取出可選類型的值
    // 取出可選類型的真實值(解包)
    print(string!)
    // 結果:Hello world\n
    
    // 注意:如果可選類型為nil,強制取出其中的值(解包),會出錯
    string = nil
    print(string!) // 報錯
    
    // 正確寫法:
    if string != nil {
        print(string!)
    }
    
    // 簡單寫法:為了讓在if語句中可以方便使用string
    // 可選綁定
    if let str = string {
        print(str)
    }

真實應用場景

  • 目的:讓代碼更加嚴謹

      // 通過該方法創建的URL,可能有值,也可能沒有值.
      // 錯誤寫法:如果返回值是nil時,就不能接收了
      // 如果字符串中有中文,則返回值為nil,因此該方法的返回值就是一個可選類型,而使用一個NSURL類型接收是錯誤的
      let url : NSURL = NSURL(string: "www.baidu.com")
      
      // 正確寫法:使用可選類型來接收
      let url : NSURL? = NSURL(string: "www.baidu.com")
      // 該方式利用類型推導
      let url = NSURL(string: "www.baidu.com")
      
      // 通過url來創建request對象:在使用可選類型前要先進行判斷是否有值
      // 該語法成為可選綁定(如果url有值就解包賦值給tempURL,并且執行{})
      if let tempUrl = url {
          let request = NSURLRequest(URL: tempUrl)
      }
    

類型轉化

常見的類型轉化符號

  • is : 用于判斷一個實例是否是某一種類型
  • as : 將實例轉成某一種類型

例子

    // 1.定義數組
    let array : [AnyObject] = [12, "why", 1.88]
    
    // 2.取出數組中的第一個元素
    let objc = array.first!
    
    // 3.判斷第一個元素是否是一個Int類型
    if objc is Int {
        print("是Int類型")
    } else {
        print("非Int類型")
    }
    
    // 4.將objc轉成真正的類型來使用
    // 4.1.as? 將AnyObject轉成可選類型,通過判斷可選類型是否有值,來決定是否轉化成功了
    let age = objc as? Int
    print(age) // 結果:Optional(12)
    
    // 4.2.as! 將AnyObject轉成具體的類型,但是注意:如果不是該類型,那么程序會崩潰
    let age1 = objc as! Int
    print(age1) // 結果:12

函數

函數的介紹

  • 函數相當于OC中的方法

  • 函數的格式如下

      func 函數名(參數列表) -> 返回值類型 {
          代碼塊
          return 返回值
      }
    
  • func是關鍵字,多個參數列表之間可以用逗號(,)分隔,也可以沒有參數

  • 使用箭頭“->”指向返回值類型

  • 如果函數沒有返回值,返回值為Void.并且“-> 返回值類型”部分可以省略

常見的函數類型

    // 1.沒有參數,沒用返回值
    func about() -> Void {
        print("iphone6s plus")
    }
    // 調用函數
    about()
    
    // 簡單寫法
    // 如果沒用返回值,Void可以寫成()
    func about1() -> () {
        print("iphone6s plus")
    }
    // 如果沒有返回值,后面的內容可以都不寫
    func about2() {
        print("iphone6s plus")
    }
    
    about2()
    
    // 2.有參數,沒用返回值
    func callPhone(phoneNum : String) {
        print("打電話給\(phoneNum)")
    }
    callPhone("+86 110")
    
    // 3.沒用參數,有返回值
    func readMessage() -> String {
        return "吃飯了嗎?"
    }
    var str = readMessage()
    print(str)
    
    // 4.有參數,有返回值
    func sum(num1 : Int, num2 : Int) -> Int {
        return num1 + num2
    }
    var result = sum(20, num2: 30)
    print(result)
    
    // 5.有多個返回值的函數
    let nums = [1, 3, 4, 8, 22, 23]
    func getNumCount(nums : [Int]) -> (oddCount : Int, evenCount : Int) {
        var oddCount = 0
        var evenCount = 0
        for num in nums {
            if num % 2 == 0 {
                oddCount++
            } else {
                evenCount++
            }
        }
        return (oddCount, evenCount)
    }
    
    let result = getNumCount(nums)
    result.oddCount
    result.evenCount

函數的使用注意

  • 注意一: 外部參數和內部參數

    • 在函數內部可以看到的參數,就是內部參數
    • 在函數外面可以看到的參數,就是外部參數
    • 默認情況下,從第二個參數開始,參數名稱既是內部參數也是外部參數
    • 如果第一個參數也想要有外部參數,可以設置標簽:在變量名前加標簽即可
    • 如果不想要外部參數,可以在參數名稱前加_
      // num1和a是外部參數的名稱
      func ride(num1 num1 : Int, a num2 : Int, b num3 : Int) -> Int {
          return num1 * num2 * num3
      }
      var result1 = ride(num1: 20, a: 4, b: 5)
    
      // 方法的重載:方法名稱相同,但是參數不同,可以稱之為方法的重載(了解)
      func ride(num1: Int, _ num2 :Int) -> Int {
          return num1 * num2
      }
    
      var result2 = ride(20, 20)
    
  • 注意二: 默認參數

    • 某些情況,如果沒有傳入具體的參數,可以使用默認參數
      func makecoffee(type :String = "卡布奇諾") -> String {
          return "制作一杯\(type)咖啡。"
      }
    
      let coffee1 = makecoffee("拿鐵")
      let coffee2 = makecoffee()
    
  • 注意三: 可變參數

    • swift中函數的參數個數可以變化,它可以接受不確定數量的輸入類型參數
    • 它們必須具有相同的類型
    • 我們可以通過在參數類型名后面加入(...)的方式來指示這是可變參數
      func sum(numbers:Double...) -> Double {
          var total: Double = 0
          for number in numbers {
              total += number
          }
          return total
      }
    
      sum(100.0, 20, 30)
      sum(30, 80)
    
  • 注意四: 引用類型(指針的傳遞)

    • 默認情況下,函數的參數是值傳遞.如果想改變外面的變量,則需要傳遞變量的地址
    • 必須是變量,因為需要在內部改變其值
    • Swift提供的inout關鍵字就可以實現
    • 對比下列兩個函數
      // 函數一:值傳遞
      func swap(var a : Int, var b : Int) {
          let temp = a;
          a = b;
          b = temp
          
          print("a:\(a), b:\(b)")
      }
    
      var a = 10
      var b = 20
      swap(a, b: b)
      print("a:\(a), b:\(b)")
    
      // 函數二:指針的傳遞
      func swap1(inout a : Int, inout b : Int) {
          let temp = a
          a = b
          b = temp
          
          print("a:\(a), b:\(b)")
      }
    
      swap1(&a, b: &b)
      print("a:\(a), b:\(b)")
    
  • 函數的嵌套使用

    • swift中函數可以嵌套使用
    • 即函數中包含函數,但是不推薦該寫法
      // 函數的嵌套
      let value = 55
      func test() {
          func demo() {
              print("demo \(value)")
          }
          
          print("test")
          demo()
      }
    
      demo() // 錯誤
      test() // 執行函數會先打印'test',再打印'demo'
    

函數的類型

  • 函數類型的概念

    • 每個函數都有屬于自己的類型,由函數的參數類型和返回類型組成
      • 這個例子中定義了兩個簡單的數學函數:addTwoInts 和 multiplyTwoInts
      • 這兩個函數都傳入兩個 Int 類型, 返回一個合適的Int值
      • 這兩個函數的類型是 (Int, Int) -> Int
        // 定義兩個函數
        func addTwoInts(a : Int, b : Int) -> Int {
            return a + b
        }
    
        func multiplyTwoInt(a : Int, b : Int) -> Int {
            return a * b
        }
    
  • 抽取兩個函數的類型,并且使用

      // 定義函數的類型
      var mathFunction : (Int, Int) -> Int = addTwoInts
      
      // 使用函數的名稱
      mathFunction(10, 20)
      
      // 給函數的標識符賦值其他值
      mathFunction = multiplyTwoInt
      
      // 使用函數的名稱
      mathFunction(10, 20)
    
  • 函數作為方法的參數

      // 3.將函數的類型作為方法的參數
      func printResult(a : Int, b : Int, calculateMethod : (Int, Int) -> Int) {
          print(calculateMethod(a, b))
      }
      
      printResult(10, b: 20, calculateMethod: addTwoInts)
      printResult(10, b: 20, calculateMethod: multiplyTwoInt)
    
  • 函數作為方法的返回值

      // 1.定義兩個函數
      func stepForward(num : Int) -> Int {
          return num + 1
      }
      
      func stepBackward(num : Int) -> Int {
          return num - 1
      }
      
      // 2.定義一個變量,希望該變量經過計算得到0
      var num = -4
      
      // 3.定義獲取哪一個函數
      func getOprationMethod(num : Int) -> (Int) -> Int {
          return num <= 0 ? stepForward : stepBackward
      }
      
      // 4.for玄幻進行操作
      while num != 0 {
          let oprationMethod = getOprationMethod(num)
          num = oprationMethod(num)
          print(num)
      }
    

枚舉類型

枚舉類型的介紹

  • 概念介紹

    • 枚舉定義了一個通用類型的一組相關的值,使你可以在你的代碼中以一個安全的方式來使用這些值。
    • 在 C/OC 語言中枚舉指定相關名稱為一組整型值
    • Swift 中的枚舉更加靈活,不必給每一個枚舉成員提供一個值.也可以提供一個值是字符串,一個字符,或是一個整型值或浮點值
  • 枚舉類型的語法

    • 使用enum關鍵詞并且把它們的整個定義放在一對大括號內
          enum SomeEnumeration {
          // enumeration definition goes here
          }
    

枚舉類型的定義

  • 以下是指南針四個方向的一個例子

    • case關鍵詞表明新的一行成員值將被定義
    • 不像 C 和 Objective-C 一樣,Swift 的枚舉成員在被創建時不會被賦予一個默認的整數值
    • 在上面的CompassPoints例子中,North,South,East和West不是隱式的等于0,1,2和3
      enum CompassPoint {
        case North
        case South
        case East
        case West
      }
    
  • 定義方式二:多個成員值可以出現在同一行上

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

給枚舉類型賦值

  • 枚舉類型賦值可以是字符串/字符/整型/浮點型

    • 注意如果有給枚舉類型賦值,則必須在枚舉類型后面明確說明具體的類型
      // 1.枚舉類型的賦值
      enum CompassPoint : Int {
        case North = 1
        case South = 2
        case East = 3
        case West = 4
      }
    
      enum Planet {
        case Mercury = 1, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
      }
      
      // 2.枚舉類型的使用
      let p = Planet(rawValue: 3)
      
      if let p = p {
          switch p {
          case .Mercury:
              print("Mercury")
          case .Venus:
              print("Venus")
          case .Earth:
              print("Mercury")
          case .Mars:
              print("Mars")
          case .Jupiter:
              print("Jupiter")
          case .Saturn:
              print("Saturn")
          case .Uranus:
              print("Uranus")
          case .Neptune:
              print("Neptune")
          }
      }
    

結構體

結構體的介紹

  • 概念介紹

    • 結構體(struct)是由一系列具有相同類型或不同類型的數據構成的數據集合
    • 結構體(struct)指的是一種數據結構
    • 結構體是值類型,在方法中傳遞時是值傳遞
  • 結構的定義格式

      struct 結構體名稱 {
          // 屬性和方法
      }
    

為什么需要結構體?

  • 先來看一個例子
    • 我們要計算平面坐標里某個點距點Center的距離是否小于200
    • 算起來很簡單,勾股定理就搞定了:
      • 其中sqrt(n)用來計算n的平方根
      • pow(x, n)用來計算x的n次方
      let centerX : Double = 100
      let centerY : Double = 100

      func inRange(x : Double, y : Double) -> Bool {
          let disX = x - centerX
          let disY = y - centerX
          
          let dis = sqrt(pow(disX, 2) + pow(disY, 2))
          
          return dis < 200
      }

      let x : Double = 100
      let y : Double = 1000

      inRange(x, y: y)
  • 問題

    • 但是這樣有一個不足,當我們需要比較很多個點和Center的距離的時候,這些數字并不能明確告訴我們它們代表的位置的意義,甚至我們都無法知道它們代表一個數字。
    • 如果我們可以像這樣來比較位置:
      • 相比數字,它們看上去就會直觀的多
      • 而這,就是我們需要自定義struct類型最直接的原因
        inRange(location1)
        inRange(myHome)
    
  • 使用結構進行改進

      // 初始化結構體
      struct Location {
          var x : Double
          var y : Double
      }
      
      // 創建結構體
      let location = Location(x: 90, y: 90)
      
      // 優化剛才的方法
      func inRange(location : Location) -> Bool {
          let disX = location.x - centerX
          let disY = location.y - centerY
      
          let dis = sqrt(pow(disX, 2) + pow(disY, 2))
      
          return dis < 200
      }
      
      inRange(location)
    

結構體的增強

  • 擴充構造函數

    • 默認情況下創建Location時使用Location(x: x值, y: y值)
    • 但是為了讓我們在使用結構體時更加的靈活,swift還可以對構造函數進行擴充
    • 擴充的注意點
      • 在擴充的構造函數中必須保證成員變量是有值的
      • 擴充的構造函數會覆蓋原有的構造函數
        struct Location {
            var x : Double
            var y : Double
            
            init(x : Double, y : Double) {
                self.x = x
                self.y = y
            }
            
            init(xyString : String) {
                let strs = xyString.componentsSeparatedByString(",")
                x = Double(strs.first!)!
                y = Double(strs.last!)!
            }
        }
    
        let location = Location(x: 100, y: 100)
        let location1 = Location(xyString: "100,100")
    
  • 為結構體擴充方法

    • 為了讓結構體使用更加靈活,swift的結構體中可以擴充方法
    • 例子:為了Location結構體擴充兩個方法
      • 向水平方向移動的方法
      • 向垂直方向移動的方法
        struct Location {
            var x : Double
            var y : Double
            
            init(x : Double, y : Double) {
                self.x = x
                self.y = y
            }
            
            init(xyString : String) {
                let strs = xyString.componentsSeparatedByString(",")
                x = Double(strs.first!)!
                y = Double(strs.last!)!
            }
            
            mutating func moveH(x : Double) {
                self.x += x
            }
            
            mutating func moveV(y : Double) {
                self.y += y
            }
        }
    
  • 注意:

    • 如果我們使用的Location不是自己定義的,但是我們仍舊希望在自己的項目里擴展Location的操作
    • Swift也能幫我們達成,這個機制,叫做extension
      extension Location {
          mutating func moveH(x : Double) {
              self.x += x
          }
          
          mutating func moveV(y : Double) {
              self.y += y
          }
      }
    

Swift中類的使用

主要內容

  • 類的介紹和定義
  • 類的屬性
  • 類的構造函數

一. 類的介紹和定義

  • Swift也是一門面向對象開發的語言

  • 面向對象的基礎是類,類產生了對象

  • 在Swift中如何定義類呢?

    • class是Swift中的關鍵字,用于定義類
      class 類名 : SuperClass {
          // 定義屬性和方法
      }
    
  • 注意:

    • 定義的類,可以沒有父類.那么該類是rootClass
    • 通常情況下,定義類時.繼承自NSObject(非OC的NSObject)

二. 如何定義類的屬性

類的屬性介紹
  • Swift中類的屬性有多種
    • 存儲屬性:存儲實例的常量和變量
    • 計算屬性:通過某種方式計算出來的屬性
    • 類屬性:與整個類自身相關的屬性
存儲屬性
  • 存儲屬性是最簡單的屬性,它作為類實例的一部分,用于存儲常量和變量

  • 可以給存儲屬性提供一個默認值,也可以在初始化方法中對其進行初始化

  • 下面是存儲屬性的寫法

    • age和name都是存儲屬性,用來記錄該學生的年齡和姓名
    • chineseScore和mathScore也是存儲屬性,用來記錄該學生的語文分數和數學分數
      class Student : NSObject {
          // 定義屬性
          // 存儲屬性
          var age : Int = 0
          var name : String?
          
          var chineseScore : Double = 0.0
          var mathScore : Double = 0.0
      }
    
      // 創建學生對象
      let stu = Student()
    
      // 給存儲屬性賦值
      stu.age = 10
      stu.name = "why"
    
      stu.chineseScore = 89.0
      stu.mathScore = 98.0
    
計算屬性
  • 計算屬性并不存儲實際的值,而是提供一個getter和一個可選的setter來間接獲取和設置其它屬性

  • 計算屬性一般只提供getter方法

  • 如果只提供getter,而不提供setter,則該計算屬性為只讀屬性,并且可以省略get{}

  • 下面是計算屬性的寫法

    • averageScore是計算屬性,通過chineseScore和mathScore計算而來的屬性
    • 在setter方法中有一個newValue變量,是系統指定分配的
      class Student : NSObject {
          // 定義屬性
          // 存儲屬性
          var age : Int = 0
          var name : String?
          
          var chineseScore : Double = 0.0
          var mathScore : Double = 0.0
          
          // 計算屬性
          var averageScore : Double {
              get {
                  return (chineseScore + mathScore) / 2
              }
          
              // 沒有意義,因為之后獲取值時依然是計算得到的
              // newValue是系統分配的變量名,內部存儲著新值
              set {
                  self.averageScore = newValue
              }
          }
      }
    
      // 獲取計算屬性的值
      print(stu.averageScore)
    
類屬性
  • 類屬性是與類相關聯的,而不是與類的實例相關聯

  • 所有的類和實例都共有一份類屬性.因此在某一處修改之后,該類屬性就會被修改

  • 類屬性的設置和修改,需要通過類來完成

  • 下面是類屬性的寫法

    • 類屬性使用static來修飾
    • courseCount是類屬性,用來記錄學生有多少門課程
      class Student : NSObject {
          // 定義屬性
          // 存儲屬性
          var age : Int = 0
          var name : String?
          
          var chineseScore : Double = 0.0
          var mathScore : Double = 0.0
          
          // 計算屬性
          var averageScore : Double {
              get {
                  return (chineseScore + mathScore) / 2
              }
          
              // 沒有意義.newValue是系統分配的變量名,內部存儲著新值
              set {
                  self.averageScore = newValue
              }
          }
          
          // 類屬性
          static var corseCount : Int = 0
      }
    
      // 設置類屬性的值
      Student.corseCount = 3
      // 取出類屬性的值
      print(Student.corseCount)
    
監聽屬性的改變
  • 在OC中我們可以重寫set方法來監聽屬性的改變

  • Swift中可以通過屬性觀察者來監聽和響應屬性值的變化

  • 通常是監聽存儲屬性和類屬性的改變.(對于計算屬性,我們不需要定義屬性觀察者,因為我們可以在計算屬性的setter中直接觀察并響應這種值的變化)

  • 我們通過設置以下觀察方法來定義觀察者

    • willSet:在屬性值被存儲之前設置。此時新屬性值作為一個常量參數被傳入。該參數名默認為newValue,我們可以自己定義該參數名
    • didSet:在新屬性值被存儲后立即調用。與willSet相同,此時傳入的是屬性的舊值,默認參數名為oldValue
    • willSet與didSet只有在屬性第一次被設置時才會調用,在初始化時,不會去調用這些監聽方法
  • 監聽的方式如下:

    • 監聽age和name的變化
      class Person : NSObject {
          var name : String? {
              // 可以給newValue自定義名稱
              willSet (new){ // 屬性即將改變,還未改變時會調用的方法
                  // 在該方法中有一個默認的系統屬性newValue,用于存儲新值
                  print(name)
                  print(new)
              }
              // 可以給oldValue自定義名稱
              didSet (old) { // 屬性值已經改變了,會調用的方法
                  // 在該方法中有一個默認的系統屬性oldValue,用于存儲舊值
                  print(name)
                  print(old)
              }
          }
          var age : Int = 0
          var height : Double = 0.0
      }
    
      let p : Person = Person()
    
      // 在賦值時,監聽該屬性的改變
      // 在OC中是通過重寫set方法
      // 在swift中,可以給屬性添加監聽器
      p.name = "why"
    
      //p.name = "yz"
    

類的構造函數

構造函數的介紹

  • 構造函數類似于OC中的初始化方法:init方法
  • 默認情況下載創建一個類時,必然會調用一個構造函數
  • 即便是沒有編寫任何構造函數,編譯器也會提供一個默認的構造函數。
  • 如果是繼承自NSObject,可以對父類的構造函數進行重寫

構造函數的基本使用

構造函數的基本使用
  • 類的屬性必須有值

  • 如果不是在定義時初始化值,可以在構造函數中賦值

      class Person: NSObject {
          var name : String
          var age : Int
      
          // 重寫了NSObject(父類)的構造方法
          override init() {
              name = ""
              age = 0
          }
      }
      
      // 創建一個Person對象
      let p = Person()
    
初始化時給屬性賦值
  • 很多時候,我們在創建一個對象時就會給屬性賦值

  • 可以自定義構造函數

  • 注意:如果自定義了構造函數,會覆蓋init()方法.即不在有默認的構造函數

      class Person: NSObject {
          var name : String
          var age : Int
      
          // 自定義構造函數,會覆蓋init()函數
          init(name : String, age : Int) {
              self.name = name
              self.age = age
          }
      }
      
      // 創建一個Person對象
      let p = Person(name: "why", age: 18)
    
字典轉模型(初始化時傳入字典)
  • 真實創建對象時,更多的是將字典轉成模型

  • 注意:

    • 去字典中取出的是NSObject,任意類型.
    • 可以通過as!轉成需要的類型,再賦值(不可以直接賦值)
      class Person: NSObject {
          var name : String
          var age : Int
          
          // 自定義構造函數,會覆蓋init()函數
          init(dict : [String : NSObject]) {
              name = dict["name"] as! String
              age = dict["age"] as! Int
          }
      }
    
      // 創建一個Person對象
      let dict = ["name" : "why", "age" : 18]
      let p = Person(dict: dict)
    
字典轉模型(利用KVC轉化)
  • 利用KVC字典轉模型會更加方便

  • 注意:

    • KVC并不能保證會給所有的屬性賦值
    • 因此屬性需要有默認值
      • 基本數據類型默認值設置為0
      • 對象或者結構體類型定義為可選類型即可(可選類型沒有賦值前為nil)
        class Person: NSObject {
            // 結構體或者類的類型,必須是可選類型.因為不能保證一定會賦值
            var name : String?
            
            // 基本數據類型不能是可選類型,否則KVC無法轉化
            var age : Int = 0
            
            // 自定義構造函數,會覆蓋init()函數
            init(dict : [String : NSObject]) {
                // 必須先初始化對象
                super.init()
            
                // 調用對象的KVC方法字典轉模型
                setValuesForKeysWithDictionary(dict)
            }
        }
    
        // 創建一個Person對象
        let dict = ["name" : "why", "age" : 18]
        let p = Person(dict: dict)
    

類的析構函數

析構函數

  • Swift 會自動釋放不再需要的實例以釋放資源

    • Swift 通過自動引用計數(ARC)處理實例的內存管理
    • 當引用計數為0時,系統會自動調用析構函數(不可以手動調用)
    • 通常在析構函數中釋放一些資源(如移除通知等操作)
  • 析構函數的寫法

      deinit {
          // 執行析構過程
      }
    

示例練習

    class Person {
        var name : String
        var age : Int
    
        init(name : String, age : Int) {
            self.name = name
            self.age = age
        }
    
        deinit {
            print("Person-deinit")
        }
    }
    
    var p : Person? = Person(name: "why", age: 18)
    p = nil

自動引用計數

工作機制

  • Swift和OC一樣,采用自動引用計數來管理內容
    • 當有一個強引用指向某一個動向時,該對象的引用計數會自動+1
    • 當該強引用消失時,引用計數會自動-1
    • 當引用計數為0時,該對象會被銷毀

循環引用

  • 在通常情況下,ARC是會自動幫助我們管理內存的

  • 但是在開發中我們經常會出現循環引用的問題,比如下面的示例

    • Student對Book對象有一個強引用
    • 而Book對Student有一個強引用
    • 在兩個對象都指向nil時,依然不會被銷毀,就形成了循環引用
      // 1.創建類
      class Student {
          var book : Book?
    
          deinit {
              print("Student -- deinit")
          }
      }
    
      class Book {
          var owner : Student?
    
          deinit {
              print("Book -- deinit")
          }
      }
    
      // 2.創建對象
      var stu : Student? = Student()
      var book : Book? = Book()
    
      // 3.相互引用
      stu?.book = book
      book?.owner = stu
    
      // 4.對象置nil
      stu = nil
      book = nil
    
  • 解決方案

    • swift提供了兩種解決方案
      • weak : 和OC中的__weak一樣是一個弱引用.當指向的對象銷毀時,會自動將指針指向nil
      • unowned : 和OC中的__unsafe_unretained.當對象銷毀時依然指向原來的位置(容易引起野指針)
      // 1.創建類
      class Student {
          weak var book : Book?
          // unowned var book : Book = Book()
          
          deinit {
              print("Student -- deinit")
          }
      }
    
      class Book {
          var owner : Student?
    
          deinit {
              print("Book -- deinit")
          }
      }
    
      // 2.創建對象
      var stu : Student? = Student()
      var book : Book? = Book()
    
      // 3.相互引用
      stu?.book = book!
      book?.owner = stu
    
      // 4.對象置nil
      stu = nil
      book = nil
    

可選鏈

可選連的概念

  • 它的可選性體現于請求或調用的目標當前可能為空(nil)
    • 如果可選的目標有值,那么調用就會成功;
    • 如果選擇的目標為空(nil),則這種調用將返回空(nil)
  • 多次調用被鏈接在一起形成一個鏈,如果任何一個節點為空(nil)將導致整個鏈失效。
  • 可選鏈的使用
    • 在可選類型后面放一個問號,可以定義一個可選鏈。
    • 這一點很像在可選值后面放一個嘆號來強制拆得其封包內的值
      • 它們的主要的區別在于當可選值為空時可選鏈即刻失敗
      • 然而一般的強制解析將會引發運行時錯誤。
    • 因為可選鏈的結果可能為nil,可能有值.因此它的返回值是一個可選類型.
      • 可以通過判斷返回是否有值來判斷是否調用成功
      • 有值,說明調用成功
      • 為nil,說明調用失敗

可選鏈的示例

  • 從可選鏈中取值

    • 示例描述: 人(Person)有一個狗(Dog),狗(Dog)有一個玩具(Toy),玩具有價格(price)
    • 使用代碼描述上述信息
      // 1.定義類
      class Person {
          var name : String
          var dog : Dog?
          
          init(name : String) {
              self.name = name
          }
      }
    
      class Dog {
          var color : UIColor
          var toy : Toy?
          
          init(color : UIColor) {
              self.color = color
          }
          
          func runing() {
              print("跑起來")
          }
      }
    
      class Toy {
          var price : Double = 0.0
      }
    
      // 2.創建對象,并且設置對象之間的關系
      // 2.1.創建對象
      let person = Person(name: "小明")
      let dog = Dog(color: UIColor.yellowColor())
      let toy = Toy()
      toy.price = 100.0
    
      // 2.2.設置對象之間的關系
      person.dog = dog
      dog.toy = toy
    
  • 需求:獲取小明的大黃寵物的玩具價格取出的值為可選類型,因為可選鏈中有一個可選類型為nil,則返回nil因此結果可能有值,可能為nil.因此是一個可選類型

      let price = person.dog?.toy?.price
      print(price) // Optional(100.0)\n
    
  • 需求:給小明的大黃一個新的玩具

    • 相當于給可選類型賦值
      person.dog?.toy = Toy()
    
  • 需求:讓小明的狗跑起來

    • 如果可選類型有值,則會執行該方法
    • 如果可選類型為nil,則該方法不會執行
      person.dog?.runing()
    

協議

協議的格式

  • 協議的定義方式與類,結構體,枚舉的定義都非常相似

      protocol SomeProtocol {
          // 協議方法
      }
    
  • 遵守協議的格式

      class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
        // 類的內容
        // 實現協議中的方法
      }
    

協議的基本使用

  • 定義協議和遵守協議

      // 1.定義協議
      protocol SportProtocol {
          func playBasketball()
          func playFootball()
      }
      
      // 2.遵守協議
      // 注意:默認情況下在swift中所有的協議方法都是必須實現的,如果不實現,則編譯器會報錯
      class Person : SportProtocol {
          var name : String?
          var age : Int = 0
      
          // 實現協議中的方法
          func playBasketball() {
              print("人在打籃球")
          }
      
          func playFootball() {
              print("人在踢足球")
          }
      }
    
  • 協議之間的繼承

      protocol CrazySportProtocol {
          func jumping()
      }
      
      protocol SportProtocol : CrazySportProtocol {
          func playBasketball()
          func playFootball()
      }
    

代理設計模式

  • 協議繼承用于代理設計模式

      protocol BuyTicketProtocol {
          func buyTicket()
      }
      
      class Person {
          // 1.定義協議屬性
          var delegate : BuyTicketProtocol
      
          // 2.自定義構造函數
          init (delegate : BuyTicketProtocol) {
              self.delegate = delegate
          }
      
          // 3.行為
          func goToBeijing() {
              delegate.buyTicket()
          }
      }
    
      class HuangNiu: BuyTicketProtocol {
          func buyTicket() {
              print("買了一張火車票")
          }
      }
      
      let p = Person(delegate: HuangNiu())
      p.goToBeijing()
    ?```
    

協議中方法的可選

    // 1.定義協議
    @objc
    protocol SportProtocol {
        func playBasketball()
    
        optional func playFootball()
    }
    
    // 2.遵守協議
    class Person : SportProtocol {
        var name : String?
        var age : Int = 0
    
        // 實現協議中的方法
        @objc func playBasketball() {
            print("人在打籃球")
        }
    }

閉包

閉包的介紹

  • 閉包和OC中的block非常相似
    • OC中的block是匿名的函數
    • Swift中的閉包是一個特殊的函數
    • block和閉包都經常用于回調
  • 注意:閉包和block一樣,第一次使用時可能不習慣它的語法,可以先按照使用簡單的閉包,隨著學習的深入,慢慢掌握其靈活的運用方法.

閉包的使用

block的用法回顧
  • 定義網絡請求的類

      @interface HttpTool : NSObject
      - (void)loadRequest:(void (^)())callBackBlock;
      @end
      
      @implementation HttpTool
      - (void)loadRequest:(void (^)())callBackBlock
      {
          dispatch_async(dispatch_get_global_queue(0, 0), ^{
              NSLog(@"加載網絡數據:%@", [NSThread currentThread]);
      
              dispatch_async(dispatch_get_main_queue(), ^{
                  callBackBlock();
              });
          });
      }
      @end
    
  • 進行網絡請求,請求到數據后利用block進行回調

      - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
      {
          [self.httpTool loadRequest:^{
              NSLog(@"主線程中,將數據回調.%@", [NSThread currentThread]);
          }];
      }
    
  • block寫法總結:

    block的寫法:
    類型:
    返回值(^block的名稱)(block的參數)
    
    值:
    ^(參數列表) {
        // 執行的代碼
    };
    
使用閉包代替block
  • 定義網絡請求的類

      class HttpTool: NSObject {
    
          func loadRequest(callBack : ()->()){
              dispatch_async(dispatch_get_global_queue(0, 0)) { () -> Void in
                  print("加載數據", [NSThread.currentThread()])
      
                   dispatch_async(dispatch_get_main_queue(), { () -> Void in
                      callBack()
                   })
              }
          }
      }
    
  • 進行網絡請求,請求到數據后利用閉包進行回調

        override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
            // 網絡請求
            httpTool.loadRequest ({ () -> () in
                print("回到主線程", NSThread.currentThread());
            })
        }
    
  • 閉包寫法總結:

      閉包的寫法:
          類型:(形參列表)->(返回值)
          技巧:初學者定義閉包類型,直接寫()->().再填充參數和返回值
      
          值:
          {
              (形參) -> 返回值類型 in
              // 執行代碼
          }
    
閉包的簡寫
  • 如果閉包沒有參數,沒有返回值.in和in之前的內容可以省略

        httpTool.loadRequest({
            print("回到主線程", NSThread.currentThread());
        })
    
  • 尾隨閉包寫法:

    • 如果閉包是函數的最后一個參數,則可以將閉包寫在()后面
    • 如果函數只有一個參數,并且這個參數是閉包,那么()可以不寫
        httpTool.loadRequest() {
            print("回到主線程", NSThread.currentThread());
        }
    
        // 開發中建議該寫法
        httpTool.loadRequest {
            print("回到主線程", NSThread.currentThread());
        }
    

閉包的循環引用

  • 如果在HttpTool中有對閉包進行強引用,則會形成循環引用

  • 補充:在Swift中檢測一個對象是否銷毀,可以實現對象的deinit函數

        // 析構函數(相當于OC中dealloc方法)
        deinit {
            print("ViewController----deinit")
        }
    
  • 循環引用的(實現)

    • 該實現是為了產生循環引用,而產生的循環引用
      class HttpTool: NSObject {
    
          // 定義屬性,來強引用傳入的閉包
          var callBack : (()->())?
          
          func loadRequest(callBack : ()->()){
              dispatch_async(dispatch_get_global_queue(0, 0)) { () -> Void in
                  print("加載數據", [NSThread.currentThread()])
          
                   dispatch_async(dispatch_get_main_queue(), { () -> Void in
                      callBack()
                   })
              }
          
              self.callBack = callBack
          }
      }
    
  • swift中解決循環引用的方式

  • 方案一:

    • 使用weak,對當前控制器使用弱引用
    • 但是因為self可能有值也可能沒有值,因此weakSelf是一個可選類型,在真正使用時可以對其強制解包(該處強制解包沒有問題,因為控制器一定存在,否則無法調用所在函數)
        // 解決方案一:
        weak var weakSelf = self
        httpTool.loadData {
            print("加載數據完成,更新界面:", NSThread.currentThread())
            weakSelf!.view.backgroundColor = UIColor.redColor()
        }
    
  • 方案二:

    • 和方案一類型,只是書寫方式更加簡單
    • 可以寫在閉包中,并且在閉包中用到的self都是弱引用
        httpTool.loadData {[weak self] () -> () in
            print("加載數據完成,更新界面:", NSThread.currentThread())
            self!.view.backgroundColor = UIColor.redColor()
        }
    
  • 方案三:(常用)

    • 使用關鍵字unowned
    • 從行為上來說 unowned 更像OC中的 unsafe_unretained
    • unowned 表示:即使它原來引用的對象被釋放了,仍然會保持對被已經釋放了的對象的一個 "無效的" 引用,它不能是 Optional 值,也不會被指向 nil
      httpTool.loadData {[unowned self] () -> () in
              print("加載數據完成,更新界面:", NSThread.currentThread())
              self.view.backgroundColor = UIColor.redColor()
          }
    

懶加載

懶加載的介紹

  • swift中也有懶加載的方式
    • (蘋果的設計思想:希望所有的對象在使用時才真正加載到內存中)
  • 和OC不同的是swift有專門的關鍵字來實現懶加載
  • lazy關鍵字可以用于定義某一個屬性懶加載

懶加載的使用

  • 格式

      lazy var 變量: 類型 = { 創建變量代碼 }()
    
  • 懶加載的使用

        // 懶加載的本質是,在第一次使用的時候執行閉包,將閉包的返回值賦值給屬性
        // lazy的作用是只會賦值一次
        lazy var array : [String] = {
            () -> [String] in
            return ["why", "lmj", "lnj"]
        }()
    

常見注釋

單行注釋

  • Swift 中的注釋與C 語言的注釋非常相似。

  • 單行注釋以雙正斜杠(//)作為起始標記

      // 注釋內容
    

多行注釋

  • 其起始標記為單個正斜杠后跟隨一個星號(/*)

  • 終止標記為一個星號后跟隨單個正斜杠(*/)

      /* 這是一個,
      多行注釋 */
    
  • 和與 C 語言多行注釋不同,Swift 的多行注釋可以嵌套在其它的多行注釋之中

      /* 這是第一個多行注釋的開頭
      /* 這是第二個被嵌套的多行注釋 */
      這是第一個多行注釋的結尾 */
    

文檔注釋

  • Swift中添加文檔注釋較為簡單

  • 使用(///)可以為方法或者屬性添加文檔注釋

      /// 打電話給某人
      func callPhone(phoneNum : String) {
          print("打電話給\(phoneNum)")
      }
    

分組注釋

  • swift中不可以再使用 #pragma mark -

  • 如果打算對代碼進行分組可以使用 // MARK:-方式

      // MARK:-
    

訪問權限

swift中的訪問權限

  • Swift 中的訪問控制模型基于模塊和源文件這兩個概念

    - internal : 在本模塊中都可以進行訪問
    - private : 在當前源文件中可以訪問
    - public : 在其他模塊中可以訪問
    

異常處理

異常的介紹

  • 只要我們在編程,就一定要面對錯誤處理的問題。
  • Swift在設計的時候就盡可能讓我們明確感知錯誤,明確處理錯誤
    • 比如:只有使用Optional才能處理空值;
  • 如何描述一個錯誤?
    • 在Swift里,任何一個遵從ErrorType protocol的類型,都可以用于描述錯誤。
    • ErrorType是一個空的protocol,它唯一的功能,就是告訴Swift編譯器,某個類型用來表示一個錯誤。
    • 通常,我們使用一個enum來定義各種錯誤的可能性

異常的示例

  • 假如我們想要讀取一個文件中的內容,按照OC的邏輯我們可以這樣來模擬

    • 當我們調用方法獲取結果為nil時,你并不能確定到底參數了什么錯誤得到了nil
      func readFileContent(filePath : String) -> String? {
          // 1.filePath為""
          if filePath == "" {
              return nil
          }
          
          // 2.filepath有值,但是沒有對應的文件
          if filePath != "/User/Desktop/123.plist" {
              return nil
          }
          
          // 3.取出其中的內容
          return "123"
      }
    
      readFileContent("abc")
    
  • 使用異常對上述方法進行改進

      // 1.定義異常
      enum FileReadError : ErrorType {
          case FileISNull
          case FileNotFound
      }
      
      // 2.改進方法,讓方法拋出異常
      func readFileContent(filePath : String) throws -> String {
          // 1.filePath為""
          if filePath == "" {
      
              throw FileReadError.FileISNull
          }
      
          // 2.filepath有值,但是沒有對應的文件
          if filePath != "/User/Desktop/123.plist" {
      
              throw FileReadError.FileISNull
          }
      
          // 3.取出其中的內容
          return "123"
      }
    
  • 處理異常有三種方式

      // 3.異常的處理三種方式
      // 3.1.try方式,需要手動處理異常
      do {
          let result = try readFileContent("abc")
      } catch {
          print(error)
      }
      
      // 3.2.try?方式,不處理異常,如果出現了異常,則返回一個nil.沒有異常,則返回對應的值
      // 最終返回結果為一個可選類型
      let result = try? readFileContent("abc")
      
      // 3.3.try!方法,告訴系統該方法沒有異常.
      // 注意:如果出現了異常,則程序會崩潰
      try! readFileContent("abc")
    

Swift和OC相互調?用

Swift調?用OC

  1. 創建橋接?文件—> .h
  2. 在橋接?文件中導?入頭?文件
  3. 配置橋接?文件: 項目->buildSettings —> bridging —> 配置

OC調?用Swift

  • 項?目名字不能隨便起
  • Swift中的類/屬性/?方法必須使?用public修飾
  • 導?項目名稱-Swift.h
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,197評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,415評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,104評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,884評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,647評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,130評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,208評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,366評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,887評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,737評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,939評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,478評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,174評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,586評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,827評論 1 283
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,608評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,914評論 2 372

推薦閱讀更多精彩內容