Swift 协议、关联类型、Self 和默认实现的问题



我正在尝试通过我无法确定的默认实现来获取一些功能。请考虑以下代码,它简化了我正在尝试执行的操作,但尽可能简单地捕获问题。

//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

基本上,我不能有一个返回Self并使用关联类型的方法。对我来说,总是连续调用这两种方法真的很讨厌。我只想为每个类声明configure(Data)并免费获得generateObjectAndConfigure(Data)

有什么建议吗?

通过使用Self,您有点过于复杂了。

您需要做的就是在Configurable协议中声明一个初始化器,该初始化器接受您的Dataassociatedtype作为参数,并具有非静态配置函数:

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子类的扩展来添加协议的一致性。您需要做的就是实现typealiasconfigure方法:

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的子类会继承typealias,这意味着编译器推断SelfData应该没有问题,但显然这还不受支持。

编辑:@Cristik对评论中的UICollectionView提出了一个很好的观点。

这个问题可以通过为SelfUICollectionViewConfigurable添加协议扩展来解决,使用适当的初始值设定项:

extension Configurable where Self: UICollectionView {
init(data: Data) {
self.init(frame: CGRect.zero, collectionViewLayout: UICollectionViewFlowLayout())
self.configure(data: data)
}
}

然后,当将一致性添加到ConfigurableforUICollectionView时,我们将Datatypealias设为UICollectionViewLayout

extension UICollectionView: Configurable {
typealias Data = UICollectionViewLayout
func configure(data: Data) {
collectionViewLayout = data
}
}

就个人而言,我认为对于init(frame:)初始值设定项不合适的类来说,这是一种合理的方法。

相关内容

  • 没有找到相关文章

最新更新