是否有关于 Swift 协议关联类型使用 `=` 与 `:` 的信息?

Ohi*_*end 5 generics unit-testing ios associated-types swift

我试图找出是否有关于 Swift 协议associatedtype使用 a=而不是:.

例如。 associatedtype Thing = SomeOtherThingassociatedtype Thing: SomeOtherThing

不要与我typealias Thing = SomeOtherThing始终=相信的(可能让我困惑的)混淆。

我试图抽象包含委托并使用=for associatedtypes 的类的用法,直到遇到一个问题,即该类型的变量由于 而没有公开其属性=,而是需要通常的:,这对我来说是从某种意义上说,但是当我将一个特定更改=为 a时:,它导致一切都崩溃了。我在下面提供了一个示例,其中的想法是能够自由获取/设置委托对象,但或多或​​少有一个协议说它的委托必须是关联类型的类型(带有 )而=不仅仅是正如似乎暗示的那样,“你必须遵守这一点” :

我也不知道我是否迈出了这一步太过分的一步,还有其他更好的方法可以在测试方面表达这一点。这似乎是必需的,因为我不能完全依赖外部对象在测试中按预期工作,而是需要模拟它以故意失败,在某些情况下如此。

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
 */
Run Code Online (Sandbox Code Playgroud)

Swe*_*per 5

the=和 the:是关联类型声明的两个独立部分,而不是相互排斥的。这是协议关联类型声明的完整语法:

attributes(opt) 
access-level-modifier(opt) 
'associatedtype' 
typealias-name 
type-inheritance-clause(opt) 
typealias-assignment(opt) 
generic-where-clause(opt)
Run Code Online (Sandbox Code Playgroud)

部分: TypeNametype-inheritance-clause,而= TypeNametypealias-assignment

: TypeName限制关联类型可以是什么类型,即它必须继承/符合TypeName. 这就是为什么: SomeManagerDelegate在你的情况下不起作用的原因。你说的SomeManager.DelegateType一定是某种类型的SomeManagerDelegate,但是对于 来说Manager,这不是真的 -Manager.delegate是类型ManagerDelegate,这是一个完全不相关的协议。即使是这样SomeManagerDelegate,它也不会起作用,因为协议本身并不符合协议

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

typealias DelegateType = ManagerDelegate
Run Code Online (Sandbox Code Playgroud)

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

associatedtype DelegateType
Run Code Online (Sandbox Code Playgroud)

=因此,“这是唯一将其保持在一起的东西”,这与事实相去甚远。

这种= TypeName语法似乎没有很好的记录。这是相关的Swift 论坛帖子