【问题标题】:Swift generics init: Non-nominal type 'T' does not support explicit initializationSwift 泛型初始化:非标称类型“T”不支持显式初始化
【发布时间】:2018-04-11 19:15:38
【问题描述】:

我在我的 iOS 应用程序中使用 Realm,由于线程安全问题,我选择为我的数据模型创建一个领域层,以及一个可以与领域对象相互转换的层。

我实际在做的事情要复杂得多,但我能够创建一个游乐场来展示我遇到的意外问题。

基本上我创建了一个名为 RealmObject 的协议。在我的实际应用中,这是 Realm.Object 类型可以遵循的协议,它需要所有必要的属性。

我只是在 Playground 中包含此协议的 UUID 部分。

//the realm layer
protocol RealmObject {
    var uuid: String { get }
}

接下来我有我的领域层模型。它们都有 UUID,但还有一些其他属性对应于它们的非 Realm 亲属。

struct NoteObject: RealmObject {
    let uuid: String
    let noteText: String
}
struct AppointmentObject: RealmObject {
    let uuid: String
    let time: Date
}

Cacheable 是可以保存在领域中的类型必须遵守的协议。 现在我刚刚包含了 UUID 要求,以及从 RealmObject 初始化的要求

//Cacheable
protocol Cacheable {
    associatedtype T: RealmObject
    init(object: T)
}
struct Note: Cacheable {

    let noteText: String
    let uuid: String
    init(object: NoteObject) {
        self.uuid = object.uuid
        self.noteText = object.noteText
    }
}
struct Appointment: Cacheable {
    let uuid: String
    let time: Date
    init(object: AppointmentObject) {
        self.uuid = object.uuid
        self.time = object.time
    }

}

最后是问题。在我的应用程序中,此功能的功能远不止于此,但这就像我可以做到的那样简单,并且仍然可以演示问题。 基本上, T 是必须是可缓存对象的泛型类型。 U 是我要转换为可缓存对象的 RealmObject。

每个 Cacheable 对象都有一个初始化器,它接受一个 RealmObject 对象

但是失败了

func getCacheable<T: Cacheable, U: RealmObject>(from realmObject: U) -> T {

    let thing = T(object: realmObject)
    ERROR: Non-nominal type 'T' does not support explicit initialization
    return thing
}

let noteObject = NoteObject(uuid: "bobalobla", noteText: "hi how are you")
let note: Note = getCacheable(from: noteObject)

let appointmentObject = AppointmentObject(uuid: "bobloblaw", time: Date())
let appointment: Appointment = getCacheable(from: appointmentObject)

我没有看到任何如此模棱两可的东西,编译器应该无法轻易弄清楚

用类型替换泛型应该很简单

一旦函数知道它使用的是哪个 Cacheable 类型,初始化器应该很容易被路由到正确的 init 方法。我不会假装理解实际发生的事情,但这似乎是与泛型有关的非常基本的事情,所以我认为我一定做错了什么。怎么回事?

【问题讨论】:

  • 我在这里可能完全错了,这就是我评论而不是回答的原因,但是用T.init(object: realmObject) 替换T(object: realmObject) 有效果吗?
  • @Connor Cannot invoke 'init' with an argument list of type '(object: U)' 是抛出的错误。值得注意的是,我还尝试将 T.Type 作为函数参数传入,所以基本上是 type: T.Type,然后是 type(object: realmObject),但这会在我的标题中引发完全相同的错误
  • U 不一定是T.T - 您需要将T.T 传递给Tinit(object:)(即用T.T 替换函数的U 参数)。

标签: swift generics


【解决方案1】:

类型系统从您的泛型列表&lt;T: Cacheable, U: RealmObject&gt; 中不知道URealmObject 的正确类型T(换句话说,您可以将Note 传递为TAppointmentObjectU)。您只需要将函数签名更新为:

// The `where T.T == U` is the important part. Because that is defined,
// we can also remove `U: RealmObject` because it's redundant information.
func getCacheable<T: Cacheable, U>(from realmObject: U) -> T where T.T == U {
    let thing = T(object: realmObject)
    return thing
}

// Alternatively, you can just do:
func getCacheable<T: Cacheable>(from realmObject: T.T) -> T {
    let thing = T(object: realmObject)
    return thing
}

【讨论】:

  • 也许func getCacheable&lt;T: Cacheable&gt;(from realmObject: T.T) 会更好?
  • @JulienPerrenoud 我没想到 - 同意,我会补充。
  • 这就是答案
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-11
  • 1970-01-01
相关资源
最近更新 更多