Switch 语句必须详尽 - Xcode 错误?

Posted

技术标签:

【中文标题】Switch 语句必须详尽 - Xcode 错误?【英文标题】:Switch Statement Must Be Exhaustive - Xcode bug? 【发布时间】:2017-06-23 05:20:13 【问题描述】:

我对 Xcode 抛出的这个错误感到非常困惑。它说Switch statement must be exhaustive。但是,我 100% 肯定我正在处理所有案件。

这是我的代码:

enum Search: Endpoint 
    case sets(query: String?, creator: String?, imagesOnly: Bool?, autocomplete: Bool?, modifiedSince: TimeInterval?, page: Int?, perPage: Int?)
    case classes(query: String, page: Int?, perPage: Int?)
    case universal(query: String, page: Int?, perPage: Int?)

    public var baseURL: String  return QuizletEndpoint.baseURL 
    public var version: Float  return QuizletEndpoint.version 

    public var path: String 
        switch self  // Switch must be exhaustive ERROR.
        case .sets:
            return "search/sets"
        case .classes:
            return "search/classes"
        case .universal:
            return "search/universal"
        
    

更新 #1 以下是关于我的协议Endpoint 的详细信息:

 /// Represents an Endpoint for networking.
public protocol Endpoint: URLConvertible, URLRequestConvertible 
    /// The url without any parameters or paths.
    var baseURL: String  get 
    /// The version of the API.
    var version: Float  get 
    /// The path to the resource.
    var path: String  get 
    /// Any parameters to be encoded.
    var params: Parameters  get 


public extension Endpoint 

     public func asURL() throws -> URL 
         let finalURL = try baseURL.asURL().appendingPathComponent("\(version)").appendingPathComponent(path)
         return finalURL
     

     func asURLRequest() throws -> URLRequest 
         let finalURL = try asURL()
         let request = URLRequest(url: finalURL)

         return try URLEncoding.default.encode(request, with: params)
     

更新 #2 好的,现在我几乎可以肯定这是一个 Xcode 错误。将default 子句添加到我的枚举会产生一个编译器警告,指出default 子句永远不会被执行,因此编译器有点同意default 子句是多余的:

public var path: String 
        switch self 
        case .sets:
            return "search/sets"
        case .classes:
            return "search/classes"
        case .universal:
            return "search/universal"
        default: return "" // WARNING: Default will never be executed.
        
 

我是疯了还是这是一个 Xcode 错误?我已经尝试清理构建文件夹,定期清理,多次重建,但我似乎无法让这个错误消失。我开始相信这是 Xcode 中的错误,因为我正在运行测试版,但我只想仔细检查并确保它不是我这边的错误。

【问题讨论】:

请张贴代码,而不是截图——最好是minimal reproducible example。 @MartinR 会更新我的问题 @LeoDabus 但我对所有情况都满意 您的代码为我编译(如果我删除未清除的原始类型“端点”) 这绝对是 Xcode 9 beta 2 的一个错误。在详尽性上正确检测到具有单个案例 case a(String?, String?, Bool?, Int?, Int?, Int?) 的枚举上的开关,但不适用于 case a(String?, String?, Bool?, Bool?, Int?, Int?)。它以某种方式取决于枚举案例的关联值的数量和类型。 Xcode 8.3.3 不会发生这种情况。 【参考方案1】:

有时 Swift 无法检测到所有可能的情况。

最好的办法是添加 default: 案例,然后添加 break

但是请注意,如果您从 enum 中删除 : Endpoint 类型,您的代码在 Xcode 8.3.3 中可以正常工作。那是什么类型的?

【讨论】:

是的,但我不想有不必要的代码。如上所示,我满足所有情况。 即使有关联的值,编译器通常也会检测 switch 语句是否详尽。你的说法总体上是不正确的。 是的,看起来是这样。一旦你删除: Endpoint,复制和粘贴他的代码就可以了。所以看起来它与那种类型有关。在这里更新我的答案。【参考方案2】:

这样写就不会报错(Xcode 9 beta 2):

import Foundation

