Swift關鍵字解析

? !

?

Swift是一個強類型語言待锈,它希望在編譯器做更多的安全檢查,所以引入了類型判斷贫悄。而在類型判斷上如果要做到足夠安全瑞驱,避免空指針調用是一個最基本的要求。于是窄坦,Optional這種類型出現了唤反。

Optional在Swift中其實是一個枚舉類型,里面有None和Some兩種類型鸭津,其實所謂的nil就是Optional.None,非nil就是Optional.Some,然后會通過Some(T)包裝(wrap)原始值拴袭,這也是為什么在使用Optional的時候要拆包(從enum里取出原始值)的原因

public enum Optional<Wrapped>: ExpressibleNilLiteral {
  case none
  case some(Wrapped)
  
  public init(_ some:Wrapped)
}

聲明Optional只需要在類型后面緊跟一個 ? 即可

let name: String?
//等同于
let name: Optional<String>

上面這個 Optional 的聲明曙博,意思不是“我聲明了一個Optional的String值”拥刻,而是“我聲明了一個Optional類型值,它可能包含一個String值,也可能什么都不包含父泳,也就是說實際上我們聲明的是Optional類型般哼,而不是一個String類型,這一點需搞清”

let number: Int? = Optional.some(42)
let noNumber: Int? = Optional.none
print(noNumber == nil)
// Prints "true"

使用Optional的值的時候需要在具體的操作惠窄,如調用方法蒸眠,屬性,下標索引等前面加上一個杆融?楞卡,如果是nil值,也就是Optional.None,會跳轉過后面的操作不執(zhí)行。如果有值蒋腮,也就是Optional.Some,可能就會拆包(unwrap),然后對拆包后的值執(zhí)行后面的操作淘捡,來保證執(zhí)行這個操作的安全性。

用if let 拆包

var errorCodeString: String?
errorCodeString = "404"
if let theError = errorCodeString, let errorCodeInterger = Int(theError, errorCodeInterger == 404 ) {
  print("\(theError):\(errorCodeInterger)")
}

!

! 表示 隱式可選類型(implicitly upwrapped optionals),與可選類型 ? 類似池摧,只是由一個區(qū)別焦除,它們不需要解包,因為用 ! 代表你確認是有值的作彤,不過如果沒值為nil膘魄,后面的操作就會報錯crash.

var myLabel: UILable!
//相當于下面寫法的語法糖
var myLabel:ImplicityUnwrappedOptional<UILabel>

大多數時候好最好用 ? ,只有在你知道可選類型實例不會為nil或者一旦可選類型實例是nil就崩潰時才使用 ! 操作符

as? as!

Objective-C有多態(tài),某些情況下竭讳,我們會將子類實例賦值給父類指針创葡,到用的時候,再強轉回子類
Swfit這種情況是用 as? as!來做绢慢,如果要判斷某個實例的類型就用 is
A as? B 的意思是 如果實例A是B類型或者是B類型的子類灿渴,就將A的類型轉化成B類型,并返回轉換后的實例(是可選類型B?)如果不是呐芥,表達式返回nil,程序繼續(xù)運行奋岁。如果用 as! 思瘟,說明我們肯定A是類型B或者B的子類,那么強制轉換闻伶,如果不是滨攻,那么會crash.

??

?? 表達式就是三目運算符的簡單版。

let name = "huang"
let nickName = name ?? "haha" //如果name為nil蓝翰,那么表達式返回值為 ?? 之后的值光绕,如果name有值,那么表達式返回name本身的值畜份。

inout

出于某種原因诞帐,函數有時候需要修改實參的值。 in-out參數(in-out parameter)能讓函數影響函數體以外的變量爆雹。有兩個注意事項:首先停蕉,in-out參數不能有默認值,其次钙态,變長參數不能標記為inout.

var error = "The request failed"
func appendErrorCode(_ code: Int, toErrorString errorString: inout String){
  if code == 400 {
    errorString += " bad request"
  }
}
appendErrorCode(400, toErrorString: &error)
print(error)

