Swift 协议、关联类型、Self 和默认实现方面的问题
Trouble with Swift Protocols, associatedtypes, Self and default implementations
我试图通过我无法确定的默认实现来获得一些功能。考虑以下代码,它是对我正在尝试做的事情的简化,但尽可能简单地捕获了问题。
//protocol definition
protocol Configurable {
associatedtype Data
func configure(data: Data)
static func generateObject() -> Self
}
//default implementation for any UIView
extension Configurable where Self: UIView {
static func generateObject() -> Self {
return Self()
}
}
//implement protocol for UILabels
extension UILabel: Configurable {
typealias Data = Int
func configure(data: Int) {
label.text = "\(data)"
}
}
//use the protocol
let label = UILabel.generateObject()
label.configure(data: 5)
print(label.text!) //5
我有一个协议,UIView 某些方法的默认实现,以及 UILabel 的特定实现。
我的问题是最后一部分...所有这些功能的实际使用
let label = UILabel.generateObject()
label.configure(data: 5)
print(label.text!) //5
我发现自己一直在做 generateObject()
,然后是 configure(data: <something>)
。所以我尝试执行以下操作:
将 static func generateObjectAndConfigure(data: Data) -> Self
添加到协议中。当我尝试为此方法为 UIView 创建默认实现时,问题就出现了。我收到以下错误
Method 'generateObjectAndConfigure(data:)' in non-final class 'UILabel' cannot be implemented in a protocol extension because it returns
自己and has associated type requirements
基本上,我不能有一个 returns Self
并使用关联类型的方法。老是连续调用这两个方法,感觉真的很讨厌。我只想为每个 class 声明 configure(Data)
并免费获得 generateObjectAndConfigure(Data)
。
有什么建议吗?
您使用 Self
.
让这个有点过于复杂了
您需要做的就是在您的 Configurable
协议中声明一个初始化程序,它接受您的 Data
associatedtype
作为参数,并具有非静态配置函数:
protocol Configurable {
associatedtype Data
init(data: Data)
func configure(data: Data)
}
在 Configurable
协议(对于 UIView
及其子 类)的扩展中提供该初始值设定项的默认实现:
extension Configurable where Self: UIView {
init(data: Data) {
self.init(frame: CGRect.zero)
self.configure(data: data)
}
}
最后,通过对您感兴趣的任何 UIView
sub类 的扩展来添加对协议的一致性。您在这里需要做的就是实现 typealias
和configure
方法:
extension UILabel: Configurable {
typealias Data = Int
func configure(data: Data) {
text = "\(data)"
}
}
extension UIImageView: Configurable {
typealias Data = String
func configure(data: Data) {
image = UIImage(named: data)
}
}
此实现有一个额外的好处,即您使用初始化程序创建视图(实例化对象的标准 Swift 模式),而不是静态方法:
let label = UILabel(data: 10)
let imageView = UIImageView(data: "screenshot")
我不太清楚为什么编译器不喜欢你的版本。我原以为 UILabel
的 sub类 会继承 typealias
,这意味着编译器在推断 Self
和 Data
时应该没有问题,但是显然这还不受支持。
编辑:@Cristik 在评论中提出了关于 UICollectionView
的一个很好的观点。
这个问题可以通过为 Configurable
添加协议扩展来解决,其中 Self
是 UICollectionView
,使用适当的初始值设定项:
extension Configurable where Self: UICollectionView {
init(data: Data) {
self.init(frame: CGRect.zero, collectionViewLayout: UICollectionViewFlowLayout())
self.configure(data: data)
}
}
然后,当为 UICollectionView
添加对 Configurable
的一致性时,我们将 Data
typealias
设为 UICollectionViewLayout
:
extension UICollectionView: Configurable {
typealias Data = UICollectionViewLayout
func configure(data: Data) {
collectionViewLayout = data
}
}
就我个人而言,我认为这是 类 的合理方法,其中 init(frame:)
初始值设定项不合适。
我试图通过我无法确定的默认实现来获得一些功能。考虑以下代码,它是对我正在尝试做的事情的简化,但尽可能简单地捕获了问题。
//protocol definition
protocol Configurable {
associatedtype Data
func configure(data: Data)
static func generateObject() -> Self
}
//default implementation for any UIView
extension Configurable where Self: UIView {
static func generateObject() -> Self {
return Self()
}
}
//implement protocol for UILabels
extension UILabel: Configurable {
typealias Data = Int
func configure(data: Int) {
label.text = "\(data)"
}
}
//use the protocol
let label = UILabel.generateObject()
label.configure(data: 5)
print(label.text!) //5
我有一个协议,UIView 某些方法的默认实现,以及 UILabel 的特定实现。
我的问题是最后一部分...所有这些功能的实际使用
let label = UILabel.generateObject()
label.configure(data: 5)
print(label.text!) //5
我发现自己一直在做 generateObject()
,然后是 configure(data: <something>)
。所以我尝试执行以下操作:
将 static func generateObjectAndConfigure(data: Data) -> Self
添加到协议中。当我尝试为此方法为 UIView 创建默认实现时,问题就出现了。我收到以下错误
Method 'generateObjectAndConfigure(data:)' in non-final class 'UILabel' cannot be implemented in a protocol extension because it returns
自己and has associated type requirements
基本上,我不能有一个 returns Self
并使用关联类型的方法。老是连续调用这两个方法,感觉真的很讨厌。我只想为每个 class 声明 configure(Data)
并免费获得 generateObjectAndConfigure(Data)
。
有什么建议吗?
您使用 Self
.
您需要做的就是在您的 Configurable
协议中声明一个初始化程序,它接受您的 Data
associatedtype
作为参数,并具有非静态配置函数:
protocol Configurable {
associatedtype Data
init(data: Data)
func configure(data: Data)
}
在 Configurable
协议(对于 UIView
及其子 类)的扩展中提供该初始值设定项的默认实现:
extension Configurable where Self: UIView {
init(data: Data) {
self.init(frame: CGRect.zero)
self.configure(data: data)
}
}
最后,通过对您感兴趣的任何 UIView
sub类 的扩展来添加对协议的一致性。您在这里需要做的就是实现 typealias
和configure
方法:
extension UILabel: Configurable {
typealias Data = Int
func configure(data: Data) {
text = "\(data)"
}
}
extension UIImageView: Configurable {
typealias Data = String
func configure(data: Data) {
image = UIImage(named: data)
}
}
此实现有一个额外的好处,即您使用初始化程序创建视图(实例化对象的标准 Swift 模式),而不是静态方法:
let label = UILabel(data: 10)
let imageView = UIImageView(data: "screenshot")
我不太清楚为什么编译器不喜欢你的版本。我原以为 UILabel
的 sub类 会继承 typealias
,这意味着编译器在推断 Self
和 Data
时应该没有问题,但是显然这还不受支持。
编辑:@Cristik 在评论中提出了关于 UICollectionView
的一个很好的观点。
这个问题可以通过为 Configurable
添加协议扩展来解决,其中 Self
是 UICollectionView
,使用适当的初始值设定项:
extension Configurable where Self: UICollectionView {
init(data: Data) {
self.init(frame: CGRect.zero, collectionViewLayout: UICollectionViewFlowLayout())
self.configure(data: data)
}
}
然后,当为 UICollectionView
添加对 Configurable
的一致性时,我们将 Data
typealias
设为 UICollectionViewLayout
:
extension UICollectionView: Configurable {
typealias Data = UICollectionViewLayout
func configure(data: Data) {
collectionViewLayout = data
}
}
就我个人而言,我认为这是 类 的合理方法,其中 init(frame:)
初始值设定项不合适。