Swift 3:有没有办法同时将对象强制转换为类和协议?

时间:2017-04-28 18:32:00

标签: ios swift swift3 swift-protocols existential-type

我已经阅读了Apple的Swift iBook(Type Casting& Protocols)的相关章节,但我似乎找到了一种方法来指定一个对象是一个符合特定类的实例特定的协议。

作为tableView(_: , cellForRowAt: )中的示例,我想将tableView.dequeueReusableCell(withIdentifier: reuseID, for: indexPath)返回的单元格转换为符合UITableViewCell协议的RLMEntityCapableCell的子类(仅指定conformer有一个名为item的变量,它是Object的实例,或者是其子类之一。)

这条路线有效,但双重演员似乎过分了:

protocol RLMEntityCapableCell: class  {
     var item: Object { get set }
}

 public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    var cell = tableView.dequeueReusableCell(withIdentifier: reuseID, for: indexPath) as! RLMEntityCapableCell // Cast here so we can set item
    cell.item = items[indexPath.row]
    return cell as! UITableViewCell // Cast again so the return type is right…
}

另一种方法:

var cell = tableView.dequeueReusableCell(withIdentifier: reuseID, for: indexPath) 
           as! RLMEntityCapableCell, UITableViewCell

给出了这个错误:

  

模式中缺少类型注释

显然也不是正确的方法。

我更希望指定为了符合协议,对象必须从UITableViewCellUICollectionViewCell继承,但协议的基础只能限于类类型而不是进一步

编辑:

这里的想法是为Realm对象提供一个通用数据源,利用泛型,就像ArrayDictionary一样。每个表视图中使用的单元格将特定于要显示的实体,但数据源只知道该单元格将是符合UITableViewCell的{​​{1}}的子类。所有数据源需要担心的是告诉单元格需要显示哪个实例(它始终是RLMEntityCapableCell的子类),单元格将从那里获取它并根据需要进行自我配置。

2 个答案:

答案 0 :(得分:4)

不,这不可能......

下一个Swift版本(版本4)可能会带来您正在寻找的内容,这是一项名为 Class and Subtype Existentials 的新功能:

  

通过允许Swift表示符合协议的类和子类型的存在,该提案为类型系统带来了更多的表达能力。

     

该提案保留现有的&语法,但允许其中一个元素为AnyObject或类类型(例如SomeClass & SomeProtocol)。

然后你可以说:

var cell = tableView.dequeueReusableCell(withIdentifier: reuseID, for: indexPath) 
           as! UITableViewCell & RLMEntityCapableCell

但是,当然,您无法使用此功能将超类要求添加到RLMEntityCapableCell协议中(如您最初所希望的那样)。我们可能需要等待Swift 5:)

使用上述 Class和Subtype Existentials (Swift 4)功能的其他一些示例:

protocol P {}
struct S {}
class C {}
class D : P {}
class E : C, P {}

let u: S & P // Compiler error: S is not of class type
let v: C & P = D() // Compiler error: D is not a subtype of C
let w: C & P = E() // Compiles successfully

protocol P {}
class C {}
class D : C { }
class E : C { }
class F : D, P { }

let t: C & D & P = F() // Okay: F is a subclass of D and conforms to P
let u: D & P = t       // Okay: D & P is equivalent to C & D & P
let v: C & D & P = u   // Okay: C & D & P is equivalent to D & P
let w: D & E & P       // Compiler error: D is not a subclass of E or vice-versa

答案 1 :(得分:-1)

UITableViewCell没有item属性,所以您可能要做的是创建符合您的协议的子类,然后将cell转换为这一点。

protocol RLMEntityCapableCell: class  {
    var item: Object { get set }
}

class RLMCell: UITableViewCell, RLMEntityCapableCell {
    var item: Object
}

 public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    var cell = tableView.dequeueReusableCell(withIdentifier: reuseID, for: indexPath) as? RLMCell ?? RLMCell() 
    cell.item = items[indexPath.row]
    return cell
}

注意:
1. dequeueReusableCell返回一个可选项,在某些情况下肯定会返回nil,所以不要强行打开它。
2.您至少需要执行以下操作之一:make item可选,提供默认值或添加初始化函数。