inout加在String前面表示這個函數期??一個特??的String:它需要一個inout的String慧起。 調用這個函數時,????給inout??數的變量需要在前面加上&册倒。這表示函數會修改這個變量蚓挤。 在這里,errorString被改為The request failed: bad request

mutating

在Swift中,包含三種類型:struct(結構體) enum(枚舉) class(類)
其中struct enum是值類型灿意,class是引用類型
但與Objective-C不同的是估灿,struct和enumd也可以擁有方法,其中方法可以為實例方法脾歧,也可以為類方法甲捏。

雖然結構體和枚可以定義自己的方法,但是默認情況下鞭执,實例方法中是不可以修改值類型的屬性司顿。

覺個簡單的例子,假如定義一個點結構體兄纺,該結構體有一個修改點位置的實例方法:

struct Point {
  var x = 0 , y = 0
  
  func moveXBy(x:Int, yBy y: Int){
    self.x += x
    //Cannot invoke '+=' with an argument list of type '(Int, Int)'
    self.y += y
    //Cannot invoke '+=' with an argument list of type '(Int, Int)'
  }
}

編譯器拋出錯誤大溜,說明確實不能在實例方法中修改屬性值

為了能夠在實例方法中修改屬性的值,可以在方法定義前添加關鍵字 mutating

stuct Point {
  var x = 0, y = 0
  
  mutating func moveXBy(x:Int,)
}
var p = Point(x: 5, y: 5)
p.moveBy(3,yBy: 3)

另外估脆,在值類型的實例方法中钦奋,也可以直接修改self屬性值

enum TriStateSwitch
   case Off,Low,High
   mutating func next {
    switch self {
    case Off:
      self = Low
    case Low:
      self = High
    case High:
      self = Off
    }
   }

typealias

  1. typealias 類型別名, 為已經存在的類型重新定義名字的疙赠,通過命名付材,可以是代碼變得更加清晰
extension Double {
    var km: Double { return self * 1000.0}
    var m: Double { return self }
    var cm: Double { return self / 100 }
    var ft : Double { return self / 3.28084}
}
let runningDistance: Double = 3.54.km
runningDistance

給Double取一個別名,讓代碼可讀性更強

typealias Length = Double

extension Double {
    var km: Length { return self * 1000.0}
    var m: Length { return self }
    var cm: Length { return self / 100 }
    var ft : Length { return self / 3.28084} 
}
let runningDistance: Length = 3.54.km
runningDistance
  1. typealias 定義個閉包名字
typealias Success = (_ data: String) -> Void
typealias Failure = (_ error: String) -> Void

func request(_ url: String, success: Success, failue: Failue) {
    // do request
}
  1. typealias 與 協(xié)議
    另外一種使用場景是某個類型同時實現多個協(xié)議的組合時圃阳,我們可以用 & 符號鏈接幾個協(xié)議厌衔,然后給他們一個新的復合上下文的名字,來增強代碼的可讀性捍岳。
protocol Cat { }
protocol Dog { }

typealias Pat = Cat & Dog

associatedtype

associatedtype 其實與 typealias 一樣也是取別名富寿,但是它用在protocol里面, 并與在實現protocol的類型里的
typealias 配合使用

來看下面的例子

protocol WeightCalculable {
    associatedtype WeightType
    var weight: WeightType { get } //這里的weight的類型有可能是Double ,有可能是Int或其他,這里用associatedtype 取個 WeightType 代替
}

class iPhone7 : WeightCalculable {
     typealias WeightType = Double  //用typealias 為WeightType 指定具體類型
     var weight: WeightType {
          return 0.114
      }
} 

class Ship: WeightCalculable {
    typealias WeightType = Int
    let weight: WeightType
    init(weight: Int){
        self.weight = weight
    }
}

extension Int {
    typealias Weight = Int
    var t: Weight { return 1_000*self}
}

//let titanic = Ship(weight: 46_328_000)
let titanic = Ship(weight: 46_328.t)

private fileprivate internal public open

這五種是Swift對于訪問權限的控制锣夹。按從低到高排序如下

