是否有关于 Swift protocol associatedtype using `=` vs `:` 的信息?
Is there information somewhere on Swift protocol associatedtype using `=` versus `:`?
我试图找出是否有关于 Swift 的协议 associatedtype
使用 =
而不是 :
的任何文档。
例如。
associatedtype Thing = SomeOtherThing
对比
associatedtype Thing: SomeOtherThing
不要与(可能让我感到困惑的)typealias Thing = SomeOtherThing
混淆,我相信它始终是 =
。
我试图抽象 class 的用法,其中包含一个委托,并且在 associatedtype
中使用 =
直到我 运行 进入变量的问题那是因为 =
类型没有暴露它的属性,而是需要通常的 :
,这在某种程度上对我来说是有道理的,但是当我改变一个特定的 =
到 :
它导致一切都崩溃了。我在下面提供了一个示例,其中的想法是能够自由 get/set 委托对象,但或多或少的一个协议说它的委托必须是关联类型的类型(使用 =
) 而不是 :
似乎暗示的只是“你必须遵守这一点”。
我也不知道我是否在这一步走得太远了,还有其他更好的方法可以在测试方面表达这一点。这似乎是必需的,因为我不能完全依赖外部对象在测试中按预期工作,而是需要故意模拟它以使其失败,在某些情况下也是如此。
import Foundation
// Concrete objects (eg external framework)
protocol ManagerDelegate: AnyObject {
func managerDidSomething(_ manager: Manager)
}
class Manager {
weak var delegate: ManagerDelegate?
func doSomething() {
delegate?.managerDidSomething(self)
}
}
// Custom objects using concrete objects, set up to be testable
class CustomManagerDelegate: ManagerDelegate {
func managerDidSomething(_ manager: Manager) {
print(#function)
}
}
class CustomClass<ManagerType: SomeManager> {
private(set) var doSomethingCustomCalled = false
private let managerDelegate: ManagerType.DelegateType
private let manager: ManagerType
init(manager: ManagerType, managerDelegate: ManagerType.DelegateType) {
self.manager = manager
self.managerDelegate = managerDelegate
manager.delegate = managerDelegate
}
func doSomethingCustom() {
doSomethingCustomCalled = true
manager.doSomething()
}
}
// Example creation of custom object
class Example {
static func createCustomObject() {
let customObject = CustomClass(
manager: Manager(),
// if `:` used instead of `=` for `SomeManager.DelegateType`, error is:
// Cannot convert value of type 'CustomManagerDelegate' to expected argument type 'Manager.DelegateType'
managerDelegate: CustomManagerDelegate() // error fix: add `as! Manager.DelegateType`
)
customObject.doSomethingCustom()
}
}
// Testable interface
protocol SomeManager: AnyObject {
// This `=` is the only thing keeping it together
associatedtype DelegateType = SomeManagerDelegate
// This doesn't work
//associatedtype DelegateType: SomeManagerDelegate
var delegate: DelegateType? { get set }
func doSomething()
}
protocol SomeManagerDelegate {
associatedtype ManagerType: SomeManager
func managerDidSomething(_ manager: ManagerType)
}
// Testable interface conformance
// if `:` used instead of `=` for `SomeManager.DelegateType`, error is:
// Type 'Manager' does not conform to protocol 'SomeManager'
extension Manager: SomeManager {
// already conforms
}
class MockManagerDelegate: SomeManagerDelegate {
typealias ManagerType = MockManager
func managerDidSomething(_ manager: ManagerType) {
print(#function)
}
}
class MockManager: SomeManager {
weak var delegate: MockManagerDelegate?
func doSomething() {
delegate?.managerDidSomething(self)
}
}
// Tests
class CustomClassTests {
func testCustomSomethingWasCalled() {
let mockInjectedCustomClass = CustomClass(
manager: MockManager(),
managerDelegate: MockManagerDelegate()
)
mockInjectedCustomClass.doSomethingCustom()
print("Was Called:", mockInjectedCustomClass.doSomethingCustomCalled)
assert(mockInjectedCustomClass.doSomethingCustomCalled)
}
}
CustomClassTests().testCustomSomethingWasCalled()
/* console:
managerDidSomething(_:)
Was Called: true
*/
=
和:
是关联类型声明的两个独立部分,而不是相互排斥的。这是 protocol associated type declaration:
的完整语法
attributes(opt)
access-level-modifier(opt)
'associatedtype'
typealias-name
type-inheritance-clause(opt)
typealias-assignment(opt)
generic-where-clause(opt)
: TypeName
部分是type-inheritance-clause
,= TypeName
是typealias-assignment
。
: TypeName
限制关联类型可以是什么类型,即必须inherit/conform到TypeName
。这就是 : SomeManagerDelegate
在您的情况下不起作用的原因。你说 SomeManager.DelegateType
一定是某种 SomeManagerDelegate
,但是对于 Manager
,这不是真的 - Manager.delegate
是 ManagerDelegate
类型,这是一个完全不相关的协议。就算是SomeManagerDelegate
也不行,因为.
= TypeName
设置关联类型的默认类型。如果编译器无法推断一致性的关联类型应该是什么类型,并且您也没有明确说明,它将使用该类型。但在你的情况下,这个事实并不重要。真正导致您的代码工作的不是 = SomeManagerDelegate
的 addition,而是约束 : SomeManagerDelegate
的 removal ].您不再限制关联类型应该是什么类型(它可以是任何类型!),因此对于 Manager
,关联类型现在可以推断为 ManagerDelegate
。请注意,您不必明确地说:
typealias DelegateType = ManagerDelegate
事实上,您可以完全删除 = SomeManagerDelegate
并只说:
associatedtype DelegateType
所以说 =
是“唯一保持在一起的东西”远非事实。
这个 = TypeName
语法看起来不像 very well documented. Here's a related Swift forums post。
我试图找出是否有关于 Swift 的协议 associatedtype
使用 =
而不是 :
的任何文档。
例如。
associatedtype Thing = SomeOtherThing
对比
associatedtype Thing: SomeOtherThing
不要与(可能让我感到困惑的)typealias Thing = SomeOtherThing
混淆,我相信它始终是 =
。
我试图抽象 class 的用法,其中包含一个委托,并且在 associatedtype
中使用 =
直到我 运行 进入变量的问题那是因为 =
类型没有暴露它的属性,而是需要通常的 :
,这在某种程度上对我来说是有道理的,但是当我改变一个特定的 =
到 :
它导致一切都崩溃了。我在下面提供了一个示例,其中的想法是能够自由 get/set 委托对象,但或多或少的一个协议说它的委托必须是关联类型的类型(使用 =
) 而不是 :
似乎暗示的只是“你必须遵守这一点”。
我也不知道我是否在这一步走得太远了,还有其他更好的方法可以在测试方面表达这一点。这似乎是必需的,因为我不能完全依赖外部对象在测试中按预期工作,而是需要故意模拟它以使其失败,在某些情况下也是如此。
import Foundation
// Concrete objects (eg external framework)
protocol ManagerDelegate: AnyObject {
func managerDidSomething(_ manager: Manager)
}
class Manager {
weak var delegate: ManagerDelegate?
func doSomething() {
delegate?.managerDidSomething(self)
}
}
// Custom objects using concrete objects, set up to be testable
class CustomManagerDelegate: ManagerDelegate {
func managerDidSomething(_ manager: Manager) {
print(#function)
}
}
class CustomClass<ManagerType: SomeManager> {
private(set) var doSomethingCustomCalled = false
private let managerDelegate: ManagerType.DelegateType
private let manager: ManagerType
init(manager: ManagerType, managerDelegate: ManagerType.DelegateType) {
self.manager = manager
self.managerDelegate = managerDelegate
manager.delegate = managerDelegate
}
func doSomethingCustom() {
doSomethingCustomCalled = true
manager.doSomething()
}
}
// Example creation of custom object
class Example {
static func createCustomObject() {
let customObject = CustomClass(
manager: Manager(),
// if `:` used instead of `=` for `SomeManager.DelegateType`, error is:
// Cannot convert value of type 'CustomManagerDelegate' to expected argument type 'Manager.DelegateType'
managerDelegate: CustomManagerDelegate() // error fix: add `as! Manager.DelegateType`
)
customObject.doSomethingCustom()
}
}
// Testable interface
protocol SomeManager: AnyObject {
// This `=` is the only thing keeping it together
associatedtype DelegateType = SomeManagerDelegate
// This doesn't work
//associatedtype DelegateType: SomeManagerDelegate
var delegate: DelegateType? { get set }
func doSomething()
}
protocol SomeManagerDelegate {
associatedtype ManagerType: SomeManager
func managerDidSomething(_ manager: ManagerType)
}
// Testable interface conformance
// if `:` used instead of `=` for `SomeManager.DelegateType`, error is:
// Type 'Manager' does not conform to protocol 'SomeManager'
extension Manager: SomeManager {
// already conforms
}
class MockManagerDelegate: SomeManagerDelegate {
typealias ManagerType = MockManager
func managerDidSomething(_ manager: ManagerType) {
print(#function)
}
}
class MockManager: SomeManager {
weak var delegate: MockManagerDelegate?
func doSomething() {
delegate?.managerDidSomething(self)
}
}
// Tests
class CustomClassTests {
func testCustomSomethingWasCalled() {
let mockInjectedCustomClass = CustomClass(
manager: MockManager(),
managerDelegate: MockManagerDelegate()
)
mockInjectedCustomClass.doSomethingCustom()
print("Was Called:", mockInjectedCustomClass.doSomethingCustomCalled)
assert(mockInjectedCustomClass.doSomethingCustomCalled)
}
}
CustomClassTests().testCustomSomethingWasCalled()
/* console:
managerDidSomething(_:)
Was Called: true
*/
=
和:
是关联类型声明的两个独立部分,而不是相互排斥的。这是 protocol associated type declaration:
attributes(opt)
access-level-modifier(opt)
'associatedtype'
typealias-name
type-inheritance-clause(opt)
typealias-assignment(opt)
generic-where-clause(opt)
: TypeName
部分是type-inheritance-clause
,= TypeName
是typealias-assignment
。
: TypeName
限制关联类型可以是什么类型,即必须inherit/conform到TypeName
。这就是 : SomeManagerDelegate
在您的情况下不起作用的原因。你说 SomeManager.DelegateType
一定是某种 SomeManagerDelegate
,但是对于 Manager
,这不是真的 - Manager.delegate
是 ManagerDelegate
类型,这是一个完全不相关的协议。就算是SomeManagerDelegate
也不行,因为
= TypeName
设置关联类型的默认类型。如果编译器无法推断一致性的关联类型应该是什么类型,并且您也没有明确说明,它将使用该类型。但在你的情况下,这个事实并不重要。真正导致您的代码工作的不是 = SomeManagerDelegate
的 addition,而是约束 : SomeManagerDelegate
的 removal ].您不再限制关联类型应该是什么类型(它可以是任何类型!),因此对于 Manager
,关联类型现在可以推断为 ManagerDelegate
。请注意,您不必明确地说:
typealias DelegateType = ManagerDelegate
事实上,您可以完全删除 = SomeManagerDelegate
并只说:
associatedtype DelegateType
所以说 =
是“唯一保持在一起的东西”远非事实。
这个 = TypeName
语法看起来不像 very well documented. Here's a related Swift forums post。