隐式解包可选项协议符合性

9

我想创建一个Swift协议,可以应用于UILabelUITextFieldUITextView,并且包含它们的textattributedTextfont属性。

然而,这三个类在使用这些属性时是否使用可选类型或隐式解包可选类型方面存在不一致性。

例如,如果我创建了这个协议:

protocol MyProtocol: class {
    var font: UIFont? { get set }
}

我来应用它:

extension UILabel: MyProtocol { }
extension UITextField: MyProtocol { }
extension UITextView: MyProtocol { }

UITextFieldUITextView 的使用正常,但因为 UILabelfont 属性是 UIFont!,所以编译器报告说 UILabel 不符合 MyProtocol 的协议。

另外,UILabelUITextFieldtextattributedText 属性是可选的(String?),而这些属性对于 UITextView 来说则是隐式解包的可选项(String!)。所以对于这三个属性来说,使用的可选项和隐式解包的可选项并不一致。

因此,我不得不在协议中将 font 重命名为例如 uiFont,并在上述每个扩展中实现以下别名 font

extension UILabel: MyProtocol {
    var uiFont: UIFont? {
        get { font }
        set { font = newValue }
    }
} 
// … and similarly for UITextField and UITextView

这有点令人恼火,因为它减少了协议的简洁性。

我在Swift论坛上发现了这篇帖子,似乎是同样的问题,讨论似乎表示Swift 4.2不应该这样行事,但我正在使用Swift 5,仍然遇到这个问题。甚至有一个提案废除IUOs已经被合并

请注意,我正在macOS Catalina 10.15.6 (19G2021)上使用Xcode 11.7和iOS 13.7。

有没有办法完全避免这个问题,或者使代码更加简洁,以便我不需要有那么多冗余代码呢?

谢谢


5
很好的总结。我建议提交一个错误报告。我们一直被告知隐式解包可选类型只是“可选类型”,那么为什么它会成为你的阻碍呢?(而且为什么在API中还会留下任何隐式解包可选类型呢?) - matt
1
https://bugs.swift.org/browse/SR-13501 - shim
看起来这并不是一个真正的 bug(请参见 swift.org 上的 bug)。 - shim
1个回答

4
尽管这看起来像是Swift的一个bug,你可以扩展协议本身以使其正常工作:
extension UILabel: MyProtocol { }
extension MyProtocol where Self: UILabel {
    var font: UIFont? {
        get { self.font ?? nil }
        set { self.font = newValue }
    }
}

1
太好了!这大大减少了绕过代码的量,因为现在我只需要为 UILabelUITextView 添加扩展即可。没想到你可以在使用相同属性名称而不产生循环引用的情况下这样做。 - shim
然而,Swift错误跟踪器上的评论者指出,这对于可读性/调试有点不利。当您在隐式为UILabel或显式为UILabelMyProtocol实例上调用font时,不太清楚您是访问此font的别名还是实际属性。在这种情况下,它有点无害,但使用这种方法时可能值得小心。 - shim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接