我正在尝试通过我无法确定的默认实现来获取一些功能。请考虑以下代码,它简化了我正在尝试执行的操作,但尽可能简单地捕获问题。
//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
协议中声明一个初始化器,该初始化器接受您的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
子类的扩展来添加协议的一致性。您需要做的就是实现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
的子类会继承typealias
,这意味着编译器推断Self
和Data
应该没有问题,但显然这还不受支持。
编辑:@Cristik对评论中的UICollectionView
提出了一个很好的观点。
这个问题可以通过为Self
UICollectionView
Configurable
添加协议扩展来解决,使用适当的初始值设定项:
extension Configurable where Self: UICollectionView {
init(data: Data) {
self.init(frame: CGRect.zero, collectionViewLayout: UICollectionViewFlowLayout())
self.configure(data: data)
}
}
然后,当将一致性添加到Configurable
forUICollectionView
时,我们将Data
typealias
设为UICollectionViewLayout
:
extension UICollectionView: Configurable {
typealias Data = UICollectionViewLayout
func configure(data: Data) {
collectionViewLayout = data
}
}
就个人而言,我认为对于init(frame:)
初始值设定项不合适的类来说,这是一种合理的方法。