对于使用“some”指令返回的 var,类型特定方法不可用

Type specific method is unavailable for a var returned with `some` directive

考虑工厂方法模式实现:

import UIKit

protocol TransportProtocol: CustomStringConvertible {
    func techReview()
}
// Make default implemetation opposed to @objc optional methods and properties
extension TransportProtocol {
    func techReview() {
        print("Reviewing \(type(of: self))")
    }
    var description: String {
        "This is a \(type(of: self))"
    }
}

final class Car: TransportProtocol {
    func changeOil() {
        print("Changed oil")
    }
}

final class Ship: TransportProtocol {
}


protocol LogisticProtocol {
    associatedtype Transport: TransportProtocol
    func createTransport() -> Transport
    func delivery(from A: Any, to B: Any)
    func techRewiew(for transport: TransportProtocol)
}

extension LogisticProtocol {
    
    func delivery(from A: Any, to B: Any) {
        print("Moving \(type(of: self)) from \(A) to \(B)")
    }
    
    func techRewiew(for transport: TransportProtocol) {
        transport.techReview()
    }
}

final class RoadLogistics: LogisticProtocol {
    
    func createTransport() -> some TransportProtocol {
        Car()
    }
    
}

final class SeaLogistics: LogisticProtocol {
    
    func createTransport() -> some TransportProtocol {
        Ship()
    }
}

// Usage:

class Client {
    // ...
    static func someClientCode<L: LogisticProtocol>(creator: L) -> some TransportProtocol {
        let transport = creator.createTransport()
        print("I'm not aware of the creator's type, but it still works.\n"
              + transport.description + "\n")
        creator.delivery(from: "Source", to: "Destination")
        return transport
    }
    // ...
}

              
let someTransport = Client.someClientCode(creator: RoadLogistics())
type(of: someTransport.self) // Car
someTransport.changeOil() // Error: Value of type 'some TransportProtocol' has no member 'changeOil'

问题是如果编译器知道它是 Car 而不仅仅是 TransportProtocol,为什么我不能在 someTransport 上调用 changeOil()。 我们可以从使用 some 指令中获得什么好处,而不仅仅是裸协议类型?

方法的return类型是some TransportProtocol,不是Car。因此,即使 returned 实例的类型为 Car,您也不能调用任何仅存在于 Car 上但不存在于 TransportProtocol.[=25= 上的方法]

运行时知道someTransport的具体类型是Car,但是编译器只知道return 类型符合 TransportProtocol.

如果您希望能够访问 someTransport 上的 Car 方法,您需要将其向下转换为 Car.

if let car = someTransport as? Car {
    car.changeOil()
}

使用 some 关键字在用于不透明 returns 类型时有 type-system 好处(作为 Swift 5.1 的一部分在 SE-0244 中引入),因为它实际上启用了 returning 具有关联类型的协议,而不必显式地使该方法通用。这就是驱动 SwiftUI 的原因,因为它使您能够 return some View.

另一方面,对没有关联类型的协议使用不透明 return 类型没有任何好处,因此在您的特定示例中,没有理由使用它。