【问题标题】:Swift 3: Is there a way to cast an object to a class and protocol at the same time?Swift 3:有没有办法同时将对象转换为类和协议?
【发布时间】:2017-09-26 23:01:39
【问题描述】:

我已经阅读了 Apple 的 Swift iBook(类型转换和协议)的相关部分,但我似乎找到了一种方法来指定对象是符合特定协议的特定类的实例。

作为tableView(_: , cellForRowAt: ) 中的示例,我想将tableView.dequeueReusableCell(withIdentifier: reuseID, for: indexPath) 返回的单元格转换为符合RLMEntityCapableCell 协议的UITableViewCell 的子类(只需指定conformers 有一个名为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 所做的那样。每个表格视图中使用的单元格将特定于要显示的实体,但数据源只知道该单元格将是符合RLMEntityCapableCellUITableViewCell 的子类。所有数据源需要担心的是告诉单元格它需要显示什么实例(它始终是Object 的子类),单元格会从那里获取它并根据需要配置自己。

【问题讨论】:

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


    【解决方案1】:

    不,这是不可能的……

    下一个 Swift 版本(第 4 版)可能会带来您正在寻找的东西,一个名为 Class and Subtype Existentials 的新功能:

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

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

    然后你可以说:

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

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


    使用上述类和子类型存在(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
    

    【讨论】:

    • 我感觉答案是“做不到”,但至少对未来有希望。不过,我并没有尝试添加超类要求,只是尝试为 Realm 构建一个真正通用的数据源。将使用的UITableViewCell 的特定子类不为数据源所知,仅知道它将是符合协议的子类。
    • @theMikeSwan 是的,一切都没有丢失,Swift 4 指日可待 :) 但是再次阅读您的(更新的)问题,感觉就像您在满足超类要求:..数据源只会知道该单元格将是符合RLMEntityCapableCellUITableViewCell子类。上述超类要求将是 UITableViewCell 类...
    • ...这样的要求可以指定为protocol RLMEntityCapableCell: UITableViewCell {...}。顺便说一句,我也很想拥有这个功能!
    【解决方案2】:

    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. 您至少需要执行以下操作之一:将item 设为可选、提供默认值或添加初始化函数。

    【讨论】:

    • 我正在制作一个通用数据源,因此该类不知道 UITableViewCell 的特定子类,只知道它将是一个符合协议的单元格。
    • @theMikeSwan 我假设当您想要访问 item 属性时,您将把它转换为您的协议。在这种情况下,只要符合协议,具体的子类是什么并不重要。
    • 我添加的编辑可能会阐明我的目标。我现在的双重铸造应该可以工作,为了清楚起见,我只想做一次所有的铸造。
    • @theMikeSwan 也许我误解了你的意思,但是“单元格将是符合UITableViewCellRLMEntityCapableCell 的子类”,这正是我的回答。跨度>
    • 除非它取决于类中的代码知道 which UITableViewCell 子类将被使用。因为我使用的是泛型,所以该类不知道单元格将是什么类,否则您的答案将正是需要的。如果这是 Objective-C,我会使用 UITableViewCell<RLMEntityCapableCell>,编译器会知道它将是符合 RLMEntityCapableCellUITableViewCell 的子类。
    猜你喜欢
    • 1970-01-01
    • 2022-06-15
    • 1970-01-01
    • 2021-07-10
    • 2020-09-13
    • 2011-12-10
    • 1970-01-01
    • 2015-02-13
    • 2023-03-17
    相关资源
    最近更新 更多