Swift5中Protocol扩展编译报错:无法用Self类型参数调用函数
解决Swift装饰器模式中的编译错误
错误根源分析
你遇到的Cannot invoke 'decorate' with an argument list of type '(view: Self)'错误,本质是编译器无法确认传入的ViewDecorator关联类型View和当前调用方法的Self(具体的UIView子类)是匹配的。
在你的原始代码里,decorate<T: ViewDecorator>这个泛型约束只要求T符合ViewDecorator协议,但T的关联类型View可以是任何实现了Decoratable的类型——编译器没有任何线索能确定这个View就是当前的Self,所以它拒绝把self传给decorator.decorate(view:)。
直接解决方案:添加类型匹配约束
我们只需要给泛型方法加上一个额外的类型约束,强制要求装饰器的关联类型View必须等于当前视图的类型Self,就能让编译器明确类型匹配关系。
修改后的完整代码如下:
protocol ViewDecorator { associatedtype View: Decoratable func decorate(view: View) } protocol Decoratable { // 新增约束:装饰器的View类型必须和当前类型一致 func decorate<T: ViewDecorator>(with decorator: T) where T.View == Self } extension Decoratable where Self: UIView { // 扩展实现同步添加相同约束 func decorate<T : ViewDecorator>(with decorator: T) where T.View == Self { decorator.decorate(view: self) } } class GreenView: UIView, Decoratable { } class GreenViewDecorator: ViewDecorator { typealias View = GreenView func decorate(view: GreenView) { view.backgroundColor = UIColor.green } } class ViewController: UIViewController { @IBOutlet var greenView: GreenView! override func viewDidLoad() { super.viewDidLoad() let decorator = GreenViewDecorator() greenView.decorate(with: decorator) } }
扩展:支持父类类型的装饰器
如果你的需求是让装饰器能作用于某一类视图(比如所有UIView子类),可以把约束改成Self: T.View,这样当前视图只要是装饰器View类型的子类就能使用它:
protocol Decoratable { // 修改约束:当前类型是装饰器View类型的子类 func decorate<T: ViewDecorator>(with decorator: T) where Self: T.View } extension Decoratable where Self: UIView { func decorate<T : ViewDecorator>(with decorator: T) where Self: T.View { decorator.decorate(view: self) } } // 可以作用于所有UIView子类的装饰器 class AllGreenViewDecorator: ViewDecorator { typealias View = UIView func decorate(view: UIView) { view.backgroundColor = UIColor.green } } // 使用示例 greenView.decorate(with: AllGreenViewDecorator()) // 现在可以正常调用
这样调整后,你既可以有针对特定视图的装饰器,也能有通用的装饰器,灵活性更高。
内容的提问来源于stack exchange,提问作者Ihar Katkavets
相关产品推荐
相关产品推荐

