You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中defer块异常的正确处理:是否支持抑制异常特性?

Handling Defer-Block Exceptions in Swift (Similar to Java's Suppressed Exceptions)

Great question! Unfortunately, Swift doesn’t have a built-in equivalent to Java’s suppressed exceptions—there’s no automatic way to attach errors thrown in a defer block to a primary error from the main execution path. But we can implement a similar pattern manually to avoid your two unwanted outcomes (unlogged cleanup failures or abrupt process termination).

Here’s how to do it:

Step 1: Define a Custom Error Type

First, create an error type that can capture both the primary error (from body() or directory creation) and any suppressed cleanup errors. This lets the caller access all relevant failure information:

enum TemporaryDirectoryError: Error {
    case creationFailed(underlying: Error)
    case bodyFailed(primary: Error, suppressedCleanupError: Error?)
    case cleanupFailed(underlying: Error)
}

Step 2: Rewrite the usingTemporaryDirectory Function

Modify the function to capture cleanup errors in a mutable variable, then combine them with any primary errors before throwing:

import Foundation

func createTemporaryDirectory() throws -> URL { 
    // Example implementation
    let tempDir = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
    try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
    return tempDir
}

func removeDirectory(_ url: URL) throws { 
    // Example implementation
    try FileManager.default.removeItem(at: url)
}

func usingTemporaryDirectory(body: (URL) throws -> Void) throws {
    // Handle directory creation errors explicitly
    let tempDir: URL
    do {
        tempDir = try createTemporaryDirectory()
    } catch {
        throw TemporaryDirectoryError.creationFailed(underlying: error)
    }
    
    // Variable to capture any cleanup error from the defer block
    var cleanupError: Error?
    
    defer {
        // Attempt cleanup, capture errors instead of letting them escape
        do {
            try removeDirectory(tempDir)
        } catch {
            cleanupError = error
        }
    }
    
    // Execute the body logic, then handle combined errors
    do {
        try body(tempDir)
    } catch {
        // If body threw, check if cleanup also failed and throw a combined error
        if let cleanupErr = cleanupError {
            throw TemporaryDirectoryError.bodyFailed(primary: error, suppressedCleanupError: cleanupErr)
        } else {
            throw error
        }
    }
    
    // If body succeeded but cleanup failed, throw the cleanup error
    if let cleanupErr = cleanupError {
        throw TemporaryDirectoryError.cleanupFailed(underlying: cleanupErr)
    }
}

Step 3: Handle the Composite Error in the Caller

Now the caller can inspect both the primary error and any suppressed cleanup errors, allowing them to take appropriate action (like retrying cleanup, alerting the user, or logging to monitoring):

do {
    try usingTemporaryDirectory { tempDir in
        // Your business logic here—could throw an error
        throw NSError(domain: "MyApp", code: 1, userInfo: [NSLocalizedDescriptionKey: "Failed to process file"])
    }
} catch TemporaryDirectoryError.bodyFailed(let primaryErr, let suppressedErr) {
    print("Primary error: \(primaryErr.localizedDescription)")
    if let suppressed = suppressedErr {
        print("Suppressed cleanup error: \(suppressed.localizedDescription)")
        // Optionally retry cleanup or escalate the issue here
    }
} catch TemporaryDirectoryError.cleanupFailed(let err) {
    print("Cleanup failed after successful body execution: \(err.localizedDescription)")
} catch {
    print("Unexpected error: \(error.localizedDescription)")
}

Why This Works

  • No silent failures: Cleanup errors aren’t ignored—they’re either thrown as the primary error (if the body succeeded) or attached to the body’s error as a suppressed error.
  • Graceful cleanup: The process isn’t terminated abruptly, so outer stack frames can still run their own cleanup logic (like deleting other temporary directories).
  • Transparent error handling: The caller gets full visibility into all failures, mirroring the behavior of Java’s suppressed exceptions.

If you need a more general solution (not tied to temporary directories), you could create a reusable helper function that wraps any defer cleanup logic and captures suppressed errors, but this tailored approach is straightforward for your specific use case.

内容的提问来源于stack exchange,提问作者Feuermurmel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 10:04:07