Ios 如何使泛型类型的实例或返回类型符合具有关联值的协议

Ios 如何使泛型类型的实例或返回类型符合具有关联值的协议,ios,swift,uitableview,swift-protocols,Ios,Swift,Uitableview,Swift Protocols,所以我不知道标题是否正确,但问题是: 我有一个用于标识几种类型的表视图单元格的枚举。每种类型的单元都有自己的类。我已经将其中的每一个都符合一个协议,但是有一个相关的类型。我现在正试图创建一种方法来拥有这些类的实例,并任意使用它们的协议方法和属性。下面是一个例子: enum CellType { case a, b, c func getCellClass() -> ProtocolWithAssociatedType.Type { //Error here

所以我不知道标题是否正确,但问题是:

我有一个用于标识几种类型的表视图单元格的枚举。每种类型的单元都有自己的类。我已经将其中的每一个都符合一个协议,但是有一个相关的类型。我现在正试图创建一种方法来拥有这些类的实例,并任意使用它们的协议方法和属性。下面是一个例子:

enum CellType {
    case a, b, c
   
    func getCellClass() -> ProtocolWithAssociatedType.Type { //Error here
        switch self {
        case .a: return ClassA.self
        case .b: return ClassB.self
        case .c: return ClassC.self
    }
}
此枚举引发了一个错误,协议“CreateOrderTableViewCellProtocol”只能用作一般约束,因为它在该行上具有自身或关联的类型要求

这就是我的协议,除了名字:

protocol ProtocolWithAssociatedType: UITableViewCell {
    associatedtype Delegate
    var delegate: Delegate? {get set}
    func setupCell()
}
ClassA、ClassB和ClassC的所有类都符合此要求。他们都有自己的委托协议,并使用
typealias
强制转换,例如:

protocol ClassADelegate: class {
...
}

class ClassA: UITableViewCell, ProtocolWithAssociatedType {
    typealias Delegate = ClassADelegate
    weak var delegate: ClassADelegate?
    func setupCell() {}

    ...
}

extension ViewController: ClassADelegate {
...
}
所有这些都是为了精简tableView(…cellForItemAt:…)和其他类似的方法,因为在这个项目中有许多单元格类,它超出了可读性的范围,因此很难在这个特定的视图控制器上进行任何开发。 我有一个UITableView的扩展,用于为那些其可重用id与其类名相同的单元创建可重用单元,如下所示:

func dequeueReusableCell<C>(_ cellType: C.Type, for indexPath: IndexPath) -> C where C: UITableViewCell {
    let identifier = String(describing: cellType)
        
    guard let cell = dequeueReusableCell(withIdentifier: identifier, for: indexPath) as? C else {
        fatalError("Failed to find cell with identifier \"\(identifier)\" of type \"\(C.self)\"")
    }
        return cell
}
我已经阅读了这么多的问题和答案,我正在努力做到这一点,但我还没有做到。我试图避免在视图控制器中使用大量函数,并尽可能使其可修改。所有这些单元本身都像小视图控制器,它们都必须与视图控制器通信。另外,细胞不是静态的,就像在不同的场合有不同的细胞,我必须显示。现在,即使是增加一个简单的细胞是一个地狱的工作,更不用说创造一个全新的场合。但通过这种方法,我试图让事情。。。模块化

那么,有没有什么方法可以让我做到这一点,而不让运行时在这里或那里崩溃,或者创造一个黑洞,结束宇宙

编辑:我尝试了泛型类型的方法,但无法这样做。由于我希望
func getCellClass()
工作的方式,不可能让编译器知道泛型类型是什么。例如:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cellType = CellDataArray[indexPath.row].cellType
    let cellClass = cellType.getCellClass()

    let cell = tableView.dequeueReusableCell(cellClass, for: indexPath) \\ Here I have to have the cell conform to the protocol somehow

    cell.delegate = self \\So here is the associated type which doesn't need to be read but is need to be written only
    cell.setupCell() \\Obviously there are going to be some data passed to the cell instance right here
    return cell
}
func getCellClass<C>() -> C.Type where C: ProtocolWithAssociatedValue {
...
}
protocol ProtocolWithAssociatedType: UITableViewCell {
    var _delegate: AnyObject? {get set}
    func setupCell()
}
protocol CellProtocol: UITableViewCell {
    var delegate: CommonCellDelegate?
    func setupCell(...)
}
protocol CommonCellDelegate: class {
    var viewControllerProperty1: SomeClass1 { get set }
    var viewControllerProperty2: SomeClass2 { get set }
    ...
    var viewControllerPropertyN: SomeClassN { get set }

    func someCommonFunction1(...) -> ...
    func someCommonFunction2(...) -> ...
    ...
    func someCommonFunctionN(...) -> ...
}

通过这种方式,协议可用作枚举函数的返回类型,我可以强制将可重用单元转换为它,因为协议本身符合UITableViewCell。我已经建立了没有任何问题,但还不能测试(测试服务器关闭以外的工作时间)。当我测试它时,如果它运行没有任何问题,我会将其作为解决方案发布。

似乎您试图将多态行为(即单元类型的数组)与静态行为(具有关联类型的协议)混合在一起,这在当前的Swift设计中或多或少是不可能的

具有关联类型的协议是在编译时确定的,而
tableView(\uquo:cellForRowAt:)
方法中的代码是在运行时执行的,这使得它在某种程度上与具有关联类型的协议不兼容

let cell=tableView.dequeueReusableCell(cellClass,for:indexath)
在上面的代码中,为了插入委托,您需要能够告诉编译器您希望单元使用哪种类型,但是这是一种编译时特性,并且您需要在运行时使用它,因此不兼容

恐怕您必须坚持运行时错误传播/处理。除非您愿意更改
委托
以匹配整个可能的委托集,并且在这种情况下不再需要关联的类型,从而启用运行时功能

协议MyCell:UITableViewCell{
变量委托:CellDelegate1和CellDelegate2…&CellDelegateN{get set}
func setupCell(一些通用协议GetScasteDatRuntime)
}

解决方案

所以经过几天的工作和尝试,我终于想出了一个解决方案。多亏了@Cristik,我有了一个洞察力,改变了我的方法

首先,我改变了处理代表的方式。每个单元都有不同的代表。现在我只有一个所有这些细胞都使用的

因此,通过这种方式,我可以使我的财产如下所示:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cellType = CellDataArray[indexPath.row].cellType
    let cellClass = cellType.getCellClass()

    let cell = tableView.dequeueReusableCell(cellClass, for: indexPath) \\ Here I have to have the cell conform to the protocol somehow

    cell.delegate = self \\So here is the associated type which doesn't need to be read but is need to be written only
    cell.setupCell() \\Obviously there are going to be some data passed to the cell instance right here
    return cell
}
func getCellClass<C>() -> C.Type where C: ProtocolWithAssociatedValue {
...
}
protocol ProtocolWithAssociatedType: UITableViewCell {
    var _delegate: AnyObject? {get set}
    func setupCell()
}
protocol CellProtocol: UITableViewCell {
    var delegate: CommonCellDelegate?
    func setupCell(...)
}
protocol CommonCellDelegate: class {
    var viewControllerProperty1: SomeClass1 { get set }
    var viewControllerProperty2: SomeClass2 { get set }
    ...
    var viewControllerPropertyN: SomeClassN { get set }

    func someCommonFunction1(...) -> ...
    func someCommonFunction2(...) -> ...
    ...
    func someCommonFunctionN(...) -> ...
}
对于单元格将使用的视图控制器的所有属性和一些常用方法,我声明了委托协议,如下所示:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cellType = CellDataArray[indexPath.row].cellType
    let cellClass = cellType.getCellClass()

    let cell = tableView.dequeueReusableCell(cellClass, for: indexPath) \\ Here I have to have the cell conform to the protocol somehow

    cell.delegate = self \\So here is the associated type which doesn't need to be read but is need to be written only
    cell.setupCell() \\Obviously there are going to be some data passed to the cell instance right here
    return cell
}
func getCellClass<C>() -> C.Type where C: ProtocolWithAssociatedValue {
...
}
protocol ProtocolWithAssociatedType: UITableViewCell {
    var _delegate: AnyObject? {get set}
    func setupCell()
}
protocol CellProtocol: UITableViewCell {
    var delegate: CommonCellDelegate?
    func setupCell(...)
}
protocol CommonCellDelegate: class {
    var viewControllerProperty1: SomeClass1 { get set }
    var viewControllerProperty2: SomeClass2 { get set }
    ...
    var viewControllerPropertyN: SomeClassN { get set }

