Swift 2.0 try? 的替代方法

前端之家收集整理的这篇文章主要介绍了Swift 2.0 try? 的替代方法前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

try? 语法的优点在于你不必把可能会抛出错误函数写在一个 do-catch 代码块当中。如果你使用了 try?,该函数的返回值就会是一个可选类型:成功返回 .Some,失败则返回 .None。你可以配合着 if-let 或者 guard 语句来使用 try? 语法。

try? 语法的不足则在于它对错误的简化,让你难以了解到错误是什么以及错误发生的时间。这可不是件好事。

但你可以试着写出 try? 的替代方法。比如一个简单的结果枚举:

  1. enum Result<T> {
  2. case Value(T)
  3. case Error(ErrorType)
  4. }

就像上面的代码写的那样,我喜欢分成 ValueError 而不是 errok ,枚举的不同状态可以按你自己的喜好来命名。然后你可以写个函数do-catch 语句给你枚举类型的结果做个封装然后返回。

  1. func tryit<T>(block: () throws -> T) -> Result<T> {
  2. do {
  3. let value = try block()
  4. return Result.Value(value)
  5. } catch {return Result.Error(error)}
  6. }

实际上我也不太喜欢 tryit 这个名字,你用你喜欢的名字代替就好。这个函数调用有点啰嗦了,不是我们想要的这样:

  1. let result = try myFailableCoinToss()

而是这种:

  1. let result = tryit(myFailableCoinToss)

一位名叫 glessard 的读者提供给我了另一种替代方式,建议我给 Result 添加一个初始化方法而不是用 tryit:

  1. enum Result<T> {
  2. case Value(T)
  3. case Error(ErrorType)
  4.  
  5. init(_ block: () throws -> T) {
  6. do {
  7. let value = try block()
  8. self = Result.Value(value)
  9. } catch {
  10. self = Result.Error(error)
  11. }
  12. }
  13. }

然后你直接这么调用就行了:

  1. let result = Result(myFailableCoinToss)

如果你要用 if-letguard 之外的语句来拆包你的返回值,可以用 switch

  1. let result = tryit(myFailableCoinToss)
  2. switch result {
  3. case .Value(let value): print("Success:",value)
  4. case .Error(let error): print("Failure:",error)
  5. }

或者直接用模式匹配:

  1. if case .Value(let value) = result {
  2. print("Success:",value)
  3. } else if case .Error(let error) = result {
  4. print("Failure:",error)
  5. }

你也可以添加一些退出作用域的代码来模仿 guard,这的确可行,但是代码太难看了。

  1. enum Result<T> {
  2. case Value(T)
  3. case Error(ErrorType)
  4.  
  5. func unwrap() throws -> T {
  6. if case .Value(let value) = self {return value}
  7. throw "Unable to unwrap result"
  8. }
  9.  
  10. func handleError(errorHandler: ErrorType -> Void) -> Bool {
  11. if case .Error(let error) = self {
  12. errorHandler(error)
  13. return true
  14. }
  15. return false
  16. }
  17. }
  18.  
  19. func tryit<T>(block: () throws -> T) -> Result<T> {
  20. do {
  21. let value = try block()
  22. return Result.Value(value)
  23. } catch {return Result.Error(error)}
  24. }
  25.  
  26. let result = tryit(myFailableCoinToss)
  27.  
  28. // guard error
  29. if result.handleError({
  30. error in
  31. print("Error is \(error)")
  32. }) {fatalError()} // leave scope on true
  33.  
  34. // force try for success case
  35. let unwrappedResult = try! result.unwrap()
  36.  
  37. // result is now usable at top level scope
  38. print("Result is \(unwrappedResult)")

这还有另外一种更像 try? 的方式,但是至少打印了错误

  1. func tryit<T>(block: () throws -> T) -> Optional<T>{
  2. do {
  3. return try block()
  4. } catch {
  5. print(error)
  6. return nil
  7. }
  8. }

第二种替代 try?方法不仅拥有了 if-letguard 的语句特性,还能返回错误。你可以以同样的方法调用它:

  1. let result = tryit(myFailableCoinToss)

你仍然不能基于错误类型和错误细节来制定错误处理策略,但是这种实现方式也不像 try? 那样把错误信息完全丢弃掉了。

你也可以修改 tryit 函数,让它也能接受做错误处理的代码块,但因为要处理两种不同的代码块,这个函数就会变得相当臃肿。我尝试过几种不同的实现方式,但都做的不太好,就不在这分享出来了。最大的问题是,就算你把做错误处理的代码块传给 tryit 了,你也不能像 guard 语句那样退出作用域,而且也没有这样的 guard 的替代形式能接受 try? 的 error 来作退出作用域代码块的参数。

我觉得我最后实现的应该类似于下面的这种形式,在顶层作用域中执行条件赋值,并采用 guard 语句的方式来替代普通的 try

  1. guard let result = try!! myFailableCoinToss() else {error in ...}

之所以这么做是因为我们想知道我们的程序到底会不会出错,如果不出错的话,直接就能得到返回值了。

  1. let result = try myFailableCoinToss()

如果要用 try 而且又需要进行错误处理的话,你就必须得用 do-catch 或者像结果枚举之类的其它方法了。

猜你在找的Swift相关文章