Swift 3:有没有办法同时将对象转换为 class 和协议?
Swift 3: Is there a way to cast an object to a class and protocol at the same time?
我已经通读了 Apple 的 Swift iBook(类型转换和协议)的相关部分,但我似乎可以找到一种方法来指定对象是特定对象的实例 class 符合特定协议。
作为 tableView(_: , cellForRowAt: )
中的示例,我想将 tableView.dequeueReusableCell(withIdentifier: reuseID, for: indexPath)
返回的单元格转换为符合 [=17] 的 UITableViewCell
的子 class =] 协议(仅指定符合者有一个名为 item
的变量,它是 Object
或其子 class 之一的实例)。
这条路线可行,但 双重铸造 似乎过多:
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
给出这个错误:
type annotation missing in pattern
显然这也不是正确的方法。
我更愿意指定,为了符合协议,对象必须从 UITableViewCell
或 UICollectionViewCell
继承,但协议的基础只能限于 class 输入,不再输入。
编辑:
这里的想法是为 Realm 对象提供一个通用数据源,它像 Array
和 Dictionary
一样利用泛型。每个 table 视图中使用的单元格将特定于要显示的实体,但数据源只会知道该单元格将是 UITableViewCell
的子 class,符合 RLMEntityCapableCell
。所有数据源需要担心的是告诉单元格它需要显示什么实例(总是 Object
的子 class),单元格将从那里获取它并根据需要配置自己.
不,这不可能...还。
下一个 Swift 版本(第 4 版)可能会带来您正在寻找的东西,一个名为 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
).
然后你可以说:
var cell = tableView.dequeueReusableCell(withIdentifier: reuseID, for: indexPath)
as! UITableViewCell & RLMEntityCapableCell
但是,当然,您将无法使用它向您的 RLMEntityCapableCell
协议添加 超类要求(正如您最初希望的那样)。我们可能需要等待 Swift 5 :)
使用上述 Class 和子类型存在 (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
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
return 是可选的,在某些情况下几乎肯定会 return nil,所以不要强行打开它。
2. 您至少需要执行以下操作之一:使 item
可选、提供默认值或添加初始化函数。
我已经通读了 Apple 的 Swift iBook(类型转换和协议)的相关部分,但我似乎可以找到一种方法来指定对象是特定对象的实例 class 符合特定协议。
作为 tableView(_: , cellForRowAt: )
中的示例,我想将 tableView.dequeueReusableCell(withIdentifier: reuseID, for: indexPath)
返回的单元格转换为符合 [=17] 的 UITableViewCell
的子 class =] 协议(仅指定符合者有一个名为 item
的变量,它是 Object
或其子 class 之一的实例)。
这条路线可行,但 双重铸造 似乎过多:
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
给出这个错误:
type annotation missing in pattern
显然这也不是正确的方法。
我更愿意指定,为了符合协议,对象必须从 UITableViewCell
或 UICollectionViewCell
继承,但协议的基础只能限于 class 输入,不再输入。
编辑:
这里的想法是为 Realm 对象提供一个通用数据源,它像 Array
和 Dictionary
一样利用泛型。每个 table 视图中使用的单元格将特定于要显示的实体,但数据源只会知道该单元格将是 UITableViewCell
的子 class,符合 RLMEntityCapableCell
。所有数据源需要担心的是告诉单元格它需要显示什么实例(总是 Object
的子 class),单元格将从那里获取它并根据需要配置自己.
不,这不可能...还。
下一个 Swift 版本(第 4 版)可能会带来您正在寻找的东西,一个名为 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 eitherAnyObject
or of class type (e.g.,SomeClass & SomeProtocol
).
然后你可以说:
var cell = tableView.dequeueReusableCell(withIdentifier: reuseID, for: indexPath)
as! UITableViewCell & RLMEntityCapableCell
但是,当然,您将无法使用它向您的 RLMEntityCapableCell
协议添加 超类要求(正如您最初希望的那样)。我们可能需要等待 Swift 5 :)
使用上述 Class 和子类型存在 (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
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
return 是可选的,在某些情况下几乎肯定会 return nil,所以不要强行打开它。
2. 您至少需要执行以下操作之一:使 item
可选、提供默认值或添加初始化函数。