符合Equatable协议的嵌套枚举产生错误

时间:2019-03-05 22:20:18

标签: swift enums

我有一个名为TableViewItem的协议。该协议强制符合标准的对象实现type属性,该属性具有协议TableViewCellIdentifiable作为其类型。 TableViewCellIdentifiable用于将三个嵌套枚举分组在一起,如下所示:

internal protocol TableViewCellIdentifiable: Equatable { }

internal enum TableViewCellType {
    internal enum PortfolioSelection: String, TableViewCellIdentifiable {
        case portfolio = "portfolioTableViewCell"
        case enterPortfolioDetails = "enterPortfolioDetailsTableViewCell"
        case addPortfolio = "actionTableViewCell"
    }

    internal enum EditPortfolio: String, TableViewCellIdentifiable {
        case editPortfolioName = "editPortfolioNameTableViewCell"
        case deletePortfolio = "deletePortfolioTableViewCell"
    }

    internal enum Portfolio: String, TableViewCellIdentifiable {   
        case portfolioAsset = "portfolioAssetTableViewCell"
        case addAsset = "actionTableViewCell"
    }
}

以下是如何使用它的示例:

internal final class EditPortfolioNameTableViewItem: TableViewItem {

    // MARK: - Internal Properties

    internal let type: TableViewCellIdentifiable = TableViewCellType.EditPortfolio.editPortfolioName
    internal let viewModel: TableViewCellModel

    // MARK: - Initialization

    internal init(viewModel: EditPortfolioNameTableViewCellModel) {
        self.viewModel = viewModel
    }
}

不幸的是,在声明type属性的那一行上,我收到以下错误:

  

协议“ TableViewCellIdentifiable”只能用作一般约束,因为它具有“自我”或相关类型要求

我已经阅读了遇到此错误的其他人的其他问题/答案,但我不太明白为什么这种特殊的实现方式会带来问题,以及解决方案将是什么。我知道Equatable是问题的根源,但这对功能至关重要,因为枚举有两个目的:

  1. 为表视图单元格(原始值)提供重用标识符。
  2. 要允许比较类型-即:

    self.tableViewItems.contains(where: { $0.type == item.type })
    

任何建议都将不胜感激,即使这意味着采取其他方法。

2 个答案:

答案 0 :(得分:2)

在您脑海中,以下代码应该编译吗?

var x : Equatable

不应该。为什么?

因为您有:

var x : Equatable 
var y : Equatable

然后,编译器无法确保x和y是同一类型。 x可以是“ John”,因为“ John” /字符串是平等的……而y可以是10,因为10 /整数是平等的。

并且编译器会怀疑您可能想在下面几行

if x == y { print ("equal" } 

它无法处理。因此,它一开始就阻止您进行此操作。


由于上述原因,下面的代码行将触发相同的错误。

internal let type: TableViewCellIdentifiable = TableViewCellType.EditPortfolio.editPortfolioName

答案 1 :(得分:1)

正如Honey的回答所解释的,TableViewCellIdentifiable没有提供足够的类型信息供编译器使用。您可能会采用另一种方法,该方法会稍微改变结构(并可能会导致过大杀伤力),但会提供您正在寻找的功能:

internal protocol ValueAssociated { }

internal extension ValueAssociated {

    fileprivate var association: (label: String, value: Any?)? {
        get {
            let mirror = Mirror(reflecting: self)
            if let association = mirror.children.first, let label = association.label {
                return (label, association.value)
            }
            return nil
        }
    }
}

internal protocol CellIdentifiable {

    var rawValue: String { get }
}

internal enum CellType: Equatable, ValueAssociated {

    case portfolio(PortfolioIdentifier)
    case portfolioSelection(PortfolioSelectionIdentifier)
    case editPortfolio(EditPortfolioIdentifier)

    internal var identifier: String? {
        return (self.association?.value as? CellIdentifiable)?.rawValue
    }

    internal enum PortfolioIdentifier: String, Equatable, CellIdentifiable {

        case portfolioAsset = "portfolioAssetTableViewCell"
        case addAsset = "actionTableViewCell"
    }

    internal enum PortfolioSelectionIdentifier: String, Equatable, CellIdentifiable {

        case portfolio = "portfolioTableViewCell"
        case enterPortfolioDetails = "enterPortfolioDetailsTableViewCell"
        case addPortfolio = "actionTableViewCell"
    }

    internal enum EditPortfolioIdentifier: String, Equatable, CellIdentifiable {

        case editPortfolioName = "editPortfolioNameTableViewCell"
        case deletePortfolio = "deletePortfolioTableViewCell"
    }
}

可以如下使用:

internal let cellType: CellType = .portfolio(.portfolioAsset)
print(cellType.identifier!) // Prints "portfolioAssetTableViewCell"

希望这会有所帮助。