用swift处理异常

时间:2015-12-29 13:03:33

标签: ios swift exception-handling

我对Swift中的异常处理有疑问。 UIStoryboard类的UIKit文档声明了instantiateViewControllerWithIdentifier(identifier:String) - >如果标识符为nil或故事板中不存在,则UIViewController函数将抛出异常。但是,如果我使用如下所示的do / try / catch,我会收到一个警告"在尝试'尝试' 。表达"

这只是一个警告,所以我认为这是一个智能问题;但是当我运行以下代码并故意使用无效标识符时,不会捕获异常并生成SIGABRT。

        let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
    do {
        let controller = try storyboard.instantiateViewControllerWithIdentifier("SearchPopup")

        // This code is only included for completeness...
        controller.modalPresentationStyle = .Popover
        if let secPopoverPresentationController = controller.popoverPresentationController {
            secPopoverPresentationController.sourceView = self.view
            secPopoverPresentationController.permittedArrowDirections = .Any
            secPopoverPresentationController.barButtonItem = self.bSearchButton
        }
        self.presentViewController(controller, animated: true, completion: nil)
        // End code included for completeness.
    }
    catch {
        NSLog( "Exception thrown instantiating view controller." );
        return;
    }

对于抛出这样的异常的函数,你应该怎么做/ try / catch?

提前致谢。

布赖恩

4 个答案:

答案 0 :(得分:4)

这是Catching NSException in Swift

中讨论的更一般问题的具体情况

摘要似乎是快速异常和objc异常不同。

在这个例子中,swift文档说它抛出了一个Exception,但是这个不能被捕获;这听起来像是一个文档错误。

我不同意这里的其他答案,一个丢失的VC显然是程序员错误。如果行为是记录在案的,那么可以假设一种设计,其中公共代码的反应取决于在特定情况下是否存在VC。必须添加额外的配置以确保仅在存在时才尝试加载VC是对边缘案例错误等的邀请。 C.F.更新异常。

答案 1 :(得分:1)

您没有从该例外中恢复,该例外是RuntimeException 简单地问问自己:"我将如何反应?" - 如果答案是"我不知道" 那么为什么你甚至想要抓住它?

以不正确的标识符为例 - 在捕获错误时你会怎么做?

你无法以任何有意义的方式恢复。如果找不到您传入的标识符,则表示您在创建应用时出错了。在测试应用程序时,这是显而易见的。如果您错过了它,您的应用程序将在Apple的评论或客户端的设备上崩溃。

答案 2 :(得分:1)

instantiateViewControllerWithIdentifier不是投掷功能,您无法使用do ... try ... catch来处理它。如果故事板中没有视图控制器,则无法执行任何操作。程序员的错误,创造它的人,应该处理这些问题。你不能因为这种错误而责怪iOS运行时。

答案 3 :(得分:0)

let storyboard = UIStoryboard(name: "StoryboardName", bundle: nil)

如果没有找到故事板,此方法不会抛出错误并且不会返回 nil,因此它只会在运行时使应用程序崩溃。那是不可避免的。但是我已经找到了确保不会发生由于此异常导致的运行时崩溃的方法。它通过单元测试。

虽然有一些约定:

enum AppStoryboard:String, CaseIterable {
    case Main
    case Magazine
    case AboutUs
    
    var storyboard:UIStoryboard {
        return UIStoryboard(name: self.rawValue, bundle: nil)
    }
}
  1. 你应该只通过这个枚举来初始化故事板。此枚举是 CaseIterable,因此您可以使用 AppStoryboard.allCases 访问所有案例

在此之后创建一个单元测试类,用于检查我们需要的所有故事板是否存在。

class AppstoryboardTests: XCTestCase {
    func testIfAllStoryboardExistInBundle() throws {
        let storyboards = AppStoryboard.allCases
        for sb in storyboards {
            _ = sb.storyboard
        }
        XCTAssert(true)
    }
}

如果 Appstoryboard 中定义的所有故事板都不存在于 bundle 中,则测试将失败并出现异常。如果所有故事板都可用,则测试将通过。

这种方法有点不正统,但比应用程序在运行时崩溃要好。 :)

注意:如果您对单元测试不满意 把它放在 AppDelegate 的 didFinishLaunchingWithOptions 方法中,如下所示:

   func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        let storyboards = AppStoryboard.allCases
        for sb in storyboards {
            _ = sb.storyboard
        }
        return true
    }

如果没有找到故事板,应用会做的第一件事就是崩溃。