为什么我的 View 可以在 SwiftUI 中使用 Equatable 函数,但我却不能在 View 中使用它?

use*_*ser 8 swift swiftui

我正在尝试使用 EquatableView 作为我的视图,但 SwiftUI 不使用我的 == 函数,看来我做的一切都是正确的,但不起作用,需要帮助。我想使用 == 函数来阻止 SwiftUI 进行不必要的渲染,但它每次都会渲染并且不使用给定的函数,为什么?

import SwiftUI

extension Int { var isEven: Bool { return self % 2 == 0 } }
Run Code Online (Sandbox Code Playgroud)
struct ContentView: View {
    
    @State private var number = 3
    
    var body: some View {
        VStack {
            
            // EquatableView ( content: NumberView(number: number) )  // <<: Here
            // NumberView(number: number).equatable()   // <<: Or Here
            

            Button("New Random Number") {
                number = Int.random(in: 1...100)
            }
            
            Text(number.description).bold().padding()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)
struct NumberView: View, Equatable {
    
    let number: Int
    
    init(number: Int) {
        
        print("initializing")
        
        self.number = number
    }

    var body: some View {
        
        print("rendering")

        return VStack {
            
            if number.isEven {
                Text("EVEN").bold().foregroundColor(Color.green)
            } else {
                Text("ODD").bold().foregroundColor(Color.red)
            }
            
        }
        .padding()
  
    }

    static func == (lhs: NumberView, rhs: NumberView) -> Bool {
        
        print("Equatable used!")
        
        return lhs.number.isEven == rhs.number.isEven
    }
  
}
Run Code Online (Sandbox Code Playgroud)

jn_*_*pdx 7

(Xcode 12.4、macOS 11.2.3)

\n

其机制equatable()有点不透明。事实上,所有标题都有这个(当然不详细)定义:

\n
\n

当新值与其旧值相同时,防止视图更新其子视图。

\n
\n

例如,人们会假设在您最初的示例中,您==将被调用来评估视图是否相等。但是,显然这不会发生。

\n

它可以通过多种(有时是令人惊讶的)方式被迫发生。例如(这个是不足为奇的),如果您在NumberView@State属性上添加一个附加属性,则==每次都会调用您的属性:

\n
\nstruct NumberView: View, Equatable {\n    \n    var number: Int = 0\n    @State var additionalProp: Int = 0\n    \n    var body: some View {\n        print("rendering \\(Date()): \\(number)")\n        return VStack {\n            Text("\\(number)")\n            if number.isEven {\n                Text("EVEN").bold().foregroundColor(Color.green)\n            } else {\n                Text("ODD").bold().foregroundColor(Color.red)\n            }\n        }\n        .padding()\n    }\n\n    static func == (lhs: NumberView, rhs: NumberView) -> Bool {\n        print("Equatable used!")\n        return lhs.number.isEven == rhs.number.isEven\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

其行为与以下内容不同(其中==未调用):

\n
var number: Int = 0\nvar additionalProp: Int = 0\n
Run Code Online (Sandbox Code Playgroud)\n

由此,我想我们可以推断出以下几点:

\n
    \n
  1. 如果View没有其他属性,SwiftUI 将对 props 进行相等性检查,如果它们不相等,它甚至不会调用你的equatable函数
  2. \n
  3. 如果有 @State 属性,SwiftUI调用您的 equatable 函数。
  4. \n
\n

这是对我来说更有趣的案例:

\n
struct NumberView: View, Equatable {\n    \n    var number: Int = 0\n    var additionalProp: String = ""\n    \n    var body: some View {\n        print("rendering \\(Date()): \\(number)")\n        return VStack {\n            Text("\\(number)")\n            if number.isEven {\n                Text("EVEN").bold().foregroundColor(Color.green)\n            } else {\n                Text("ODD").bold().foregroundColor(Color.red)\n            }\n        }\n        .padding()\n    }\n\n    static func == (lhs: NumberView, rhs: NumberView) -> Bool {\n        print("Equatable used!")\n        return lhs.number.isEven == rhs.number.isEven\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

在上面的情况下,equatable 函数将再次被使用,即使它String是 Swift 中的值类型(和 Equatable)并且不使用@State. 我对那部分没有很好的解释。

\n
\n此页面 (https://swiftui-lab.com/equatableview/) 引用了一位 Apple 工程师关于何时使用 == 的言论:\n
\n

SwiftUI 假设任何 Equatable.== 都是真正的相等检查,因此对于 POD 视图,它直接比较每个字段(通过反射)。对于非 POD 视图,它更喜欢 view\xe2\x80\x99s ==,但如果没有 ==,则回退到其自己的字段比较。EqView 是一种强制使用 == 的方法。\n当它进行每个字段比较时,相同的规则将递归地应用于每个字段(选择直接比较或 == 如果已定义)。(POD = 纯数据,请参阅 Swift\xe2\x80\x99s _isPOD() 函数。)

\n
\n