enum Search 
    case sets(query: String?, creator: String?, imagesOnly: Bool?, autocomplete: Bool?, modifiedSince: TimeInterval?, page: Int?, perPage: Int?)
    case classes(query: String, page: Int?, perPage: Int?)
    case universal(query: String, page: Int?, perPage: Int?)

    public var baseURL: String  return "" 
    public var version: Float  return 0 

    public var path: String 
        switch self  // Switch must be exhaustive ERROR.
        case .sets(_, _, _, _, _, _, _):
            return "search/sets"
        case .classes(_, _, _):
            return "search/classes"
        case .universal(_, _, _):
            return "search/universal"
        
    

如果我要做很多匹配,我通常更喜欢将关联的值包装在单独的结构中。这使得模式更简单,并且在更改关联值的性质时减少了工作量。因此:

enum Search 

    struct Sets 
        var query: String?
        var creator: String?
        var imagesOnly: Bool?
        var autocomplete: Bool?
        var modifiedSince: TimeInterval?
        var page: Int?
        var perPage: Int?
    

    case sets(Sets)

    struct Classes 
        var query: String
        var page: Int?
        var perPage: Int?
    

    case classes(Classes)

    struct Universal 
        var query: String
        var page: Int?
        var perPage: Int?
    

    case universal(Universal)

    public var baseURL: String  return "" 
    public var version: Float  return 0 

    public var path: String 
        switch self  // Switch must be exhaustive ERROR.
        case .sets(_):
            return "search/sets"
        case .classes(_):
            return "search/classes"
        case .universal(_):
            return "search/universal"
        
    

【讨论】:

但是 OP 的代码(没有 (_, ...) 模式)也不会产生错误 ... 是的,它为我修好了。我会在 3 分钟内将其标记为已接受(如果允许的话)。你知道为什么会这样吗? 当我将 OP 的代码放在 Xcode 8.3.3 操场上时,它不会出错,但是当我将它粘贴到 Xcode 9 beta 2 REPL 中时,它会出错。 @MartinR 即使我很难理解两者都不应该起作用吗? @Harish 谢谢。所以这个答案可能应该更新清楚。【参考方案3】:

XCode 仅在您切换枚举时检查 switch 语句是否详尽。对于其他所有情况,它会检查是否存在默认语句,如果没有,则会发出警告。

为了编译器,你需要包含一个默认块,但没有任何事情要做,break 关键字就派上用场了:

public var path: String 
    switch self  // Switch must be exhaustive ERROR.
    case .sets:
        return "search/sets"
    case .classes:
        return "search/classes"
    case .universal:
        return "search/universal"
   default: break
    

【讨论】:

【参考方案4】:

使用enum 类型时,切换块是详尽的。即使打开Bool 也需要除了true 和false 之外的默认块。所以可能编译器会引发

每个 switch 语句都必须是详尽的。也就是说,所有可能的 正在考虑的类型的值必须与以下之一匹配 切换案例。

正如@Dave Wood 所说,最好的办法是添加一个默认值:case 后跟 break

【讨论】:

但是,我的代码中也有其他枚举,它工作正常。我不明白为什么这个失败了。【参考方案5】:

如果在 switch 语句中你负责返回 TableViewCell 或 CollectionViewCell 之类的东西,那么除了以下两个之外的所有解决方案都将失败:

    使用 UITableViewCell() 或 UICollectionViewCell()。 “()”将实例化您需要返回的项目的空值版本。

    写一个错误。在重构代码以添加另一种可能性而不增加 switch 语句的相应案例时,您可能会遇到此错误(从而使其不完整)。在这种情况下,您需要向未来的程序员写一封情书错误消息,以明确解释他们忘记了什么。

    返回 UITableViewCell()

... 是使我摆脱此错误的代码,但如果您确实已经详尽无遗,则可能更适合编写错误代码。 fatalError() 可以解决问题。

【讨论】:

以上是关于Switch 语句必须详尽 - Xcode 错误?的主要内容,如果未能解决你的问题,请参考以下文章

Swift中开关盒的详尽条件

枚举的详尽 switch 语句的静态分析 [重复]

switch 在递归类型中必须是详尽的

Xcode 6.1 if/else/for/switch 语句不自动完成(仅限 Swift)

Java中为啥我写switch语句,在case后加break就错误,不加就正确,很困惑,

Swift 无法确定 switch 子句是不是详尽