ตัวอย่างสถานะการผูก SwiftUI + นี้จัดการให้ทำงานได้อย่างไรโดยไม่ต้องเรียกใช้เนื้อความซ้ำ

Aug 19 2020

เพื่อนร่วมงานคนหนึ่งมาพร้อมกับตัวอย่าง SwiftUI ต่อไปนี้ซึ่งดูเหมือนว่าจะใช้งานได้ตามที่คาดไว้ (คุณสามารถป้อนข้อความบางส่วนและได้รับการสะท้อนด้านล่าง) แต่วิธีการทำงานนั้นน่าแปลกใจสำหรับฉัน!

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)
    }
}

รูปแบบจิตใจมือใหม่ของฉันเกี่ยวกับ SwiftUI ทำให้ฉันคิดว่าการพิมพ์ใน TextField จะเปลี่ยนการ$textผูกซึ่งจะกลายเป็นการกลายพันธุ์text@State var นี้ก็จะโมฆะวิกฤติภาวนาสดContentView bodyแต่ที่น่าสนใจนั่นไม่ใช่สิ่งที่เกิดขึ้น! การตั้งค่าเบรกพอยต์ใน ContentView bodyจะถูกตีเพียงครั้งเดียวในขณะที่ WrappedText bodyจะทำงานทุกครั้งที่การเชื่อมโยงเปลี่ยนไป เท่าที่ฉันสามารถบอกได้ว่าtextสถานะกำลังเปลี่ยนไปจริงๆ

เกิดอะไรขึ้นที่นี่? ทำไมไม่ SwiftUI อีกครั้งวิงวอนร่างกาย ContentView ของการเปลี่ยนแปลงทุกtext?

คำตอบ

1 Asperi Aug 20 2020 at 04:04

ในกลไกการแสดงผล SwiftUI ของการเปลี่ยนสถานะในตอนแรกจะตรวจสอบความเท่าเทียมกันของมุมมองภายในร่างกายและหากบางส่วนไม่เท่ากันให้เรียกใช้ร่างกายเพื่อสร้างใหม่ แต่เฉพาะมุมมองที่ไม่เท่ากัน ในกรณีของคุณไม่มีมุมมองใดขึ้นอยู่กับ (เป็นค่า) กับtextมูลค่า (การผูกก็เหมือนกับการอ้างอิง - เหมือนกัน) ดังนั้นจึงไม่มีอะไรที่ต้องสร้างใหม่ในระดับนี้ แต่ข้างในWrappedTextตรวจพบว่าTextใหม่textไม่เท่ากับอันเก่าtextดังนั้น body of WrappedTextจึงถูกเรียกให้ re-render ส่วนนี้

นี่เป็นการประกาศการเพิ่มประสิทธิภาพการแสดงผลของ SwiftUI - โดยการตรวจสอบและตรวจสอบความถูกต้องของมุมมองที่เปลี่ยนแปลงโดยความเท่าเทียมกัน

โดยค่าเริ่มต้นกลไกนี้ทำงานโดยคุณสมบัติ View struct แต่เราสามารถมีส่วนร่วมได้โดยการยืนยันมุมมองของเราต่อEqatableโปรโตคอลและทำเครื่องหมาย.equatable()ตัวปรับแต่งเพื่อให้ตรรกะที่ซับซ้อนมากขึ้นสำหรับการตรวจจับว่า View ควรจะแสดง (หรือไม่) อีกครั้ง