是否有关于 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= TypeNametypealias-assignment

: TypeName限制关联类型可以是什么类型,即必须inherit/conform到TypeName。这就是 : SomeManagerDelegate 在您的情况下不起作用的原因。你说 SomeManager.DelegateType 一定是某种 SomeManagerDelegate,但是对于 Manager,这不是真的 - Manager.delegateManagerDelegate 类型,这是一个完全不相关的协议。就算是SomeManagerDelegate也不行,因为.

= TypeName 设置关联类型的默认类型。如果编译器无法推断一致性的关联类型应该是什么类型,并且您也没有明确说明,它将使用该类型。但在你的情况下,这个事实并不重要。真正导致您的代码工作的不是 = SomeManagerDelegateaddition,而是约束 : SomeManagerDelegateremoval ].您不再限制关联类型应该是什么类型(它可以是任何类型!),因此对于 Manager,关联类型现在可以推断为 ManagerDelegate。请注意,您不必明确地说:

typealias DelegateType = ManagerDelegate

事实上,您可以完全删除 = SomeManagerDelegate 并只说:

associatedtype DelegateType

所以说 = 是“唯一保持在一起的东西”远非事实。

这个 = TypeName 语法看起来不像 very well documented. Here's a related Swift forums post