Xcode 错误:编译器无法在合理的时间内对该表达式进行类型检查;尝试将表达式分解为不同的子表达式

Sco*_*ler 5 xcode swift swiftui

当使用 Xcode 13.2.1 和 SwiftUI 实现一个简单的幻灯片时,我遇到了一个编译时错误,其中 Xcode 在我的 M1 上花了大约 5 分钟来决定它无法解析我的代码,并最终给出错误:

编译器无法在合理的时间内对该表达式进行类型检查;尝试将表达式分解为不同的子表达式

我将其范围缩小到底部附近的 NavigationLink 线。如果我注释掉它,它会快速编译,只显示警告。

以下是我的最小的、可重现的示例:

import SwiftUI
import Foundation

enum MarkerType: Double {
    case unlabeled = -99
    case end = -4
    case start  = -3
    case stop = -2
    case blank = -1
    case image = 1
}

class LabeledImage {
    let image: Image
    let marker: Double
    var appeared = false
    
    init(image: Image, marker: Double) {
        self.image = image
        self.marker = marker
    }
}

struct SlideShow {
    private let maxImages: Int = 10000
    var images = [LabeledImage]()
    var labels = [String]()
    var totalImages: Int { return self.images.count }
    private var fromFolder: URL
    
    init(fromURL: URL = Bundle.main.bundleURL.appendingPathComponent("Contents/Resources/DefaultImages")) {
        self.fromFolder = fromURL
    }
}

class AppState: ObservableObject {
    static var docDir: URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
    @Published var isMainMenuActive = false
    @Published var loadFolder: URL = Bundle.main.bundleURL.appendingPathComponent("Contents/Resources/DefaultImages")
    @Published var intervalSeconds: Double = 0.6

    var saveFolder = URL(fileURLWithPath: "BCILab", relativeTo: docDir)
    var labels = [String]()
    var totalImages: Int = 0
    var saveIndex: Int = 0
}


struct minsample: View {
    @StateObject private var appState = AppState()
    @State private var slideshow = SlideShow()
    @State private var selection: Int = 0
    
    private func insertAppears(_ marker: Double) {
        let nmarker = marker + 100.0
    }
    
    var body: some View {
        NavigationView {
            ForEach(0..<slideshow.images.count-1, id: \.self) { i in
                let thisImage = slideshow.images[i].image
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .onAppear(perform: { insertAppears(slideshow.images[i].marker) })
                let nextImage = slideshow.images[i+1].image
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .onAppear(perform: { insertAppears(slideshow.images[i+1].marker) })

                NavigationLink(destination: nextImage, tag: i, selection: self.$selection) { thisImage }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

jn_*_*pdx 4

一般来说,使用基于索引的解决方案ForEach是一个坏主意。它破坏了 SwiftUI 的视图差异系统,并且往往还会导致编译时问题。

我首先要制作LabeledImage Identifiable

class LabeledImage : Identifiable {
    var id = UUID()
    let image: Image
    let marker: Double
    var appeared = false
    
    init(image: Image, marker: Double) {
        self.image = image
        self.marker = marker
    }
}
Run Code Online (Sandbox Code Playgroud)

(我也会把它变成struct——稍后会详细介绍)

然后,由于您确实需要索引来实现您的nextImage功能,因此您可以.enumerated在集合上使用:

struct MinSample: View {
    @StateObject private var appState = AppState()
    @State private var slideshow = SlideShow()
    @State private var selection: Int? = 0
    
    private func insertAppears(_ marker: Double) {
        let nmarker = marker + 100.0
    }
    
    var body: some View {
        NavigationView {
            ForEach(Array(slideshow.images.enumerated()), id: \.1.id) { (index,imageModel) in
                if index < slideshow.images.count - 1 {
                    let thisImage = imageModel.image
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                        .onAppear(perform: { insertAppears(imageModel.marker) })
                    let nextImage = slideshow.images[index + 1].image
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                        .onAppear(perform: { insertAppears(slideshow.images[index+1].marker) })

                    NavigationLink(destination: nextImage, tag: index, selection: self.$selection) { thisImage }
                } else {
                    EmptyView()
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

上面的代码在我的 M1 上编译很快,没有任何问题。


现在,与您的问题没有直接关系,但我还需要更改一些其他内容:

  1. 制作你的模型struct,SwiftUI 在进行状态比较时通常可以更好地处理模型
  2. 不要存储对 SwiftUIImage的引用 - 相反,存储对路径或其他重新创建该图像的方式的引用。无论如何,这将使过渡变得LabeledImage更加struct容易。因此,您的模型可能如下所示:
struct LabeledImage : Identifiable {
    var id = UUID()
    let imageName: String
    let marker: Double
    var appeared = false
}
Run Code Online (Sandbox Code Playgroud)
  1. 考虑一下您是否需要 -中的tag和参数- 也许在最小的示例中并不清楚为什么使用它们。selectionNavigationLink