简体   繁体   English

`Identifiable` 一致性返回类型泛型作为变量

[英]`Identifiable` conformance return type generic as variable

Background:背景:

I'm trying to build an OutlineGroup from this example from Apple: https://developer.apple.com/documentation/swiftui/outlinegroup我正在尝试从 Apple 的此示例构建OutlineGrouphttps://developer.apple.com/documentation/swiftui/outlinegroup

But instead of using static types I want to use objects that conform to my protocol.但是,我不想使用 static 类型,而是想使用符合我的协议的对象。 So the question is more general if that's possible in swift what I'm trying to do:因此,如果在 swift 中可行的话,这个问题就更笼统了:

Problem:问题:

I have protocol that gives me people coworkers and name from various objects:我有协议,可以为我提供来自各种对象的coworkersname

protocol CastProtocol {
    var name: String { get }
    var coworkers: [CastProtocol]? { get } // here will be the build error later
}

And consider two models as example:并以两个模型为例:

class Actor: Identifiable, CastProtocol {
    var id: Self { self }
    var name: String { return "Chuck Norris" }
    
    var coworkers: [CastProtocol]? {
        return [
            Director()
        ]
    }
}

class Director: Identifiable, CastProtocol {
    var id: Self { self }
    var name: String { return "Gina Carey" }
    
    var coworkers: [CastProtocol]? {
        return [
            Actor(),
            Director(),
            Actor()
        ]
    }
}

So till now everything working, but I need to ensure that my CastProtocol conforms to Identifiable , so changing my protocol implementation to:所以到目前为止一切正常,但我需要确保我的CastProtocol符合Identifiable ,因此将我的协议实现更改为:

protocol CastProtocol: Identifiable {
    ...
}

And now this creates a problem:现在这产生了一个问题:

Protocol 'CastProtocol' can only be used as a generic constraint because it has Self or associated type requirements协议“CastProtocol”只能用作通用约束,因为它具有 Self 或关联的类型要求

Questions:问题:

  1. So there is a way to do this with return not static type in coworkers or maybe I should go with subclassing?所以有一种方法可以在coworkers中返回不是 static 类型,或者我应该 go 子类化?
  2. Is that a correct approach?这是一个正确的方法吗? Maybe the only thing I'm missing is declare in protocol level identifier requirements for identifiable?也许我唯一缺少的是在可识别的协议级别标识符要求中声明? But if so - how to do it?但如果是这样 - 怎么做?
  3. I cannot really go with associatedType I think.我认为我不能真正使用 associatedType 的 go。 If coworkers in this example can contain Actor or Director associatedType I think cannot be use如果此示例中的coworkers可以包含ActorDirector associatedType 我认为不能使用

Protocols never conform to other protocols.协议从不符合其他协议。 The syntax protocol CastProtocol: Identifiable does not mean "CastProtocol conforms to Identifiable."语法protocol CastProtocol: Identifiable并不意味着“CastProtocol 符合 Identifiable”。 It means "in order to conform to CastProtocol, a type must also conform to Identifiable."它的意思是“为了符合 CastProtocol,一个类型也必须符合 Identifiable”。

To the question, though, no, you cannot require that.但是,对于这个问题,不,您不能要求这样做。 Identifiable includes an associated type ID . Identifiable包括一个关联的类型ID Consider this code:考虑这段代码:

let coworker in coworkers {
    let id = coworker.id // What type is `id` here?
    ...
}

Swift cannot assign a single type type id in this case.在这种情况下,Swift 无法分配单个类型类型id Someday they may add a feature called a Generalized Existential that would allow id to be inferred to be the type Any where Self: Hashable , but it's not currently possible to express that in Swift.有一天,他们可能会添加一个名为 Generalized Existential 的功能,该功能允许将id推断为Any where Self: Hashable类型,但目前无法在 Swift 中表达。

But in your case, it couldn't work anyway.但在你的情况下,它无论如何都无法工作。 Say you could do this.说你可以做到这一点。 Since you've made the identifier be the object itself, eventually the system would need to evaluate Director == Actor .由于您已将标识符设置为 object 本身,因此系统最终需要评估Director == Actor That's not possible in Swift.这在 Swift 中是不可能的。 Those aren't the same type.那些不是同一类型。

Instead, I suspect you want this, if you really want Cast members to be classes:相反,如果您真的希望 Cast 成员成为类,我怀疑您想要这个:

protocol CastMember: class {
    var name: String { get }
    var coworkers: [CastMember] { get } // Optional Arrays are almost always wrong
    var id: ObjectIdentifer { get }
}

extension CastMember {
    var id: ObjectIdentifier { ObjectIdentifier(self) }
}

// You can still put Identifiable here for [Actor] properties
class Actor: CastProtocol, Identifiable {
    // ...
}

Now, this won't let you put these directly into a ForEach(actor.coworkers) call, which I assume is what you want.现在,这不会让您将这些直接放入ForEach(actor.coworkers)调用中,我认为这是您想要的。 Personally, I'd just pass the id: \.id parameter, but you can of course make it a little nicer with an extension if you do this a lot:就个人而言,我只是传递id: \.id参数,但如果你经常这样做,你当然可以通过扩展使它更好一点:

extension ForEach where Content : View, Data.Element == CastMember {
    public init(_ data: Data, @ViewBuilder content: @escaping (Data.Element) -> Content) {
        self.init(data, id: \.id, content: content)
    }
}

Note the Data.Element == CastMember not : .注意Data.Element == CastMember不是: This applies when it's an array of CastMember, not an array of things that conform to CastMember.这适用于 CastMember 数组,而不是符合 CastMember 的事物数组。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM