如何在Swift中向下转换/转换结构的泛型类型

jos*_*inm 7 objective-c ios swift

我已将UICollectionViewCells的工作重构为以下内容

struct CollectionViewCellModel<T: UICollectionViewCell> {
    let reuseIdentifier: NSString
    let allowsSelection: Bool

    // Optional callbacks
    var onCreate: ((T) -> Void)?            = nil
    var onSelection: ((T) -> Void)?         = nil
    var onWillBeDisplayed: ((T) -> Void)?   = nil
    var onDelete: ((T) -> Void)?            = nil

    // Create cell
    func toCell(collectionView: UICollectionView, indexPath: NSIndexPath) -> T {
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as T
        if let onCreate = onCreate { onCreate(cell) }
        return cell
    }
}
Run Code Online (Sandbox Code Playgroud)

这使我更容易创建特定单元格列表(对于表格之类的东西),然后使用它们.

但是,我一直在惹恼如何存储这些对象.我无法将它们转发给它CollectionViewCellModel<UICollectionViewCell>,因此无法存储列表[CollectionViewCellModel<UICollectionViewCell>

我认为Swift支持向下转换,但显然不适用于泛型类型?

let cell = CollectionViewCellModel<A>(...)
let genericCell = cell as CollectionViewCellModel<UICollectionViewCell>
// ERROR: UICollectionViewCell is not identical to A
let genericMaybeCell = cell as? CollectionViewCellModel<UICollectionViewCell>
// ERROR: UICollectionViewCell is not identical to A
Run Code Online (Sandbox Code Playgroud)

我是否必须将这些存储为一个数组,Any然后每次都将它们投射出来或者我只是误解某些(或两者)?


更新:我在操场上做了一些工作,以清楚地说明我的意思:

protocol IA {
    func name() -> String
}

class A:IA { func name() -> String { return "A (Base)" } }
class B: A { override func name() -> String { return "B (Child of A)" } }
class C: B { override func name() -> String { return "C (Child of B)" } }

struct SA<T: A> {
}

let struct0: Any = SA<B>()
// OK: yes, the struct of B is an Any! 
// but since B inherits from A, isn't it safe to 
// say that SA<B> is also of type SA<A>?
let struct1: SA<A> = SA<B>() as SA<A>
// NO
// ERROR: 'B' is not identical to 'A'
let struct1Optional: SA<A> = SA<B>() as? SA<A>
// not even optionally? NO
// ERROR: 'B' is not identical to 'A'
Run Code Online (Sandbox Code Playgroud)

我想这不可能.也许在Swift 1.3中.请参阅评论中的主题.

更新(2015年2月17日)


对于那些对我为什么一开始就这样做感兴趣的人,你必须了解我是如何使用我的CollectionViewControllers(CVC)的.我抽象了一个基础CVC来执行每个屏幕需要的常用方法.这个CVC有一个协议,期望Factory创建CVC模型.这些模型知道如何改变自己,对行动做出反应,并且非常像控制器.他们很胖,很活跃.另一方面,我的观点都是愚蠢的.他们所知道的只是在屏幕上移动东西或进入不同的显示状态.从CVC配置单元时,您最终会执行这些大的switch语句,这些语句实际上并没有从可读性角度告诉您"路由此".你开始在那里配置视图会变得更糟.这并不可怕,但一般来说 - 对我来说 - 视图控制器控制它负责的视图.虽然这个VC可能是单元的父VC,但这并没有给它适当的访问权来大量操作它.这现在让你的VC做的事情如下cell.changeDisplayToActiveState(); 简而言之,它现在承担着控制其子细胞的负担.这就是所有"肥胖"VC的贡献.我首先采用这种VIPER模式选择偏离这条道路,但我发现它有点过分 - 特别是对于一个新项目.我废弃它并开始使用基础CVC.这是我的项目目前的工作方式:

  • 基础CVC期望工厂和工厂期望CVC(从而促进单元的双向绑定)
  • 该工厂为THE CVC生产单元和割台模型.
  • 这些模型具有嵌入其中的回调和数据及配置功能.
  • 他们可以从这些回调中调用视图控制器的PUBLIC函数(因此严格地分离职责),例如vc.changeDisplayState(...)changeToVc(...)

Dav*_*ams 5

我不确定你到底发生了什么,但我会尽力回答.据推测,你对于存储CollectionViewCellModel<X>一些特定的东西并不感兴趣X,因为那样的答案是"如何存储它们?" 很简单:[CollectionViewCellModel<X>].所以我猜你想要一个CollectionViewCellModel<T>异构的数组T.实现这一目标的最好方法是给你所有的CollectionViewCellModel<T>共同协议(或基类,但在你的情况下,它们是struct,所以不是那样),大致是这样的:

protocol CollectionViewCellModelType {
  var reuseIdentifier: NSString {get}
  var allowsSelection: Bool {get}

  func toCell(
    collectionView: UICollectionView, indexPath: NSIndexPath
  ) -> UICollectionViewCell
}

struct CollectionViewCellModel<T: UICollectionViewCell>
  : CollectionViewCellModelType {
  let reuseIdentifier: NSString
  let allowsSelection: Bool

  // Optional callbacks
  var onCreate: ((T) -> Void)?            = nil
  var onSelection: ((T) -> Void)?         = nil
  var onWillBeDisplayed: ((T) -> Void)?   = nil
  var onDelete: ((T) -> Void)?            = nil

  // Create cell
  func toCell(
    collectionView: UICollectionView, indexPath: NSIndexPath
  ) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCellWithReuseIdentifier(
      reuseIdentifier, forIndexPath: indexPath) as! T

    if let onCreate = onCreate { onCreate(cell) }
    return cell
  }
}

var a: [CollectionViewCellModelType] = [] // put them in here
Run Code Online (Sandbox Code Playgroud)

希望这有帮助,

戴夫