我有一个通用视图子类TintableView<T>: UIView
,该子类实现了具有相同关联类型TintStateComputing
的协议T
。 TintStateComputing
的受限扩展实现未调用;而是调用其不受限制的扩展实现。
TintStateComputing
协议具有一个函数computeTintState() -> T
,其功能听起来像:检查本地状态属性,并返回T
的相应实例。
我想在func computeTintState() -> T
的类型上写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)
答案 0 :(得分:1)
问题在于TintableView只有一种专业化,它是基于它自己的定义所知道的。编译该类时,它会考虑computeTintState()
,发现此时并未承诺TintState
就是ControlState
,因此可以使用更通用的版本进行编译。
为了做您想做的事情,当遇到TintableView<ControlState>
时,它需要完全重新考虑并重新编译TintableView
类。 Swift目前不这样做。在这种情况下,我不认为这是一个错误。我认为这段代码试图变得太神奇了,并且正在滥用扩展,但这只是我的看法。如果您认为Swift应该可以处理这种情况,那么我建议在bugs.swift.org中打开一个缺陷。
请记住,如果TintableView
在一个模块中而TintState == ControlState
扩展在另一个模块中(例如在带有let a =
的模块中),将会发生什么。在这种情况下,将不可能获得您所要求的行为,因为一个模块无法重新专门化另一个模块(它可能没有可用的源代码)。如果将这些代码放在一个模块中时以一种方式运行,但是如果它们在不同模块中时具有不同的可见行为,那么您认为此代码很好吗?这就是为什么我认为这太棘手且容易出错。这个拆分模块专门化问题一直在发生,但是它只会影响性能(并且stdlib使用私有编译器指令来改进它,因为这是特例)。