【问题标题】:Implement Swift protocol with Objective-C property getter使用 Objective-C 属性获取器实现 Swift 协议
【发布时间】:2016-12-16 14:11:47
【问题描述】:

我想为 Swift 中的所有高亮视图创建一个共同的祖先。我希望实现highlighted 属性的现有UIKit 类开箱即用,因此在阅读this answer 并检查the Objective-C getter is defined as isHighlighted 后,我将协议定义更改为:

@objc protocol Highlightable {
    var highlighted: Bool { @objc(isHighlighted) get set }
}

所以UILabelUIControl 的协议实现就这么简单:

extension UILabel: Highlightable {}
extension UIControl: Highlightable {}

这很好用,我可以从 Swift 访问 highlighted 属性并将其设置为 Highlightable 实例。然而,当我尝试在我的 Swift 类上实现协议时,即使是这样最简单的实现:

class HighlightableView: UIView, Highlightable {
    var highlighted: Bool = false
}

我得到这个编译错误:

getter 为 'highlighted' 提供的 Objective-C 方法 'highlighted' 与需求的选择器 ('isHighlighted') 不匹配

我可以让它工作的唯一方法是使用计算属性,但这不是我想要的。

class HighlightableView: UIView, Highlightable {
    var highlighted: Bool { @objc(isHighlighted) get { return true } set {} }
}

我的环境是 Xcode 8.0 和 Swift 3。更新到 XCode 8.2 并且错误仍然存​​在。


我目前的解决方法是完全避免 Objective-C 和 Swift 之间的任何命名冲突。但这远非理想:

@objc protocol Highlightable {
    var _highlighted: Bool { get set }
}

extension UILabel: Highlightable {
    var _highlighted: Bool {
        get { return isHighlighted }
        set { isHighlighted = newValue }
    }
}
extension UIControl: Highlightable {
    var _highlighted: Bool {
        get { return isHighlighted }
        set { isHighlighted = newValue }
    }
}
class HighlightableView: UIView, Highlightable {
    var _highlighted: Bool = false
}

【问题讨论】:

    标签: ios objective-c swift swift3


    【解决方案1】:

    您的UILabelUIControl 扩展满足您创建协议的方式,因为它们已经有一个名为highlighted 的属性,其getter 访问器方法是isHighlighted

    您的HighlightableView 不满足采用您的Highlightable 协议,因为您的getter 具有@objc(isHighlighted) 要求。

    您必须使用计算属性来满足这一点。但是,这意味着您还需要 highlighted 属性的后备存储。类似private var _highlighted = false

    在您的情况下,由于这是不可取的,您可以删除协议上的 @objc 属性。

    protocol Highlightable: class {
        var highlighted: Bool { get set }
    }
    
    extension UILabel: Highlightable { }
    extension UIControl: Highlightable { }
    
    class HighlightableView: UIView, Highlightable {
        var highlighted = false
    }
    
    let label = UILabel()
    label.isHighlighted // Prior to iOS 10, this property is called "highlighted"
    
    let view = HighlightableView()
    view.highlighted
    
    let highlightables: [Highlightable] = [ label, view ]
    
    for highlightable in highlightables {
        print(highlightable.highlighted)
    }
    
    // Prints:
    // false
    // false
    

    但是,属性名称在具体类型中是不一致的。

    这是另一种方法:

    @objc protocol Highlightable: class {
        var isHighlighted: Bool { @objc(isHighlighted)get @objc(setHighlighted:)set }
    }
    
    extension UILabel: Highlightable { }
    extension UIControl: Highlightable { }
    
    class HighlightableView: UIView, Highlightable {
        private var _isHighlighted = false
        var isHighlighted: Bool {
            @objc(isHighlighted) get {
                return _isHighlighted
            }
            @objc(setHighlighted:) set {
                _isHighlighted = newValue
            }
        }
    }
    
    let label = UILabel()
    label.isHighlighted = true
    
    let view = HighlightableView()
    view.isHighlighted
    
    let highlightables: [Highlightable] = [ label, view ]
    
    for highlightable in highlightables {
        print(highlightable.isHighlighted)
    }
    
    // Prints:
    // false
    // false
    

    这会在所有具体类型中公开一致的isHighlighted 属性,同时仍符合Highlightable。这里的缺点是@objc 属性在不需要的上下文中更为普遍。也就是说,@objc 属性没有用于向 Objective-C 代码公开 Swift 协议。

    编辑:

    查看适用于 Swift 的 iOS 10 API 差异(并在 Xcode 7.2 中进行了一些测试),UILabelUIControlisHighlighted 属性以前被命名为 highlighted。在链接 iOS SDK 9.3 或更低版本时使用上述代码将导致编译时错误。

    在第一个示例中,可以通过将 label.isHighlighted 行重命名为 label.highlighted 来修复这些错误。

    在第二个示例中,可以通过将所有 isHighlighted 实例重命名为 highlighted 来修复这些错误(@objc 属性括号内的实例除外)。

    9.3 到 iOS 10.0 API 差异:https://developer.apple.com/library/content/releasenotes/General/iOS10APIDiffs/index.html

    【讨论】:

    • 您是否测试过您的第一个样品?我无法编译它
    • @redent84 是的。它对我来说编译得很好(Xcode 8.2)。你能分享一下错误吗?
    • 它不适用于 XCode 8.0,但它适用于 XCode 8.2。
    • 针对 iOS 9.0,您的第一个解决方案在 XCode 8.2 上按预期工作
    • @redent84 感谢您的检查。您是否尝试将 Base SDK 设置为 iOS 9.0?我相信如果您只将部署目标设置为 iOS 9.x,仅此一项不会产生错误。相反,您的 Base SDK 必须是 iOS 9.x。我相信现在这只能通过使用 iOS 9.x SDK 附带的 Xcode 版本来实现。我已经更新了我的答案以反映这一点。
    【解决方案2】:

    我有一个解决方案,但它比你想要的要大一些,但我认为它会对你有所帮助。

    您应该执行 swift 协议并确定您需要的类的默认实现

    protocol Highlightable: class {
        var highlighted: Bool { get set }
    }
    
    extension Highlightable where Self: UILabel {
        var highlighted: Bool {
            get {
                return isHighlighted
            }
            set {
                isHighlighted = newValue
            }
        }
    }
    
    extension Highlightable where Self: UIControl {
        var highlighted: Bool {
            get {
                return isHighlighted
            }
            set {
                isHighlighted = newValue
            }
        }
    }
    

    所以你可以实现它

    extension UILabel: Highlightable {}
    extension UIControl: Highlightable {}
    
    class CustomView: UIView, Highlightable {
        var highlighted: Bool = false
    }
    

    希望对你有帮助。

    【讨论】:

      猜你喜欢
      • 2015-01-01
      • 1970-01-01
      • 2017-03-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-15
      相关资源
      最近更新 更多