    func someCommonFunction1(...) -> ...
    func someCommonFunction2(...) -> ...
    ...
    func someCommonFunctionN(...) -> ...
}
由于我从一开始就打算保持视图控制器的整洁,因此我将单元格单独需要的那些不常见的方法放在这些单元格的类声明下面,作为委托属性的扩展:

class SomeCellClass: CellProtocol {
    var delegate: CommonCellDelegate?
    func setupCell(...) {
    ...
    }

    ...
}
extension CommonCellDelegate {
    func someMethodForTheCellAbove1(...) -> ... {
    ...
    }
    func someMethodForTheCellAbove2(...) -> ... {
    ...
    }
    .
    .
    .
}
因为我不再需要enum方法返回不同的类,所以我对它做了一些修改,以返回每个单元格类型的重用id。因此无需在此处返回类类型:

enum CellType {
    case cellA, cellB, cellC, ...
   
    func getCellClass() -> String { //Error here
        switch self {
        case .cellA: return String(describing: ClassA.self)
        case .cellB: return String(describing: ClassB.self)
        case .cellC: return String(describing: ClassC.self)
        ...
        // Note: We use the the name of the cell classes as their reuse id's for simplicity.
    }
}
最后,我能够在
tableView(u:cellForRowAt:)
方法中非常轻松地创建这些单元格,而无需在时空结构中创建裂缝:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cellType = CellDataArray[indexPath.row].cellType
    let cellReuseId = String(describing: cellType.getCellId())

    guard let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseId, for: indexPath) as? CellProtocol else { fatalError("some fatal error") }

    cell.delegate = self
    cell.setupCell(...)
    return cell
}
通过这种方式,可以非常容易地进行新的实现,因为需要做的只是创建类,使类符合
CellProtocol
,在枚举中为它做一个案例,在需要的地方或任何时候将案例添加到数组中,注册单元格id,然后就在那里!由于委托,单元格现在知道了所需的所有属性,因此他们可以轻松地使用这些属性,就像他们自己的属性一样,唯一的区别是它不是
self.something
,而是
delegate?.something


我希望我的努力能对其他人有所帮助。干杯

好的,谢谢你的回答。它并没有解决我的问题,但它让我了解了如何解决它。