协议关联类型typealias赋值编译错误

mes*_*ngr 7 protocols swift swift2

以下代码:

protocol SomeProtocol {
    typealias SomeType = Int // used typealias-assignment

    func someFunc(someVar: SomeType)
}

class SomeClass: SomeProtocol {
    func someFunc(someVar: SomeType) {
        print(someVar)
    }
}
Run Code Online (Sandbox Code Playgroud)

给出编译时错误:

使用未声明类型'SomeType'

例如typealias SomeType = Double,添加SomeClass可以解决错误.

问题是,虽然协议关联类型声明的typealias-assignment部分(可选btw)有什么意义?

Qby*_*yte 5

在这种情况下的分配Inttypealias因为它可以让你的贴合型重载等于没有赋值:

// this declaration is equal since you HAVE TO provide the type for SomeType
protocol SomeProtocol {
    typealias SomeType

    func someFunc(someVar: SomeType)
}
Run Code Online (Sandbox Code Playgroud)

这样的赋值提供了一个默认类型,SomeType它被实现覆盖SomeClass,但它对协议扩展特别有用:

protocol Returnable {
    typealias T = Int // T is by default of type Int
    func returnValue(value: T) -> T
}

extension Returnable {
    func returnValue(value: T) -> T {
        return value
    }
}

struct AStruct: Returnable {}

AStruct().returnValue(3) // default signature: Int -> Int
Run Code Online (Sandbox Code Playgroud)

只有通过符合协议而不指定类型,才能免费获得该功能T.如果要typealias T = String // or any other type在结构体中设置自己的类型.

有关提供的代码示例的一些其他说明

您解决了这个问题,因为您明确了该参数的类型.Swift还会推断您使用的类型:

class SomeClass: SomeProtocol {
    func someFunc(someVar: Double) {
        print(someVar)
    }
}
Run Code Online (Sandbox Code Playgroud)

因此SomeType推断该协议Double.

另一个例子,你可以SomeType在类声明中看到它没有引用协议:

class SomeClass: SomeProtocol {
    typealias Some = Int
    func someFunc(someVar: Some) {
        print(someVar)
    }
}

// check the type of SomeType of the protocol
// dynamicType returns the current type and SomeType is a property of it
SomeClass().dynamicType.SomeType.self // Int.Type
// SomeType gets inferred form the function signature
Run Code Online (Sandbox Code Playgroud)

但是,如果你这样做:

protocol SomeProtocol {
    typealias SomeType: SomeProtocol

    func someFunc(someVar: SomeType)
}
Run Code Online (Sandbox Code Playgroud)

SomeType必须是SomeProtocol可以用于更明确的抽象和更多静态代码的类型,而这:

protocol SomeProtocol {
    func someFunc(someVar: SomeProtocol)
}
Run Code Online (Sandbox Code Playgroud)

将动态调度.


fqd*_*qdn 5

协议中的" 关联类型 " 文档中有一些很好的信息.

它们在整个标准库中的使用非常丰富,例如参考SequenceType协议,它声明了一个typealiasfor Generator(并指定它符合GeneratorType).这允许协议声明引用该别名类型.

在你的情况下,你使用的地方typealias SomeType = Int,或许你的意思是" 我希望SomeType被约束为类似Integer的行为,因为我的协议方法将取决于那个约束 " - 在这种情况下,你可能想要typealias SomeType: IntegerType在你的协议中使用,然后在你的课堂上继续为那个符合的别名分配一个类型IntegerType.

UPDATE

在打开苹果公司的一个错误并对其进行了广泛的讨论后,我已经了解了基础问题的核心内容:

当符合协议时,您不能直接引用仅在该协议中声明的关联类型

(但请注意,在扩展协议时,相关类型可用,如您所料)

所以在你的初始代码示例中:

protocol SomeProtocol {
    typealias SomeType = Int
    func someFunc(someVar: SomeType)
}

class SomeClass: SomeProtocol {
    func someFunc(someVar: SomeType) {  // use of undeclared type "SomeType"
        print(someVar)
    }
}
Run Code Online (Sandbox Code Playgroud)

...错误重新:"使用未声明的类型"是正确的,您的类SomeClass没有声明类型SomeType

但是,扩展可以SomeProtocol访问关联类型,并且在提供实现时可以引用它:

(请注意,这需要使用where子句来定义关联类型的要求)

protocol SomeProtocol {
    typealias SomeType = Int
    func someFunc(someVar: SomeType)
}

extension SomeProtocol where SomeType == Int {
    func someFunc(someVar: SomeType) {
        print("1 + \(someVar) = \(1 + someVar)")
    }
}

class SomeClass: SomeProtocol {}

SomeClass().someFunc(3)  // => "1 + 3 = 4"
Run Code Online (Sandbox Code Playgroud)