pirvate < fileprivate < internal < public open

  1. private:訪問級別所修飾的屬性或者方法只能在當前類里訪問页徐。(注意:Swift4中,extension里也可以訪問private屬性)

  2. fileprivate修飾的屬性火方法可以在當前的Swift源文件里訪問

  3. internal(默認訪問級別银萍,internal修飾符可寫可不寫)

  • internal訪問級別所修飾的屬性或方法在源代碼所在的整個模塊都可以訪問
  • 如果是框架或者是庫代碼变勇,則在整個框架內部都可以訪問,框架由外部代碼所引用時贴唇,則不可以訪問
  • 如果是 App 代碼贰锁,也是在整個 App 代碼內部可以訪問。
  1. public 可以被任何人訪問滤蝠。但在其他 <mark>module</mark> 中不可以被 override 和 繼承豌熄,而在 <mark>module</mark> 內可以被 override 和 繼承。
  2. open 可以被任何人使用物咳,包括 <mark>override</mark>和繼承

noescape escaping

在Swift3之后,所有的閉包都默認為非逃逸閉包(@noescape)锣险,如果是逃逸閉包蹄皱,就用@escaping表示出來。
簡單介紹就是如果這個閉包是在這個函數結束之前內被調用芯肤,就是非逃逸的即noescape巷折,如果這個閉包是在函數函數執(zhí)行完后才被調用,調用的地方超過了這函數的范圍崖咨,所以叫逃逸閉包锻拘。

舉個例子,我們常用的masonry或者snapkit的添加約束的方法就是非逃逸的击蹲。因為這閉包馬上就執(zhí)行了署拟。

public func snap_makeConstraints(file: String = #file, line : UInt = #line,  closure:(make: ConstraintMaker) -> Void) -> Void {
  ConstraintMaker.makeConstraints(view: self, file: file, line: line, closure: closure)
}

網絡請求結束后的回調的閉包則是逃逸的,因為發(fā)起請求后過了一段時間后這個閉包才執(zhí)行歌豺。比如這個Alamofire里的處理返回json的completionHandler閉包推穷,就是逃逸的。

public func responseJSON(
  queue queue: dispatch_queue_t? = nil,
  options: NSJSONReadingOptions = .AllowFragments,
  completionHandler: @escaping Response<AnyObject, NSError> -> Void)
  -> Self 
{
    return response (
          queue: queue,
          responseSerializer: Request.JSONResponseSerializer(options: options),
          completionHandler: comletionHandler
   )
}

guard

guard通常后面接一個表達式形成一個語句类咧,跟if/else語句一樣馒铃,guard語句會根據某個表達式返回的布爾值結果來執(zhí)行代碼;但不同之處是痕惋,如果某些條件沒有滿足区宇,可以用guard語句來提前退出函數

resource.request().onComplete {  [weak self] response in
    guard let strongSelf = self else {
        return
    }
    let model = strongSelf.updateModel(response)
    strongSelf.updateUI(model)
}

guard let 大多數情況下都可以代替 if let,以增加代碼閱讀性

不提倡:

func computeFFT(context: Context?,inputData: InputData?) throws -> Frequencies {
    if let context  = context {
        if let inputData = inputData {
           // use context and input to compute the frequencies
          return frequencies
        }else {
            throw FFTError.noInputData
        }
    } else {
        throw FFTError.noContext
    }
}

提倡:

func computeFFT(context: Context?, inputData: InputData?) throws -> Frequencies {
    guard let context = context else {
          throw FFTError.noContext
    }
    guard let inputData = inputData else {
        throw FFTError.noInputData
    }
   // use context and input to compute the frequencies
   return frequencies
}

不提倡:

if let number1 = number1 {
  if let number2 = number2 {
    if let number3 = number3 {
      // do something with numbers
    } else {
      fatalError("impossible")
    }
  } else {
    fatalError("impossible")
  }
} else {
  fatalError("impossible")
}

提倡:

guard let number1 = number1,
      let number2 = number2,
      let number3 = number3 else {
  fatalError("impossible")
}
// do something with numbers

