Swift 3: Is there a way to cast an object to a cla

2020-04-04 03:46发布

I've read through the relevant sections of Apple's Swift iBook (Type Casting & Protocols) but I can seem to find a way to specify that an object is an instance of a particular class that conforms to a specific protocol.

As an example in tableView(_: , cellForRowAt: ) I would like to cast the cell returned by tableView.dequeueReusableCell(withIdentifier: reuseID, for: indexPath) as being a subclass of UITableViewCell that conforms to the RLMEntityCapableCell protocol (Just specifies that conformers have a variable called item that is an instance of Object, or one of its subclasses).

This route works but the double casting seems excessive:

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…
}

This other approach:

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

gives this error:

type annotation missing in pattern

so clearly isn't the right way to do it either.

I would prefer to specify that in order to conform to the protocol an object has to inherit from either UITableViewCell or UICollectionViewCell but the base of a protocol can only be limited to the class type and no further.

Edit:

The idea here is to have a generic data source for Realm objects that leverages generics much like Array and Dictionary do. The cells used in each table view would be specific to the entity to be displayed but the data source would only know that the cell would be a subclass of UITableViewCell that conforms to RLMEntityCapableCell. All the data source needs to worry about is telling the cell what instance (that would always be a subclass of Object) it needs to display, the cell would take it from there and configure itself as needed.

2条回答
放荡不羁爱自由
2楼-- · 2020-04-04 04:23

UITableViewCell doesn't have an item property, so what you probably want to do is create a subclass of it that conforms to your protocol, and then cast cell as that.

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
}

Notes:
1. dequeueReusableCell returns an optional and will almost certainly return nil in some cases, so don't force unwrap it.
2. You'll need to do at least one of the following: make item optional, provide a default value, or add an initializer function.

查看更多
我只想做你的唯一
3楼-- · 2020-04-04 04:25

No, this isn't possible... yet.

The next Swift release (version 4) might bring what you are looking for, a new feature called Class and Subtype Existentials:

This proposal brings more expressive power to the type system by allowing Swift to represent existentials of classes and subtypes which conform to protocols.

The proposal keeps the existing & syntax but allows one of the elements to be either AnyObject or of class type (e.g., SomeClass & SomeProtocol).

You could then say:

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

But, of course, you won't be able to use this to add a superclass requirement to your RLMEntityCapableCell protocol (as you initially wished). We may need to wait for Swift 5 for that :)


Some other examples using the above Class and Subtype Existentials (Swift 4) feature:

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

and:

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
查看更多
登录 后发表回答