【问题标题】:Swift. RawRepresentable init with optional RawValue迅速。带有可选 RawValue 的 RawRepresentable init
【发布时间】:2017-07-11 10:33:01
【问题描述】:

我正在尝试使用 RawRepresentable 的可选参数制作一个通用的可失败初始化程序,基本上是 https://www.natashatherobot.com/swift-failable-enums-with-optionals/

提出了几种方法,其中一种是这样(编辑:在第二个子句中修复了let):

extension RawRepresentable {

    init?(rawValue optionalRawValue: RawValue?) {

        guard let rawValue = optionalRawValue, let value = Self(rawValue: rawValue) else { return nil }

        self = value
    }
} 

从这里https://gist.github.com/okla/e5dd8fbb4e604dabcdc3

我不知道它是否曾经在 Swift 2 上工作,但我无法在 Swift 3 上编译它。我明白了:

Command failed due to signal: Segmentation fault: 11

有没有办法让它工作?

附:我知道这篇文章及其 cmets 中的其他方法。

编辑:修复损坏的复制/粘贴代码。

【问题讨论】:

  • 编译器应该永远不会崩溃,所以这显然是一个错误。然而,它似乎在 Swift 3.1 中得到了修复(Xcode 8.3 beta 可用)。
  • File a bug 当然是关于崩溃的——没有任何代码,无论它多么损坏,都会导致编译器崩溃。

标签: swift initialization protocols


【解决方案1】:

我刚刚将您的代码复制并粘贴到 Playground 中,我得到的唯一错误是在 guard 语句中分配 value 之前缺少 let

你确定分段错误是因为这个扩展吗?

这是适合我的代码(Xcode 8.2.1,Swift 3.0.2):

extension RawRepresentable {

    init?(rawValue optionalRawValue: RawValue?) {

        guard let rawValue = optionalRawValue, let value = Self(rawValue: rawValue) else { return nil }

        self = value
    }
}

-- 编辑--

在使用原始值定义 enum 后,我确实得到了一个错误。

添加这个会使 Playground 崩溃:

enum Counter: Int {
    case one = 1, two, three, four, five
}

通过将init? 中的参数重命名为init?(optRawValue optionalRawValue: RawValue?) 来修复它。我猜问题是你在init?里面调用Self(rawValue: rawValue),编译器不知道用哪一个...

【讨论】:

  • 我尝试定义 init?(rawExtValue rawValue : RawValue) 并使用它来执行 let value = Self(rawExtValue: rawValue) 但我仍然遇到同样的错误。正如我在问题中所说,我知道其他方法,但我想使用init?(rawValue optionalRawValue: RawValue?) 使其工作。或者至少了解编译器崩溃的原因。
  • 查看此问题以获取有关编译器崩溃原因的更多详细信息:stackoverflow.com/questions/19014359/…
【解决方案2】:

无论编译此代码或使其按您希望的方式运行时遇到什么问题,我都认为您试图以错误的方式解决根本问题。

尝试设计这样的初始化程序是一种反模式:Swift 中的 Optionals 设计鼓励尽早处理和解决可空性问题,而不是迄今为止级联的故障,以至于很难恢复它们的起源。如果你有一个函数/初始化器当且仅当它被传递 nil 时返回 nil,它不应该首先接受 nil 并且永远不会返回 nil。

在 Swift 3 中,您可以为枚举保留默认的 rawValue 初始化程序,并通过两种不同的方式解决原始 NatashaTheRobot 帖子中的问题。

  1. 使用复合if(或guard)语句。

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        guard let string = segue.identifier,
              let identifier = SegueIdentifier(rawValue: string) 
            else { return /* or fatalError or whatever for catching problems */ }
        switch identifier {
             // handle all the enum cases here
        }
    }
    
  2. 使用flatMap(两次)执行函数式,如果其输入不为零,则执行闭包:

    segue.identifier.flatMap(SegueIdentifier.init).flatMap { identifier in
        switch identifier {
            // handle all cases
        }
    }
    

无论哪种方式,您都在展开两个级别的可选性(segue 是否具有标识符,以及该字符串是否是您的 SegueIdentifier 枚举的有效原始值)之前 switch ,这意味着switch 必须处理所有有效的SegueIdentifier 案例。反过来,这意味着如果您稍后添加一个新的SegueIdentifier 案例并且不处理它,您就会发现自己。 (与switching 可能是有效的SegueIdentifier 或可能为零的东西相反,这意味着你需要一个default 案例,这意味着如果你应该有新的SegueIdentifiers 你会默默地失败待处理。)

【讨论】:

  • 好吧,我解析了很多 JSON,这意味着检查每个可能的 nil 值的开销很大,而且只会带来代码重复。我认为我们必须务实,甚至会说每个带有一个参数的可失败初始化程序都应该始终接受可选参数。如果愿意,程序员有责任检查输入。不过我同意,如果只有当参数为 nil 时它才会失败,它根本不应该是失败的。
【解决方案3】:

必须将 public 添加到 init 中,它才能编译并通过测试

Apple Swift 版本 4.1.2 (swiftlang-902.0.54 clang-902.0.39.2) 目标:x86_64-apple-darwin17.6.0

extension RawRepresentable {

    public init?(rawValue optionalRawValue: RawValue?) {

      guard let rawValue = optionalRawValue, let value = Self(rawValue: rawValue) else { return nil }

      self = value
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多