當然也不能亂用guard,記住原則是用guard語句來提前退出函數

defer

defer意為延緩值戳,推遲之意议谷,用defer修飾的語句,并不會馬上執(zhí)行述寡,而是被推入棧中柿隙,直到該作用域結束時才會被調用叶洞,如果一個作用域中有多個defer,其調用順序是自下而上的鲫凶。

聲明方式如下:

defer {
    // do something
}
func doSomethingWithDefer(){
    //1 
    openDirectory()
    //2
    defer{ closeDirectory() }
    //3
    openFile()
    //4
    defer{ closeFile() }
}

執(zhí)行順序 1 3 4 2

fallthrough

在Swift的switch中,case后面加了fallthrough的用法衩辟,就和Objective-C的case后面沒有break的用法是一樣的

使用fallthrough需要注意的有:
1.使用 fallthrough 后螟炫,會直接運行 【緊跟的后一個】 case 和 default 語句,不論條件是否滿足都會執(zhí)行

var age = 10
switch age {
    case 0...10:
              print("小朋友")
    case 11...20:
             print("大朋友")
    case let x:
             print("\(x)歲的朋友")
}
// 輸出 :
小朋友 
大朋友

2.加了fallthrough語句后艺晴,【緊跟的后一個】case條件不能定義常量和變量

var age = 10
switch age {
    case 0...10:
        print("小朋友")
        fallthrough //此處報錯
    case let x:
        print("\(x)歲的朋友")
}

//程序報錯:
'fallthrough' cannot transfer control to a case label that declares variables

3.執(zhí)行完fallthrough后直接跳到下一個條件語句昼钻,本條件執(zhí)行語句后面的語句不執(zhí)行

var age = 10
switch age {
    case 0...10:
        print("小朋友")
        fallthrough
        print("我跳轉了哦") //這一句沒有執(zhí)行
    case 11...20:
        print("大朋友")
    case let x:
        print("\(x)歲的朋友")
}

//輸出結果:
小朋友
大朋友
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市封寞,隨后出現的幾起案子然评,更是在濱河造成了極大的恐慌,老刑警劉巖狈究,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件碗淌,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機亿眠,發(fā)現死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門碎罚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人纳像,你說我怎么就攤上這事荆烈。” “怎么了竟趾?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵憔购,是天一觀的道長。 經常有香客問我潭兽,道長倦始,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任山卦,我火速辦了婚禮鞋邑,結果婚禮上,老公的妹妹穿的比我還像新娘账蓉。我一直安慰自己枚碗,他們只是感情好,可當我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布铸本。 她就那樣靜靜地躺著肮雨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪箱玷。 梳的紋絲不亂的頭發(fā)上怨规,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天,我揣著相機與錄音锡足,去河邊找鬼波丰。 笑死,一個胖子當著我的面吹牛舶得,可吹牛的內容都是我干的掰烟。 我是一名探鬼主播,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼沐批,長吁一口氣:“原來是場噩夢啊……” “哼纫骑!你這毒婦竟也來了?” 一聲冷哼從身側響起九孩,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤先馆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后躺彬,有當地人在樹林里發(fā)現了一具尸體煤墙,經...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡缤底,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了番捂。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片个唧。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖设预,靈堂內的尸體忽然破棺而出徙歼,到底是詐尸還是另有隱情,我是刑警寧澤鳖枕,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布魄梯,位于F島的核電站,受9級特大地震影響宾符,放射性物質發(fā)生泄漏酿秸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一魏烫、第九天 我趴在偏房一處隱蔽的房頂上張望辣苏。 院中可真熱鬧,春花似錦哄褒、人聲如沸稀蟋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽退客。三九已至,卻和暖如春链嘀,著一層夾襖步出監(jiān)牢的瞬間萌狂,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工怀泊, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留茫藏,地道東北人。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓包个,卻偏偏與公主長得像刷允,于是被迫代替她去往敵國和親冤留。 傳聞我的和親對象是個殘疾皇子碧囊,可洞房花燭夜當晚...
    茶點故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內容