@ObservedObject คือ Property Wrapper ใน SwiftUI สำหรับสังเกตอินสแตนซ์ ObservableObject ที่ถูกส่งมาจากภายนอก แตกต่างจาก @StateObject ตรงที่ @ObservedObject ไม่ได้สร้างวัตถุใหม่ — มันสมัครรับการเปลี่ยนแปลงของวัตถุที่มีอยู่แล้ว ตาม เอกสารสำหรับนักพัฒนา Apple (2025), @ObservedObject ถูกใช้ในวิวย่อยที่ต้องการติดตามข้อมูลที่เป็นของวิวหลัก @ObservedObject ให้การเชื่อมต่อแบบรีแอกทีฟโดยไม่ต้องจัดการวงจรชีวิตของวัตถุ
ประเด็นสำคัญ
@ObservedObject คือ Property Wrapper ที่สมัครวิวให้รับการเปลี่ยนแปลงของ ObservableObject ObservableObject คือโปรโตคอลจากเฟรมเวิร์ก Combine ที่ต้องการการใช้งาน publisher objectWillChange เมื่อคุณสมบัติใดๆ ที่ถูกทำเครื่องหมายด้วย @Published เปลี่ยนแปลงภายใน ObservedObject publisher จะส่งสัญญาณ และ SwiftUI จะวาดวิวทั้งหมดที่สมัครรับผ่าน @ObservedObject ใหม่
คุณสมบัติหลักของ @ObservedObject คือ การไม่เป็นเจ้าของ วิวไม่รับผิดชอบในการสร้างหรือทำลายวัตถุ วัตถุถูกสร้างขึ้นในวิวหลัก (ผ่าน @StateObject) หรือถูกฉีดผ่าน @EnvironmentObject วิวย่อยเพียงสังเกตการเปลี่ยนแปลงและรับการอัปเดต หากวัตถุถูกแทนที่ในวิวหลัก @ObservedObject จะสลับไปยังอินสแตนซ์ใหม่
@ObservedObject เหมาะสำหรับ สถานการณ์การแชร์ข้อมูล: โมเดลผู้ใช้ การตั้งค่าร่วมกัน สถานะการเชื่อมต่อเซิร์ฟเวอร์ เมื่อวิวหลายแห่งในระดับลำดับชั้นต่างกันจำเป็นต้องแสดงข้อมูลเดียวกัน @ObservedObject ในแต่ละวิวจะสร้างการสมัครรับที่เป็นอิสระแต่สอดคล้องกันไปยังแหล่งข้อมูลเดียว
ความแตกต่างระหว่าง @ObservedObject และ @StateObject เป็นหนึ่งในหัวข้อที่พบบ่อยที่สุดในการสัมภาษณ์ SwiftUI กฎหลัก: @StateObject สร้างและเป็นเจ้าของวัตถุ @ObservedObject สังเกตวัตถุที่มีอยู่แล้ว การละเมิดกฎนี้ทำให้เกิดการสูญเสียข้อมูลที่ไม่คาดคิดหรือการเริ่มต้นซ้ำซ้อน
| ลักษณะ | @StateObject | @ObservedObject |
|---|---|---|
| การสร้างวัตถุ | ใช่, เมื่อเริ่มต้นวิว | ไม่, รับวัตถุสำเร็จรูป |
| การเป็นเจ้าของ | วิวปัจจุบัน | คอมโพเนนต์หลัก |
| อินสแตนซ์เดียว | ใช่, ตลอดวงจรชีวิต | ไม่, สามารถถูกแทนที่ได้ |
| การสร้างใหม่เมื่อเรนเดอร์ | ไม่, คงอยู่ | ขึ้นอยู่กับวิวหลัก |
| ที่ไหนที่ใช้ | วิวรากที่เป็นเจ้าของ | วิวย่อย |
@StateObject รับประกันว่าวัตถุจะถูกสร้างครั้งเดียวและอยู่รอดจากการเริ่มต้นซ้ำของโครงสร้างวิว @ObservedObject รับวัตถุจากภายนอกและถูกสร้างใหม่ทุกครั้งที่มีการเริ่มต้นโครงสร้างหลัก หากวิวหลักใช้ @StateObject สำหรับวัตถุ วิวย่อยสามารถใช้ @ObservedObject ได้อย่างปลอดภัย — วัตถุจะไม่ซ้ำกันทั่วทั้งลำดับชั้น
กลไกการติดตามของ @ObservedObject ขึ้นอยู่กับ Combine และโปรโตคอล ObservableObject ในระหว่างการเริ่มต้น SwiftUI จะเรียก publisher objectWillChange — วัตถุต้องปล่อยสัญญาณก่อนที่จะเปลี่ยนแปลงคุณสมบัติ @Published Combine ส่งสัญญาณไปยังกราฟการพึ่งพาของ SwiftUI ซึ่งทำเครื่องหมายวิวที่ขึ้นอยู่ทั้งหมดว่าต้องการการอัปเดต สิ่งนี้เกิดขึ้นแบบซิงโครนัสก่อนที่ค่าจะเปลี่ยนแปลง
class WeatherService: ObservableObject {
@Published var temperature: Double = 22.0
@Published var city: String = "Moscow"
}
struct WeatherView: View {
@ObservedObject var weather: WeatherService
var body: some View {
VStack {
Text("\(weather.city)")
Text("\(weather.temperature)°C")
}
}
}
ในตัวอย่าง WeatherService คือ ObservableObject ที่มีคุณสมบัติ @Published สองรายการ WeatherView ประกาศ @ObservedObject var weather: WeatherService โดยรับอินสแตนซ์จากวิวหลัก เมื่อ temperature เปลี่ยนแปลง objectWillChange จะถูกเรียกก่อนที่จะตั้งค่าใหม่ SwiftUI จะวาด WeatherView ใหม่และแสดงอุณหภูมิจริง การสมัครรับถูกจัดการโดยอัตโนมัติโดย SwiftUI — นักพัฒนาไม่จำเป็นต้องเรียก sink หรือ dispose
รูปแบบแรกคือ การส่งโมเดลผ่านตัวเริ่มต้น วิวหลักสร้าง ObservableObject ผ่าน @StateObject และส่งไปยังวิวย่อยเป็น @ObservedObject นี่คือการถ่ายโอนข้อมูลตามลำดับชั้นมาตรฐานที่วิวรากจัดการวงจรชีวิตของโมเดลและคอมโพเนนต์ที่ซ้อนกันทั้งหมดสมัครรับการเปลี่ยนแปลง
รูปแบบที่สองคือ EnvironmentObject เวอร์ชันทั่วโลกของ @ObservedObject ผ่านสภาพแวดล้อม SwiftUI วัตถุถูกฉีดที่ระดับซีนหรือวิวรากและพร้อมใช้งานโดยอัตโนมัติสำหรับคอมโพเนนต์ย่อยทั้งหมดโดยไม่ต้องส่งอย่างชัดเจนผ่านตัวเริ่มต้น ภายในวิวย่อย @EnvironmentObject ทำงานคล้ายกับ @ObservedObject แต่รับวัตถุจากสภาพแวดล้อม
รูปแบบที่สามคือ การรวม ObservableObject หลายตัว ในแอปพลิเคชันที่ซับซ้อน วิวสามารถสังเกตวัตถุหลายตัว: @ObservedObject var user: UserService, @ObservedObject var network: NetworkMonitor สิ่งนี้แยกความรับผิดชอบระหว่างบริการและรักษาความสามารถในการทดสอบของแต่ละคอมโพเนนต์
struct DashboardView: View {
@ObservedObject var user: UserViewModel
@ObservedObject var network: NetworkMonitor
var body: some View {
VStack {
Text("Welcome, \(user.name)")
HStack {
Circle()
.fill(network.isConnected ? Color.green : Color.red)
.frame(width: 10, height: 10)
}
}
}
}
DashboardView สังเกต UserViewModel และ NetworkMonitor แต่ละวัตถุจัดการโดเมนข้อมูลของตนเองและแจ้งวิวอย่างอิสระเกี่ยวกับการเปลี่ยนแปลง หากเครือข่ายตัดการเชื่อมต่อ NetworkMonitor จะเปลี่ยน isConnected และ SwiftUI จะวาด DashboardView ใหม่ อัปเดตสีของตัวบ่งชี้ การรวม ObservableObject เป็นวิธีที่ต้องการในการจัดระเบียบข้อมูลในแอปพลิเคชัน SwiftUI
@Published คือ Property Wrapper จาก Combine ที่เพิ่ม publisher โดยอัตโนมัติให้กับคุณสมบัติภายใน ObservableObject เมื่อคุณสมบัติ @Published เปลี่ยนแปลง Combine จะสร้างเหตุการณ์ผ่าน publisher objectWillChange SwiftUI สมัครรับ publisher นี้เมื่อใช้ @ObservedObject หรือ @StateObject และวาดวิวใหม่ทุกครั้งที่มีค่าใหม่
@Published รองรับ ทุกประเภท รวมถึงตัวเลือก คอลเล็กชัน และโครงสร้างที่กำหนดเอง อย่างไรก็ตาม สำหรับคอลเล็กชัน (อาร์เรย์, ดิกชันนารี) SwiftUI จะติดตามเฉพาะการแทนที่การอ้างอิง ไม่ใช่การเปลี่ยนแปลงเนื้อหา ในการตรวจจับการเพิ่มหรือลบองค์ประกอบ คุณต้องกำหนดคอลเล็กชันทั้งหมดใหม่หรือใช้ ObservableObject พร้อม objectWillChange.send() ด้วยตนเอง
รายละเอียดสำคัญ: @Published ควรใช้ภายในคลาสที่ใช้งาน ObservableObject เท่านั้น การใช้ @Published นอก ObservableObject จะทำให้เกิดข้อผิดพลาดในการคอมไพล์ นอกจากนี้ @Published ไม่สามารถใช้กับคุณสมบัติการเริ่มต้นแบบขี้เกียจ (lazy var) หรือคุณสมบัติที่คำนวณได้
ข้อผิดพลาดที่สำคัญที่สุดคือการใช้ @ObservedObject เพื่อสร้างวัตถุ หากคุณเขียน @ObservedObject var model = UserViewModel() ในวิวหลัก ทุกครั้งที่เรนเดอร์จะสร้างอินสแตนซ์ UserViewModel ใหม่ ข้อมูลจะสูญหายและการสมัครรับ @Published จะถูกสร้างขึ้นใหม่ ใช้ @StateObject สำหรับการสร้างเสมอ และใช้ @ObservedObject เพื่อรับวัตถุสำเร็จรูปเท่านั้น
ข้อผิดพลาดที่สองคือ การแก้ไขคุณสมบัติ @Published นอกเธรดหลัก ObservableObject ใช้ Combine ซึ่งต้องการส่งการเปลี่ยนแปลงบนเธรดหลัก (main actor) หากคุณเปลี่ยน @Published ในคิวพื้นหลัง SwiftUI อาจวาดวิวใหม่ในเวลาที่ไม่เหมาะสม ทำให้เกิด race conditions ใช้ DispatchQueue.main.async หรือ @MainActor สำหรับการอัปเดต
ปัญหาที่สามคือ การอัปเดตแบบวนซ้ำ หากการเปลี่ยนแปลง @Published ทำให้เกิดผลข้างเคียงที่เปลี่ยนแปลง @Published อีกครั้ง จะเกิดการวนซ้ำการวาดใหม่ไม่มีที่สิ้นสุด วิธีแก้ไข: ใช้แฟล็กป้องกัน (isUpdating) หรือแยกตรรกะไปยัง ObservableObject ต่างๆ ที่มีขอบเขตความรับผิดชอบชัดเจน
คำถามที่พบบ่อย
ได้ SwiftUI รองรับ @ObservedObject var model: UserViewModel? อย่างไรก็ตาม วิวจะไม่สมัครรับการเปลี่ยนแปลงในขณะที่วัตถุเป็น nil เมื่อกำหนดค่า การสมัครรับจะเปิดใช้งานโดยอัตโนมัติ
@ObservedObject รับวัตถุผ่านตัวเริ่มต้น @EnvironmentObject รับผ่านสภาพแวดล้อม SwiftUI @EnvironmentObject ไม่ต้องการการส่งอย่างชัดเจนผ่านคอนสตรัคเตอร์ แต่วัตถุต้องถูกฉีดที่ระดับสูงสุดของลำดับชั้น
เรียก objectWillChange.send() ก่อนที่จะเปลี่ยนคุณสมบัติ สิ่งนี้มีประโยชน์เมื่อ @Published ไม่เหมาะสม (เช่น สำหรับคุณสมบัติที่คำนวณได้หรือการดำเนินการกับคอลเล็กชันที่คุณต้องรายงานการเปลี่ยนแปลงก่อนการกลาย)
@ObservedObject และ @Published ติดตามการแทนที่การอ้างอิง ไม่ใช่การเปลี่ยนแปลงเนื้อหาคอลเล็กชัน เพื่อกระตุ้นการวาดใหม่ คุณต้องกำหนดอาร์เรย์ใหม่: items.append(newItem) → items = items หรือใช้ objectWillChange.send() ก่อนการกลาย
ไม่ได้ @ObservedObject คือ Property Wrapper ของ SwiftUI ที่ใช้ได้เฉพาะภายในประเภทที่ใช้งานโปรโตคอล View สำหรับ struct ทั่วไป ให้ใช้ Combine โดยตรงกับ ObservableObjectPublisher
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