Swift 中的异常和错误处理异常处理基础篇 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
yiling
V2EX    程序员

Swift 中的异常和错误处理异常处理基础篇

  •  
  •   yiling 2016-03-17 17:38:58 +08:00 2768 次点击
    这是一个创建于 3498 天前的主题,其中的信息可能已经有所发展或是发生改变。

    异常处理基础篇

    泊学原文

    只要我们在编程,就一定要面对错误处理的问题。其实,为了让我们少犯错误, Swift 在设计的时候就尽可能让我们明确感知错误,明确处理错误。例如:

    • 只有使用 Optional 才能处理空值;
    • switch...case...必须处理所有的请求;

    总之,你处处能感受到 Swift 为你少犯错的良苦用心。所以,当你真的要处理错误的时候, Swift 当然更会要求你严谨处理。

    如何描述一个错误?

    在 Swift 里,任何一个遵从 ErrorType protocol 的类型,都可以用于描述错误。 ErrorType 是一个空的 protocol ,它唯一的功能,就是告诉 Swift 编译器,某个类型用来表示一个错误。而通常,我们使用一个 enum 来定义各种错误。例如,假设我们有一个机器人类型,我们要定一个表达它工作状态的错误:

    enum RobotError: ErrorType { case LowPower(Double) case Overload(Double) } 

    其中 LowPower 表示电量低,它的 associated value 表示电量的百分比。而 Overload 表示超过负载,它的 associated value 表示最大负载值。

    如何描述一个会发生错误的方法?

    然后,我们来创建一个表示机器人的类:

    class Robot { var power = 1.0 let maxLifting = 100.0 // Kg } 

    它有两个属性, power 表示当前电量, maxLifting 表示它可以举起来的最大质量。然后,我们添加一些可以发送给 Robot 的命令:

    enum Command { case PowerUp case Lifting(Double) case Shutdown } 

    Command 中的三个 case 分别表示对 Robot 发送:启动、举重和关机三个命令。

    接下来,我们给 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 { } 

    do...catch...

    在 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 代码块里。

    把错误变成一个 Optional

    最后,我们调用 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>。 enter image description here

    如果我们把 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)") 

    enter image description here 接下来? 在下一段中,我们将向大家介绍多线程环境中的异常处理。

    目前尚无回复
    div class="content">
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1119 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 23:16 PVG 07:16 LAX 16:16 JFK 19:16
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86