【问题标题】:exception handling with swift用 swift 处理异常
【发布时间】:2016-04-03 08:59:46
【问题描述】:

我有一个关于 Swift 中的异常处理的问题。 UIStoryboard 类的 UIKit 文档指出,如果标识符为 nil 或故事板中不存在,则 instantiateViewControllerWithIdentifier( identifier: String ) -> UIViewController 函数将引发异常。但是,如果我使用如下所示的 do/try/catch,我会收到警告“在 'try' 表达式中没有调用抛出函数。”

这只是一个警告,所以我认为这是一个智能感知问题;但是当我运行以下代码并故意使用无效标识符时,不会捕获到异常并生成 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?

提前致谢。

布莱恩

【问题讨论】:

  • 感谢您的快速回复。我的问题的重点在所有细节中都丢失了。文档说抛出了异常,但 XCode 警告说没有抛出异常 - 为什么有区别?
  • 是的,你得到了 RuntimeException,但是对于静态函数不是这样。
  • 对不起 - 我花了超过 5 分钟来写这篇文章,所以我重新发布。感谢您及时的回复。我的问题的重点在所有细节中都丢失了。文档说抛出了一个异常,但是 XCode 警告说没有抛出异常——为什么会有区别?我的 catch 没有模式,并且根据 Swift 语言参考“如果 catch 子句没有模式,则该子句匹配任何错误并将错误绑定到名为 error 的本地常量。”我看过你的帖子,我认为我所做的与你记录的内容没有什么不同。
  • 这能回答你的问题吗? Catching NSException in Swift

标签: ios swift exception-handling


【解决方案1】:
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
    }

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

【讨论】:

    【解决方案2】:

    这是Catching NSException in Swift中讨论的更普遍问题的一个具体案例

    总结好像是swift异常和objc异常是不一样的。

    在这种情况下,swift 文档说它抛出了一个异常,但这不能被捕获;这听起来至少是一个文档错误。

    我不同意这里的其他答案,即缺少 VC 显然是程序员错误。如果行为如文档所述,则可以假设一种设计,其中公共代码的反应不同,具体取决于特定情况|产品|本地化中是否存在 VC。必须添加额外的配置以确保仅在存在 VC 时才尝试加载它,这是对边缘情况错误等的邀请。参考文献更新异常。

    【讨论】:

    • 确实,在查看 UIStoryboard 初始化方法的文档时,我不明白为什么文档会说它抛出但我无法在该初始化上 try?。只为找到你的答案。谢谢。
    【解决方案3】:

    instantiateViewControllerWithIdentifier 不是一个抛出函数,你不能使用 do...try...catch 来处理它。如果视图控制器在情节提要中不可用,则您无能为力。这是一个程序员的错误,创建它的人应该处理这些问题。这类错误不能归咎于 iOS 运行时。

    【讨论】:

    • 感谢 Midhun 和 @luk2302。这对于硬编码的标识符非常有意义。对于数据驱动的标识符,它在制作应用程序防弹方面留下了一些空白,但理解这种行为将推动正确的测试。谢谢!
    【解决方案4】:

    您无法从该异常中恢复,该异常是 RuntimeException
    简单地问自己:“我会如何反应?” - 如果答案是“我不知道”,那你为什么还要抓住它呢? p>

    以不正确的标识符为例——当你发现错误时你会怎么做???

    您无法以任何有意义的方式恢复。如果无法找到您传入的标识符,那是您作为开发人员在创建应用程序时做错的事情。这在测试应用程序时会并且应该是显而易见的。如果您不知何故错过了它,您的应用将在 Apple Review 中或客户端设备上崩溃。

    【讨论】:

    • 我不同意。如果您通过 http 请求动态地收到标识符,并且您的应用程序有两个版本:NewVersion 和 OldVersion(因为您总是有少数用户不更新),您必须捕捉并响应旧版本中的错误。我有几种情况,我在旧版本中使用回退代码来响应只有新版本才有的代码。例如,catch 块很可能会触发警告,告诉用户他需要更新以检查新内容,而这只是表面问题。
    • @Uzaak 使用 try-catch 检测更新听起来非常糟糕。这仍然不是必须捕获此类异常的有效用例。 如果您从外部来源获得标识符,您应该首先验证它,而不仅仅是使用它并对产生的异常做出反应。听起来像是一个糟糕的设计恕我直言。
    • 我完全同意应该事先验证,但苹果并没有给我们验证的方法。最初,storyboard.instantiateViewControllerWithIdentifier 的目的是在他们没有找到控制器时返回 nil(如当时的文档所述),这很好。但它错误地抛出了异常。后来,他们修复了文档(大声笑)并将异常抛出作为一般情况。因此,除非有一种非 try-catch 方式来确定视图是否存在,否则我目前坚持这一点。你知道有什么更好的方法吗?
    猜你喜欢
    • 1970-01-01
    • 2016-04-25
    • 1970-01-01
    • 2014-07-27
    • 2016-01-21
    • 1970-01-01
    • 2015-09-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多