【问题标题】:Generic Swift Protocol inside Generic Controller通用控制器内的通用 Swift 协议
【发布时间】:2016-01-18 16:27:37
【问题描述】:

泛型里面可以有泛型吗?

我有这个协议

public protocol ListViewModelProtocol {
    typealias ViewModel
    typealias Cell

    func titleForHeaderInSection(section: Int) -> String?
    func numberOfSections() -> Int
    func numberOfRowsInSection(section: Int) -> Int
    func viewModelAtIndexPath(indexPath: NSIndexPath) -> ViewModel
}

我也有实现此协议的基础ListViewModel

public class BaseListViewModel<T, U> : ListViewModelProtocol  {
}

但是这里已经说我的ListViewModelProtocol 没有实现。如何将 T 和 U 设置为协议内的特定类?因为如果我在协议中写这个

typealias ViewModel: CustomClass
typealias Cell: CustomCell

还是不行。

我的目标是继承 BaseListViewModel 喜欢

public class TestListViewModel : BaseListViewModel<TestCellViewModel, TestTableViewCell> { 
}

然后我可以在我的BaseViewController 中执行此操作

public class BaseViewController<T: ListViewModelProtocol>: UITableViewController  {
}

在某些子类ViewController 中这样做:

public class CustomViewController: BaseViewController<TestListViewModel>  {
}

这样CustomViewController 会“得到”TestCellViewModelTestTableViewCell(实际上是它的 BaseViewController)。

当然,这并没有像我预期的那样工作。我错过了什么?或者我必须在每个实现它或将它用作泛型类型的类中为ListViewModelProtocol 定义typealias?这意味着我必须在BaseListViewModel 类和BaseViewController 类中定义ListViewModelProtocolViewModelCell,但这不是那么通用,因为我只想将它们的基类型放在协议中,仅此而已.

或者也许我的方法有问题,我应该以不同的方式实施?

任何建议都是有用的。谢谢

编辑

我已经设法解决了这个问题,但我还有另一个问题。

    public class BaseViewController<T: ListViewModelProtocol>: UITableViewController  {
         var dataSource: T?
    }

这个数据源通过调用它自己的方法在 UITableViewDataSource 方法中使用(参见 ListViewModelProtocol 方法)。一切正常,但是当一些自定义控制器:

Controller: BaseViewController&lt;TestListViewModel&gt;

正在取消初始化我收到 EXC_BAD_ACCESS 错误。如果我把

deinit {
    self.dataSource = nil
}

它可以工作,但我想知道为什么我需要将它设置为 nil。

谢谢。

【问题讨论】:

    标签: swift generics protocols type-alias associated-types


    【解决方案1】:

    typealias 关键字的含义不止一种……

    // protocol can't be generic
    protocol P {
        // here typealias is just placeholder, alias
        // for some unknown type
        typealias A
        func foo(a:A)->String
    }
    
    // C is generic
    class C<T>:P {
        // here typealias define the associated type
        // in this example it is some generic type
        typealias A = T
        func foo(a: A) -> String {
            return String(a)
        }
    }
    
    let c1 = C<Int>()
    print(c1.foo(1))     // 1
    
    let c2 = C<Double>()
    print(c2.foo(1))     // 1.0
    
    // D is not generic!!!
    class D: C<Double> {}
    
    let d = D()
    print(d.foo(1)) // 1.0
    

    更新,回答讨论中的问题

    class Dummy {}
    
    protocol P {
        // here typealias is just placeholder, alias
        // for some inknown type
        typealias A : Dummy
        func foo(a:A)->String
    }
    
    // C is generic
    class C<T where T:Dummy>:P {
        // here typealias define the associated type
        // in this example it is some generic type
        typealias SomeType = T
        func foo(a: SomeType) -> String {
            return String(a)
        }
    }
    
    class D:Dummy {}
    let c = C<D>()
    print(c.foo(D())) // D
    

    // now next line doesn't compile
    let c1 = C<Int>() // error: 'C' requires that 'Int' inherit from 'Dummy'
    

    【讨论】:

    • 有没有办法限制 typealias A 成为 P 协议中某些东西的子类?
    【解决方案2】:

    如果您想实现具有关联类型的协议,您必须在通用实现中设置这些关联类型:

    public class BaseListViewModel<T, U> : ListViewModelProtocol  {
      typealias ViewModel = T
      typealias Cell = U
    
      // implement the methods as well
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-01-13
      • 1970-01-01
      • 2016-09-23
      • 1970-01-01
      • 1970-01-01
      • 2019-08-31
      • 1970-01-01
      相关资源
      最近更新 更多