通用控制器内的通用Swift协议

时间:2016-01-18 16:27:37

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

是否可以在通用内部使用泛型?

我有这个协议

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)。

但当然这不符合我的预期。我错过了什么?或者我必须在实现它的每个类中为typealias定义ListViewModelProtocol或将其用作泛型类型?这意味着我必须在ViewModel类和Cell类中定义ListViewModelProtocol BaseListViewModel BaseViewController,但这不是那么通用,因为我只是想把它们的基本类型放在协议中就可以了。

或许我的方法可能有问题,我应该以不同的方式实现这一点?

任何建议都很有用。感谢

修改

我已设法解决这个问题,但我还有其他问题。

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

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

Controller: BaseViewController<TestListViewModel>

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

deinit {
    self.dataSource = nil
}

它有效,但我想知道为什么我需要将它设置为零。

感谢。

2 个答案:

答案 0 :(得分: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'

答案 1 :(得分:0)

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

public class BaseListViewModel<T, U> : ListViewModelProtocol  {
  typealias ViewModel = T
  typealias Cell = U

  // implement the methods as well
}