泊學原文編程
只要咱們在編程,就必定要面對錯誤處理的問題。其實,爲了讓咱們少犯錯誤,Swift在設計的時候就儘量讓咱們明確感知錯誤,明確處理錯誤。例如:多線程
只有使用Optional才能處理空值;app
switch...case...必須處理全部的請求;函數
總之,你到處能感覺到Swift爲你少犯錯的良苦用心。因此,當你真的要處理錯誤的時候,Swift固然更會要求你嚴謹處理。spa
在Swift裏,任何一個聽從ErrorType protocol的類型,均可以用於描述錯誤。ErrorType是一個空的protocol,它惟一的功能,就是告訴Swift編譯器,某個類型用來表示一個錯誤。而一般,咱們使用一個enum來定義各類錯誤。例如,假設咱們有一個機器人類型,咱們要定一個表達它工做狀態的錯誤:線程
enum RobotError: ErrorType { case LowPower(Double) case Overload(Double) }
其中LowPower表示電量低,它的associated value表示電量的百分比。而Overload表示超過負載,它的associated value表示最大負載值。設計
而後,咱們來建立一個表示機器人的類:code
class Robot { var power = 1.0 let maxLifting = 100.0 // Kg }
它有兩個屬性,power表示當前電量,maxLifting表示它能夠舉起來的最大質量。而後,咱們添加一些能夠發送給Robot的命令:orm
enum Command { case PowerUp case Lifting(Double) case Shutdown }
Command中的三個case分別表示對Robot發送:啓動、舉重和關機三個命令。ci
接下來,咱們給Robot添加一個接受命令的方法 action。
class Robot { var power = 1.0 let maxLifting = 100.0 // Kg func action(command: Command) throws { } }
因爲action有可能發生異常,對於這樣的方法,咱們要明確使用throws關鍵字標記它。在action的實現裏,咱們用一個switch...case來遍歷Command:
class Robot { var power = 1.0 let maxLifting = 100.0 // Kg func action(command: Command) throws { switch command { case .PowerUp: guard self.power > 0.2 else { throw RobotError.LowPower(0.2) } print("Robot started") case let .Lifting(weight): guard weight <= maxLifting else { throw RobotError.Overload(maxLifting) } print("Lifting weight: \(weight) KG") case .Shutdown: print("Robot shuting down...") } } }
在action的實現裏,當處理.PowerUp命令時,咱們使用了guard確保Robot電量要大於20%,不然,咱們使用throw RobotError.LowPower(0.2)的方式拋出了一個異常(throw出來的類型必須是ErrorType)。
處理.Lifting命令時,咱們讀取了.Liftting的associated value,若是要舉起的質量大於maxLifting,則throw RobotError.Overload(maxLifting)。
一般,guard和throw配合在一塊兒,可讓咱們的代碼變的更加簡潔。
當咱們調用了一個可能會拋出異常的方法時,咱們必定要"經過某種方式"處理可能會發生的異常,若是你不處理,iOS會替你處理。固然,做爲"代勞"的成本,iOS也會Kill掉你的app。所以,對於"業務邏輯類"的異常,咱們仍是本身處理好些,Swift容許咱們使用三種方式處理異常。爲了演示它們的用法,咱們先來定義一個讓Robot工做的函數,因爲它會調用action,所以它也會拋出RobotError異常,咱們也須要用throws來定義它:
func working(robot: Robot) throws { }
在working的實現裏,首先,咱們要讓Robot"啓動":
func working(robot: Robot) throws { do { try robot.action(Command.PowerUp) } catch let RobotError.LowPower(percentage) { print("Low power: \(percentage)") } }
經過前面action的代碼咱們知道,若是傳入的robot參數的"電量"低於20%,action會拋出異常,所以在working的實現裏:
咱們必須在調用會拋出異常的方法前面使用try關鍵字;
若是咱們要捕獲方法拋出的異常,就須要把會拋出異常的代碼放在關鍵字do包含的代碼塊裏;
咱們使用catch關鍵字匹配要捕捉的各類異常,例如在上面的例子裏,咱們捕捉了.LowPower,而且讀取了它的associated value;
若是咱們要捕獲多個異常,就能夠在do代碼塊後面,串聯多個catch,例如,咱們添加一個讓Robot舉起某個東西的命令:
func working(robot: Robot) throws { do { try robot.action(Command.PowerUp) try robot.action(Command.Lifting(52)) } catch let RobotError.LowPower(percentage) { print("Low power: \(percentage)") } catch let RobotError.Overload(maxWeight) { print("Overloading, max \(maxWeight) KG is allowd") } }
咱們就須要在do後面多串聯一個catch,用來捕獲Robot"超載"的異常。
在Swift的異常處理機制理,有一個容許咱們添加不管代碼執行正常與否,只要離開當前做用域,就必定會執行的代碼。咱們使用defer關鍵字來指定這樣的代碼。例如,咱們給working添加一個defer,它用來讓Robot關機。
func working(robot: Robot) throws { defer { try! robot.action(Command.Shutdown) } do { try robot.action(Command.PowerUp) try robot.action(Command.Lifting(52)) } catch let RobotError.LowPower(percentage) { print("Low power: \(percentage)") } catch let RobotError.Overload(maxWeight) { print("Overloading, max \(maxWeight) KG is allowd") } }
在上面的defer代碼塊裏,咱們使用了"try!"這樣的形式。這是因爲defer代碼塊中,不容許咱們包含任何會跳出當前代碼塊的語句,例如:break / return / 拋出異常等。所以,咱們使用try!告訴Swift咱們肯定這個調用不會發生異常(若是你對Swift說謊,是會引起運行時異常的 ^.^)。
另外,使用"try!"標記的函數調用,能夠不放在do代碼塊裏。
最後,咱們調用working函數,讓Robot完成工做:
let iRobot = Robot() try? working(iRobot)
在這裏,咱們咱們使用了"try?"的形式調用了一個會拋出異常的方法,它把表達式的評估結果轉換爲一個Optional。例如,咱們讓working返回一個Int:
func working(robot: Robot) throws -> Int { defer { try! robot.action(Command.Shutdown) } do { try robot.action(Command.PowerUp) try robot.action(Command.Lifting(52)) } catch let RobotError.LowPower(percentage) { print("Low power: \(percentage)") } catch let RobotError.Overload(maxWeight) { print("Overloading, max \(maxWeight) KG is allowd") } return 0 }
從上面的代碼裏能夠看到,當函數有返回值的時候,咱們要把throws寫在返回值前面。
而後,咱們查看working的返回值和類型:
let a = try? working(iRobot) print("value: \(a)\n type: \(a.dynamicType)")
這裏,因爲咱們處理異常,所以a的值是0,可是,a的類型,是一個Optional<Int>。
若是咱們把RobotError.Overload註釋掉,而後讓Robot舉起超過100KG的物體:
func working(robot: Robot) throws -> Int { defer { try! robot.action(Command.Shutdown) } do { try robot.action(Command.PowerUp) try robot.action(Command.Lifting(152)) } catch let RobotError.LowPower(percentage) { print("Low power: \(percentage)") } /*catch let RobotError.Overload(maxWeight) { print("Overloading, max \(maxWeight) KG is allowd") }*/ return 0 }
這樣異常就會被拋到working外圍,此時Swift運行時會捕捉到這個異常,而且,把a的值設置成nil:
let a = try? working(iRobot) print("value: \(a)\n type: \(a.dynamicType)")
接下來?在下一段中,咱們將向你們介紹多線程環境中的異常處理。