【问题标题】:Swift/iOS: Generics Error: protocol requires nested type 'V'; do you want to add it?Swift/iOS:泛型错误:协议需要嵌套类型\'V\';你想添加它吗?
【发布时间】:2022-12-03 03:39:56
【问题描述】:

我正在使用 Playground,Swift 5.7.1 版。

我有两个协议和两个类。第一个很简单并且有效:

protocol TestSomeB: ObservableObject {
   associatedtype V: View
   func returnSome() -> V
}

class TestSomeBImp: TestSomeB {
   init() {}

   func returnSome() -> some View {
      Text("Hello World")
   }
}

let testSomeBImp = TestSomeBImp()
testSomeBImp.returnSome()

这有效并给了我价值{{SwiftUI.AnyTextStorage, {key "Hello World", hasFormatting false, []}, nil, nil}}

即使基本代码结构相同,第二个也不起作用:

struct TestModel {
   var title: String
}

struct TestView: View {
   var body: some View {
       Text("Hello, World!")
   }
}

// similar to protocol TestSomeB
protocol TestSomeA: ObservableObject {
    associatedtype V: View
    func linkBuilder<Content: View>(data: TestModel, @ViewBuilder content: () -> Content) -> V
}

class TestSomeAImp: TestSomeA {
   init() {}

   // returns `some View` similar to returnSome() method above
   func linkBuilder<Content: View>(data: TestModel, @ViewBuilder content: () -> Content) -> some View {
      NavigationLink(destination: routeToPage(data: data)) {
         content()
      }
   }

   private func routeToPage(data: TestModel) -> some View {
       TestView()
   }
}

let testSomeImp = TestSomeAImp()
testSomeImp.linkBuilder(
    data: TestModel(title: "Hello "),
    content: {
       Text("World!")
    }
)

可悲的是,这给了我错误:protocol requires nested type 'V'; do you want to add it? associatedtype V: View

  • 我需要返回some View,但我还需要抽象我的实现。
  • 我尝试在返回类型中使用 Content 而不是 V 但这也给我错误。
  • 我尝试在协议中仅使用 associatedtype V 而不指定类型,但这也给了我一个错误。
  • 我尝试创建两个关联类型,一个 V,另一个用于 Content,但是这实际上给了我相同的 nested 错误
  • 我尝试添加typealias V,但由于它是嵌套的,错误不断出现。

请指教。谢谢!

【问题讨论】:

    标签: ios swift generics swift-playground


    【解决方案1】:

    您的协议要求是您提供以下内容:

    func linkBuilder<Content: View>(data: TestModel, @ViewBuilder content: () -> Content) -> V
    

    给定一个Content,你保证总是返回一个V类型的值,这由协议的实现决定。对于给定的实现类型,只有一种类型 V

    然后你将它实现为:

    func linkBuilder<Content: View>(data: TestModel, @ViewBuilder content: () -> Content) -> some View {
    

    这表示 linkBuilder 将返回一个依赖于 Content 的不透明类型,它由呼叫者.每次调用 linkBuilder 都可以返回不同的特定类型,具体取决于 Content 是什么。这不匹配“将始终返回V,这是在编译时为此实现定义的特定具体类型。”

    在您的第一个示例中,返回的类型恰好是Text。在您失败的示例中,您的类型取决于 Content

    尝试摆脱所有的some 类型,它会更清楚发生了什么。 (some 类型必须始终可替换为编译时已知的类型。它是不透明的;它不是动态的。)

    func linkBuilder<Content: View>(data: TestModel, @ViewBuilder content: () -> Content)
        -> NavigationLink<Content, TestView> {
        NavigationLink(destination: routeToPage(data: data), label: content)
    }
    
    private func routeToPage(data: TestModel) -> TestView {
        TestView()
    }
    

    所以你当然可以为此制定一个协议,尽管我希望这不是你想要的:

    protocol TestSomeA: ObservableObject {
        func linkBuilder<Content: View>(data: TestModel, 
                                        @ViewBuilder content: () -> Content) 
            -> NavigationLink<Content, TestView>
    }
    

    这里要理解的另一件重要事情是NavigationLink&lt;Text, TestView&gt; 是与NavigationLink&lt;Image, TestViw&gt; 完全不同的类型。无法直接表达“具有任意参数的 NavigationLink”。

    这里并不清楚协议的意义何在。目前尚不清楚 TestSomeA 的另一种实现方式,以及调用者如何在不知道类型的情况下使用它。你的linkBuilder 没问题;只是加入了一个使它变得混乱的协议。这是要解决什么问题?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-12-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-03
      • 1970-01-01
      相关资源
      最近更新 更多