Bagaimana contoh status + pengikatan SwiftUI ini berhasil bekerja tanpa memanggil ulang body?

Aug 19 2020

Seorang rekan kerja datang dengan contoh SwiftUI berikut yang sepertinya berfungsi seperti yang diharapkan (Anda dapat memasukkan beberapa teks dan akan dicerminkan di bawah), tetapi cara kerjanya mengejutkan saya!

import SwiftUI

struct ContentView: View {
    @State var text = ""
    var body: some View {
        VStack {
            TextField("Change the string", text: $text) WrappedText(text: $text)
        }
    }
}

struct WrappedText: View {
    @Binding var text: String
    var body: some View {
        Text(text)
    }
}

Model mental SwiftUI saya yang baru membuat saya berpikir bahwa mengetik di TextField akan mengubah $textpengikatan, yang pada gilirannya akan mengubah textvar @State. Ini kemudian akan membatalkan ContentView, memicu pemanggilan baru dari body. Tapi yang menarik, bukan itu yang terjadi! Menyetel breakpoint di ContentView bodyhanya mendapat satu kali, sedangkan WrappedText bodydijalankan setiap kali binding berubah. Namun, sejauh yang saya tahu, textkeadaan benar-benar sedang berubah.

Jadi, apa yang terjadi disini? Mengapa SwiftUI tidak memanggil kembali isi ContentView pada setiap perubahan text?

Jawaban

1 Asperi Aug 20 2020 at 04:04

On State mengubah mesin rendering SwiftUI pada awalnya memeriksa kesetaraan tampilan di dalam tubuh dan, jika beberapa di antaranya tidak sama, memanggil tubuh untuk membangun kembali, tetapi hanya tampilan yang tidak sama. Dalam kasus Anda, tidak ada tampilan yang bergantung (sebagai nilai) pada textnilai (Binding seperti referensi - itu sama), jadi tidak ada yang perlu dibangun kembali pada level ini. Namun di WrappedTextdalamnya terdeteksi bahwa Textdengan yang baru texttidak sama dengan yang lama text, sehingga tubuh WrappedTextdipanggil untuk merender ulang bagian ini.

Ini dinyatakan dalam pengoptimalan rendering SwiftUI - dengan memeriksa & memvalidasi tampilan yang benar-benar berubah berdasarkan kesetaraan.

Secara default, mekanisme ini bekerja dengan properti View struct, tetapi kita dapat terlibat di dalamnya dengan mengonfirmasi tampilan kita ke Eqatableprotokol dan menandainya sebagai .equatable()pengubah untuk memberikan logika yang lebih rumit untuk mendeteksi apakah View harus (atau tidak) dirender ulang.