【问题标题】:Can I use set placeholder type based on return value?我可以根据返回值使用设置占位符类型吗?
【发布时间】:2018-05-09 13:28:00
【问题描述】:

我想在协议中指定一个函数,它的返回值与当前实现类的类型匹配。

protocol MakesSelf {
    static func getInstance<T>() -> T
}

除非 T 被限制为与实现 MakesSelf 的人相同的类型,例如

class MyThing: MakesSelf {

    class func getInstance<T>() -> T {
        return MyThing()
    }

}

我的理解是编译器会根据返回值为 T 分配 MyThing 的类型,但我却收到了类型转换错误:Cannot convert return expression of type 'MyThing' to return type 'T'

【问题讨论】:

  • 最终目标是返回self,但在这里我只想知道编译器对T的约束。在我看来,即使编译器不推断T = MyThing.TypeT 也应该等价于AnyObjectMyThing() 符合。

标签: ios swift generics protocols swift-protocols


【解决方案1】:

您可以创建一个具有关联类型的协议,您的getInstance() 函数将返回该类型。在协议的扩展中创建该功能的默认实现。完整示例:

protocol Initializable {
    init()
}

protocol MakesSelf: Initializable {
    associatedtype Object: Initializable = Self
    static func getInstance() -> Object
}

extension MakesSelf {
    static func getInstance() -> Object {
        return Object()
    }
}

class MyThing: MakesSelf {
    required init() {}

    func printSelf() {
        print(MyThing.self)
        print(Object.self)
        // Both will print "MyThing" because Object is a typealias of MyThing class
    }
}

然后获取您的实例:

let instance = MyThing.getInstance()
print("\(instance.self)")
/* Will print: MyTestApp.MyThing */

如您所见,因为您已经在协议的扩展中给出了getInstance() 的默认实现,而这在符合类中是不需要的。在您的问题的上下文中,关联类型用作“占位符”。

【讨论】:

  • 这是一段漂亮的代码,但实际上 getInstance 应该返回一个对象而不是类型。我的问题是为什么 T != MyThing.Type 在实现中。
  • @OlivierButler 它正在返回一个实例(对象)。它打印“MyTestApp.MyThing”,因为我在打印语句中添加了.self
  • 谢谢!我想确认我理解正确; MakesSelf 定义中的 Self 是指协议。这意味着,当我们调用 MyThing.getInstance() 时,任何符合 MakesSelf 的东西都可以,但是您提供了一个直接引用 Object 的实现。这意味着我们必须调用 Object(),并且不能返回 MyThing() 或其他一些虚假值。当我们看到在任何特定情况下 Object() 所指的函数时,我们就得到了当前植入的初始化程序。让我困惑的是为什么 MyThing() 会返回任何东西,因为初始化程序是空的。
  • 如果没有可以实现它们的类、结构等,协议将毫无用处,这就是为什么我将Self 视为符合协议的对象的未来self。 Apple 文档确认:“Self 是指符合协议的最终类型”。关于您的最后一条语句 - 有一个空的初始化程序非常好,但是您需要根据 Initializable 要求将其设置为 required
  • 并且协议的associated 类型Object 成为符合对象中的typealias。所以MyClass.selfObject.self代表MyClass范围内的同一个类型。
【解决方案2】:

我面临的问题是 T 无法在 MyThing 的情况下推断出来。当涉及到错误消息时,编译器并不是很有帮助(有一次我被告知Cannot convert return expression of type 'T' (aka 'MyImplementation') to return type 'T'

解决方案是提供某种方式让编译器知道T 应该是什么(如果您希望它不受约束,请使用AnyObject,而不是T)。

在我的例子中,我想返回一个 Self 类型的值(实现的类型)。这比正常的 POP 问题更复杂,因为您不能在类/静态函数中引用 Self。

为了解决这个问题,我使用了 Swift 的 associatedtype,它允许我们设置一个命名的占位符以跨协议使用。由于我们在协议级别定义它并提供一个值,因此我们可以为实现者自己的类型设置一个命名引用。

associatedtype MyType = Self.Type 

注意 associatedtype MyType = Self 不起作用,因为 self 在这种情况下是协议,而不是最终实现的类型(我们还不知道,并且我们/编译器在某些对象实际实现协议之前不会知道)。

由于我们提供了一个值,所以当我们实现我们的协议时,类型已经受到限制!由于约束仅涉及实现的类型,因此实现协议足以定义类型(酷)。最好的部分是现在我可以在我的class func 定义中消除对 Self 的引用:

protocol MakesSelf {

    associatedtype MyType = Self.Type

    static func getInstance() -> MyType

}

class MyImplementation: MakesSelf {

    class func getInstance() -> MyImplementation {
        print("Hello")
        return MyImplementation()
    }

}

let myThing = MyImplementation.getInstance() 

在这里要超级清楚 - MyImplementation 类有一个名为 MyType 的类型别名,由协议指定。当我编译时,别名将引用当前的实现。现在,当我实现函数 getInstance 时,MyImplementation 符合 MyType。

注意,我必须以class func getInstance() -&gt; MyImplementation 的形式编写我的函数签名,而不是class func getInstance() -&gt; MyType。这是因为我正在返回函数调用 MyImplementation() 的结果,它恰好符合 MyType 的约束。这与说 MyImplementation 可以通过隐式转换为 MyType 不同。

关于尝试制作protocol function specification for returning self see here的好书,但我想发布具体的解决方案和解释。

【讨论】:

    猜你喜欢
    • 2021-01-31
    • 1970-01-01
    • 2011-11-17
    • 2015-10-03
    • 1970-01-01
    • 2015-01-26
    • 2015-10-21
    • 2014-10-10
    • 1970-01-01
    相关资源
    最近更新 更多