不调用约束协议扩展中的函数实现

斯坦

问题总结

我有一个通用的视图子类TintableView<T>: UIView,该子类实现TintStateComputing具有相同关联类型的协议TTintStateComputing的约束扩展实现未调用;而是调用其不受限制的扩展实现。

TintStateComputing协议具有一个功能computeTintState() -> T,它听起来像什么:检查本地状态属性,并返回的相应实例T

我想写func computeTintState() -> Ton的扩展实现TintStateComputing,受的类型限制T例如,使用一个ControlState枚举:

extension TintStateComputing where T == ControlState {
    func computeTintState() -> T {
        return self.isEnabled ? .enabled : T.default
    }
}

但是,为了完成协议的一致性,我认为我需要考虑的其他值T因此,我对进行了无限制的扩展TintStateComputing总是调用此不受约束的扩展实现,而不是受约束的实现。

extension TintStateComputing {
    func computeTintState() -> T {
        return _tintState ?? T.default
    }
}

游乐场试验台

import UIKit

// MARK: - Types

public enum ControlState: Int {
    case normal, enabled, highlighted, selected, disabled
}

public protocol Defaultable {
    static var `default`: Self { get }
}

extension ControlState: Defaultable {
    public static let `default`: ControlState = .normal
}

// MARK: - TintStateComputing declaration

public protocol TintStateComputing {
    associatedtype TintState: Hashable & Defaultable
    func computeTintState() -> TintState
    var _tintState: TintState? { get }
    var isEnabled: Bool { get }
}

// MARK: - TintableView declaration

class TintableView<T: Hashable & Defaultable>: UIView, TintStateComputing {
    // `typealias TintState = T` is implictly supplied by compiler
    var _tintState: T?
    var isEnabled: Bool = true { didSet { _tintState = nil }}

    var tintState: T  {
        get {
            guard _tintState == nil else {
                return _tintState!
            }
            return computeTintState()
        }
        set {
            _tintState = newValue
        }
    }
}

// MARK: - Unconstrained TintStateComputing extension

extension TintStateComputing {
    func computeTintState() -> TintState {
        return _tintState ?? TintState.default
    }
}

// MARK: - Constrained TintStateComputing extension

extension TintStateComputing where TintState == ControlState {
    func computeTintState() -> TintState {
        return self.isEnabled ? .enabled : TintState.default
    }
}

// MARK: - Test Case

let a = TintableView<ControlState>()
a.isEnabled = true
print("Computed tint state: \(a.tintState);  should be .enabled") // .normal
print("finished")

解决方法

我今天早上意识到,由于(至少到目前为止)我真正想完成的工作是处理isEnabled: Bool视图上的标志,因此我可以遵循与Defaultable定义默认“启用”情况相同的模式

public protocol Enableable {
    static var defaultEnabled: Self { get }
}

extension ControlState: Defaultable, Enableable {
    public static let `default`: ControlState = .normal
    public static let defaultEnabled: ControlState = .enabled
}

到那时,我真的可以消除该TintStateComputing协议了,并更新视图的tintState: T实现以直接考虑该标志。

var tintState: T  {
    get {
        guard _tintState == nil else { return _tintState! }
        return self.isEnabled ? T.defaultEnabled : T.default
    }
    set {
        _tintState = newValue
    }
}

它不像将实现放在受约束的扩展中那样笼统,但是现在可以使用。我认为,如果将来有带有多维着色状态类型的子类(例如,“启用” +“范围内”),我将可以通过进行寻址override

struct CalendarState: Equatable, Hashable, Defaultable, Enableable  {
    let x: Int

    static let `default`: CalendarState = CalendarState(x: 0)
    static let defaultEnabled: CalendarState = CalendarState(x: 1)
}

class ControlTintableView: TintableView<ControlState> {}
class CalendarView: TintableView<CalendarState> {}

let a = ControlTintableView()
a.isEnabled = true
print("ControlTintableView computed tint state: \(a.tintState);  should be: .enabled") // .enabled

let b = CalendarView()
b.isEnabled = true
print("CalendarView computed tint state: \(b.tintState);  should be: CalendarState(x: 1)") // CalendarState(x: 1)
罗布·纳皮尔

问题在于TintableView只有一种专业化,它是基于它自己的定义所知道的。在编译该类时,它认为computeTintState()TintState并认为此时未承诺的是ControlState,因此可以在更通用的版本中进行编译。

为了做您想做的事,遇到TintableView<ControlState>时,需要完全重新考虑并重新编译TintableView该类。Swift目前不这样做。在这种情况下,我不认为这是一个错误。我认为这段代码试图变得太神奇了,并且正在滥用扩展,但这只是我的看法。如果您认为Swift应该处理这种情况,那么我建议在bugs.swift.org中打开一个缺陷。

请记住,如果TintableView在一个模块中而TintState == ControlState扩展在另一个模块中(例如,使用的模块let a =,将会发生什么在那种情况下,将不可能获得您所要求的行为,因为一个模块无法重新专门化另一个模块(它可能没有可用的源代码)。如果将这些代码放在一个模块中时以一种方式运行,但是如果它们在不同模块中时具有不同的可见行为,那么您认为此代码很好吗?这就是为什么我认为这太棘手且容易出错。这个拆分模块专业化问题一直在发生,但是它只会影响性能(并且stdlib使用私有编译器指令来改进它,因为这是特例)。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

如何使用类型约束调用协议扩展默认实现

调用Swift协议扩展方法代替在子类中实现的方法

Swift-具有通用超类约束的扩展中的协议默认实现

协议扩展中的方法被调用而不是视图控制器中的方法实现

如何为扩展中的协议实现 CustomStringConvertible?

即使在XCTest文件中的类扩展中实现了方法后,总是会调用协议的默认实现

为什么不调用实现Runnable接口的类的构造函数?

继承树中的调用协议默认实现

Swift 协议扩展默认实现与类中的实际实现

PHP-类不调用扩展类的构造函数,而在其他类中可以正常工作

React不调用函数

不调用cellForItemAt函数

不调用UIButton函数

为什么Angular在我的ControlValueAccessor实现中不调用registerOnChange?

如何在协议函数中调用reloadData()

Swift协议扩展中是否允许混合的类/协议类型约束?

Swift中协议扩展的默认实现不起作用

为什么不调用协议功能

覆盖协议扩展默认实现

模式匹配在 F# 中不调用函数

Laravel 5.8 中的函数返回不调用 else 条件

在异步函数中不调用回调等效于什么?

在不调用图形的函数中返回颜色条

Node.js中的Promise.all不调用then函数

UseState 在反应中更新而不调用 set 函数

使类中的函数只能访问而不调用类

扩展构造函数中的调用函数

Swift:在协议扩展的静态变量中存储协议的实现类型

使用扩展在Swift中实现委托函数