Swift 在延迟块中传播异常的正确方法

Swift 在延迟块中传播异常的正确方法,swift,exception-handling,Swift,Exception Handling,在下面的示例中,函数usingTemporaryDirectory()创建并删除一个临时目录,并在其间调用传递的函数body()。如果异常由createTemporaryDirectory()或传递的函数body()引发,则会传播到调用方。但是removeDirectory()引发的异常无法传递给调用者,因为任何异常都不能逃脱defer块 import Foundation func createTemporaryDirectory() throws -> URL { ... } fu

在下面的示例中,函数
usingTemporaryDirectory()
创建并删除一个临时目录,并在其间调用传递的函数
body()
。如果异常由
createTemporaryDirectory()
或传递的函数
body()
引发,则会传播到调用方。但是
removeDirectory()
引发的异常无法传递给调用者,因为任何异常都不能逃脱
defer

import Foundation

func createTemporaryDirectory() throws -> URL { ... }

func removeDirectory(_ url: URL) throws { ... }

func usingTemporaryDirectory(body: (URL) throws -> ()) throws {
    let tempDir = try createTemporaryDirectory()

    defer {
        // Errors thrown from here are not handled.
        try removeDirectory(tempDir)
    }

    try body(tempDir)
}
处理此类异常的正确方法是什么?我看到两种选择:

  • 只需捕获异常并记录一条消息
  • 捕获异常,记录消息并中止进程
  • 我不想使用选项1,因为在本例中,这可能会导致任意数量的临时目录堆积起来。我也不想使用选项2,因为这会阻止外部堆栈帧完成清理工作(例如,如果创建了多个临时目录,至少应该尝试删除所有临时目录)

    Java有一个称为的特性。在这种情况下,
    defer
    块中引发的异常可以作为抑制异常添加到
    body()
    引发的异常(如果有)中。斯威夫特有类似的特征吗

    在这种情况下,可以将延迟块中引发的异常作为抑制异常添加到由
    body()
    引发的异常(如果有)中。斯威夫特有类似的特征吗

    据我所知并非如此——不过,你可以自己建造类似的东西。首先,让我们定义一个
    Error
    类型,它可以存储多个底层错误:

    struct ErrorCollection : Error {
      private var errors: [Error] = []
      init() {}
    
      init<S : Sequence>(_ sequence: S) where S.Element == Error {
        for error in sequence {
          append(error)
        }
      }
    
      mutating func append(_ error: Error) {
        switch error {
        case let x as ErrorCollection: // ensure we flatten out any nested error collections.
          errors.append(contentsOf: x.errors)
        case let x:
          errors.append(x)
        }
      }
    }
    
    extension ErrorCollection : RandomAccessCollection {
      typealias Index = Int
      typealias Element = Error
    
      var startIndex: Index { return errors.startIndex }
      var endIndex: Index { return errors.endIndex }
    
      func index(_ i: Index, offsetBy n: Index) -> Index {
        return errors.index(i, offsetBy: n)
      }
    
      subscript(index: Index) -> Element { return errors[index] }
    }
    
    然后,您可以这样使用:

    func usingTemporaryDirectory<R>(body: (URL) throws -> R) throws -> R {
      let tempDir = try createTemporaryDirectory()
    
      return try Result { try body(tempDir) }
                  .then { try removeDirectory(tempDir) }
                  .materialize()
    }
    
    func使用临时目录(body:(URL)throws->R)throws->R{
    让tempDir=try createTemporaryDirectory()
    返回try结果{try body(tempDir)}
    。然后{尝试远程目录(tempDir)}
    .具体化
    }
    
    func usingTemporaryDirectory<R>(body: (URL) throws -> R) throws -> R {
      let tempDir = try createTemporaryDirectory()
    
      return try Result { try body(tempDir) }
                  .then { try removeDirectory(tempDir) }
                  .materialize()
    }