由于SwiftUI中的递归函数调用而导致编译器错误

f87krz0w  于 2022-12-28  发布在  Swift
关注(0)|答案(1)|浏览(97)

我正在尝试为我钟爱的项目构建一个简单的设计系统,以便在Android和iOS应用程序之间共享。小部件的定义在公共Kotlin模块中。我正在尝试编写快速的UI函数来构建实际视图:

@ViewBuilder
    func widget(widget: MyWidget) -> some View {
        switch widget {
        case let box1 as MyBox:
            box(box: box1)
        default:
            fatalError("Unsupported widget: \(widget)")
        }
    }
@ViewBuilder
    func box(box: MyBox) -> some View {
        ZStack {
            ForEach(box.children) { child in
                widget(widget: child)
            }
        }
    }

当编译项目时,我得到了以下错误:
命令CompileSwift失败,退出代码为非零
删除递归(就像用Text替换widget调用)可以解决编译问题。
请注意,这些函数不会在其他任何地方调用。
XCode版本14.1

  • 删除递归调用可修复编译器错误
col17t5w

col17t5w1#

Swift中的结构体必须有一个在编译时已知的大小。换句话说,一个结构体不能包含一个可变大小的对象。一个结构体 * 可以 * 包含一个数组或字符串或其他可变大小的对象,方法是使用一个常量大小的引用(本质上是一个指针)。你 * 不能 * 这样做:

struct A {
  var b: B
}

struct B {
  var a: A
}

但是您可以这样做,因为_a_b实际上都只是指向数组的指针(即,无论数组或数组中的对象大小如何,大小都是恒定的),而A.bB.a是实际上没有存储的计算属性。

struct A {
  var _b: [B]

  var b: B {
    get {
      return _b[0]
    }
    set(new) {
      _b[0] = new
    }
  }
}

struct B {
  var _a: [A]

  var a: A? {
    get {
      return _a[0]
    }
    set(new) {
      _a[0] = new
    }
  }
}

你也可以这样做,因为存储一个Box<T>值只存储一个指向Box<T>对象的指针,所以引用的大小不依赖于被引用值的大小。

class Box<T> {
  var value: T

  init(value: T) {
    self.value = value
  }
}

struct A {
  var b: Box<B>
}

struct B {
  var a: Box<A>
}

由于SwiftUI视图是结构体,它们 * 不 * 是递归的。在Swift中有多种创建递归数据结构的方法,我对SwiftUI的了解不多,无法说出哪种方法适合您的用例。您可以通过 Package 器类或 Package 在数组中将内容装箱,或者您可以通过indirect关键字创建一个递归Enum box对象:

enum Maybe<T> {
  case None
  indirect case Some(_ value: T)
}

struct A {
  var b: Maybe<B>
}

struct B {
  var a: Maybe<A>
}

let foo = A(b: .None)
let bar = B(a: .Some(A(b: .None)))

相关问题