@StateObject: คืออะไร การสร้างและการจัดการ ObservableObject

ผู้แต่ง: IT Sectr เผยแพร่เมื่อ: 2026-06-26 เวลาอ่าน: 9 นาที

@StateObject คือ property wrapper ใน SwiftUI ที่สร้างและเป็นเจ้าของอินสแตนซ์ ObservableObject ตลอดวงจรชีวิตทั้งหมดของ View เมื่อ View ปรากฏบนหน้าจอเป็นครั้งแรก @StateObject จะเริ่มต้นวัตถุและเก็บไว้จนกว่า View จะถูกลบออกจากหน่วยความจำ สิ่งนี้รับประกันว่าข้อมูลจะไม่ถูกรีเซ็ตเมื่อสร้างอินเทอร์เฟซใหม่ — ตัวอย่างเช่น เมื่อเปลี่ยนธีมหรืออัปเดต View หลัก ตามเอกสารของนักพัฒนา Apple (2025) ควรใช้ @StateObject เป็นแหล่งความจริงหลัก (source of truth) สำหรับ ObservableObject ในลำดับชั้น SwiftUI ในขณะที่ View ย่อยจะรับวัตถุที่สร้างไว้แล้วผ่าน @ObservedObject หรือ @EnvironmentObject

ประเด็นสำคัญ

  • @StateObject — property wrapper สำหรับการสร้างและเป็นเจ้าของ ObservableObject ภายใน View
  • สร้างครั้งเดียว — วัตถุถูกเริ่มต้นหนึ่งครั้งตลอดอายุของ View และไม่ถูกสร้างใหม่เมื่อมีการสร้างใหม่
  • แหล่งความจริง — @StateObject เป็นแหล่งความจริงในลำดับชั้น แตกต่างจาก @ObservedObject
  • วงจรชีวิต — วัตถุมีชีวิตตราบเท่าที่ View ยังอยู่ในหน่วยความจำ และถูกทำลายพร้อมกับมัน
  • การเริ่มต้น — @StateObject ต้องการค่าเริ่มต้นเมื่อสร้าง โดยปกติผ่าน init พร้อมพารามิเตอร์

@StateObject ใน SwiftUI คืออะไร

@StateObject คือ property wrapper ที่ถูกนำมาใช้ใน iOS 14 ซึ่งช่วยให้ View สามารถสร้างและเป็นเจ้าของอินสแตนซ์ของคลาสที่สอดคล้องกับโปรโตคอล ObservableObject แตกต่างจาก @State ที่ทำงานกับชนิดค่า (struct) @StateObject ถูกออกแบบมาสำหรับชนิดอ้างอิง — คลาสที่สามารถแจ้ง SwiftUI เกี่ยวกับการเปลี่ยนแปลงของคุณสมบัติของพวกมัน

เมื่อ View ใช้ @StateObject var viewModel: MyViewModel SwiftUI จะสร้างอินสแตนซ์ของ MyViewModel โดยอัตโนมัติเมื่อ View ปรากฏครั้งแรกและเก็บไว้ในพื้นที่เก็บข้อมูลพิเศษของเฟรมเวิร์ก ในการอัปเดต View แต่ละครั้ง (ตัวอย่างเช่น เมื่อสถานะหลักเปลี่ยนแปลง) SwiftUI จะไม่สร้างวัตถุใหม่ — มันใช้อินสแตนซ์ที่มีอยู่จนกว่า View จะถูกลบออกจากลำดับชั้น

ตาม Apple WWDC Session 10137 (2024) @StateObject แก้ปัญหาการสูญเสียข้อมูลที่มีอยู่ใน iOS 13 เมื่อ Views ถูกสร้างใหม่ ซึ่งบังคับให้นักพัฒนาต้องสร้าง ObservableObject ใน View หลักและส่งผ่านตัวเริ่มต้น ทำให้เกิดการซ้ำซ้อนของโค้ดและความเสี่ยงในการสร้างวัตถุซ้ำโดยไม่ได้ตั้งใจ

swift
import SwiftUI

class CounterViewModel: ObservableObject {
    @Published var count: Int = 0
    
    func increment() {
        count += 1
    }
}

struct CounterView: View {
    @StateObject var viewModel = CounterViewModel()
    
    var body: some View {
        VStack {
            Text("Count: \(viewModel.count)")
            Button("Increment", action: viewModel.increment)
        }
    }
}

@StateObject ทำงานอย่างไร

กลไกของ @StateObject ขึ้นอยู่กับการรวมตัวของ SwiftUI กับเฟรมเวิร์ก Combine เมื่อ ObservableObject ทำเครื่องหมายคุณสมบัติของมันด้วยแอตทริบิวต์ @Published SwiftUI จะสมัครรับการเปลี่ยนแปลงโดยอัตโนมัติผ่านผู้เผยแพร่ที่สร้างไว้ในโปรโตคอล ObservableObject เมื่อคุณสมบัติที่เผยแพร่เปลี่ยนแปลง วัตถุจะส่งสัญญาณผ่านผู้เผยแพร่ objectWillChange ซึ่งกระตุ้นให้เกิดการวาดใหม่ของ View ทั้งหมดที่กำลังสังเกตวัตถุนี้

SwiftUI เก็บอินสแตนซ์ ObservableObject ไว้ในพื้นที่เก็บข้อมูลพิเศษที่ผูกกับอินสแตนซ์ View เฉพาะ พื้นที่จัดเก็บนี้ถูกสร้างขึ้นครั้งเดียวในระหว่างการเรนเดอร์ครั้งแรกและคงอยู่จนกว่า View จะถูกทำลาย นี่คือสาเหตุที่ @StateObject รับประกันความเสถียรของการอ้างอิง — SwiftUI จัดการหน่วยความจำโดยอัตโนมัติ โดยไม่ต้องพึ่งพาตัวเริ่มต้นของ View

ตาม objc.io — Thinking in SwiftUI (2025) การทำงานภายในของ @StateObject ใช้กลไกคล้ายกับ @State แต่สำหรับชนิดอ้างอิง: SwiftUI สร้าง wrapper หุ้มรอบวัตถุและจัดการวงจรชีวิตผ่านตัวจัดสรรของตัวเอง ซึ่งปรับให้เหมาะสมสำหรับการสร้างลำดับชั้น View บ่อยครั้ง

วงจรชีวิตของ @StateObject

  • การสร้าง — เมื่อ View ปรากฏบนหน้าจอเป็นครั้งแรก SwiftUI เรียกตัวเริ่มต้นของวัตถุและเก็บการอ้างอิง
  • การสร้างใหม่ — เมื่อ View หลักถูกอัปเดต วัตถุจะไม่ถูกสร้างใหม่ มีการใช้อินสแตนซ์ที่มีอยู่
  • การทำลาย — เมื่อ View ออกจากหน้าจอและถูกลบออกจากลำดับชั้น SwiftUI เรียก deinit ของวัตถุ

@StateObject vs @ObservedObject: ความแตกต่างหลัก

ความแตกต่างหลักระหว่าง @StateObject และ @ObservedObject อยู่ที่ใครเป็นเจ้าของวัตถุ @StateObject สร้างและเก็บวัตถุ — มันเป็นเจ้าของ @ObservedObject เพียงสังเกตวัตถุที่ถูกสร้างขึ้นที่อื่นและส่งผ่านตัวเริ่มต้นหรือคุณสมบัติ

ลักษณะ@StateObject@ObservedObject
ความเป็นเจ้าของสร้างและเป็นเจ้าของวัตถุสังเกตเพียงอย่างเดียว
การเริ่มต้นภายใน View ผ่าน init/ค่าเริ่มต้นภายนอก ส่งผ่านพารามิเตอร์
วงจรชีวิตผูกกับวงจรชีวิตของ Viewไม่ได้ถูกควบคุมโดย View
การสร้างใหม่ไม่ถูกสร้างใหม่เมื่ออัปเดตสามารถถูกแทนที่จากภายนอก
เวอร์ชัน iOSiOS 14+iOS 13+

กฎนั้นง่าย: ถ้า View สร้าง ObservableObject — ใช้ @StateObject ถ้า View เพียง รับ วัตถุที่สร้างไว้แล้วจากหลัก — ใช้ @ObservedObject การละเมิดกฎนี้จะนำไปสู่การสูญเสียข้อมูล (ถ้าใช้ @ObservedObject สำหรับความเป็นเจ้าของ) หรือการสร้างวัตถุมากเกินไป (ถ้าใช้ @StateObject สำหรับการสังเกต)

เมื่อใดควรใช้ @StateObject

ควรใช้ @StateObject ใน View ที่เป็นแหล่งความจริงสำหรับชุดข้อมูลเฉพาะ สถานการณ์ทั่วไปรวมถึงหน้าจอที่มี view model ของตัวเอง หน้าจอรูทของสแต็กนำทาง และการนำเสนอแบบโมดอลที่จัดการสถานะของตัวเอง

  • หน้าจอที่มี view model — แต่ละหน้าจอที่จัดการข้อมูลและตรรกะของตัวเองควรสร้าง view model ของมันผ่าน @StateObject
  • View รูท — ในลำดับชั้น NavigationStack หรือ TabView องค์ประกอบรูทสร้างข้อมูล และองค์ประกอบย่อยรับข้อมูลผ่าน @ObservedObject
  • หน้าต่างโมดอล — .sheet และ .fullScreenCover มักต้องการ @StateObject ของตัวเองเพื่อจัดการฟอร์มหรือกระบวนการ
  • รายการที่แก้ไขได้ — แต่ละแถวรายการที่มีฟอร์มแก้ไขควรมี @StateObject ของตัวเอง
swift
struct ProfileView: View {
    @StateObject var viewModel = ProfileViewModel()
    
    var body: some View {
        NavigationStack {
            Form {
                TextField("Name", text: $viewModel.name)
                TextField("Email", text: $viewModel.email)
                Button("Save") {
                    viewModel.saveProfile()
                }
            }
            .navigationTitle("Profile")
        }
    }
}

การเริ่มต้น @StateObject ด้วยพารามิเตอร์

การเริ่มต้น @StateObject ด้วยพารามิเตอร์ต้องใช้ไวยากรณ์พิเศษ เนื่องจาก SwiftUI จัดการการสร้างวัตถุด้วยตัวเอง คุณไม่สามารถส่งพารามิเตอร์ไปยังตัวเริ่มต้นได้โดยตรง — คุณต้องใช้ closure แบบหลบหนีหรือวิธีการสร้างแยกต่างหาก

ตาม Swift by Sundell (2024) วิธีที่สะอาดที่สุดคือการใช้วิธีการสร้างหรือ closure ที่ SwiftUI จะเรียกเมื่อวัตถุถูกสร้างครั้งแรก วิธีทางเลือกคือการเริ่มต้น ObservableObject ใน View หลักและส่งผ่าน @StateObject โดยใช้ตัวเริ่มต้นมาตรฐาน

swift
class UserViewModel: ObservableObject {
    @Published var user: User
    
    init(user: User) {
        self.user = user
    }
}

struct UserDetailView: View {
    @StateObject var viewModel: UserViewModel
    
    init(user: User) {
        _viewModel = StateObject(wrappedValue: UserViewModel(user: user))
    }
    
    var body: some View {
        Text(viewModel.user.name)
    }
}

สิ่งสำคัญที่ต้องจำคือ ตัวเริ่มต้น View ที่มี @StateObject ควรใช้ขีดล่างก่อนชื่อคุณสมบัติ (_viewModel) เพื่อเข้าถึง property wrapper เอง ไม่ใช่ค่าของมัน นี่เป็นรูปแบบ Swift มาตรฐานสำหรับการทำงานกับ property wrappers ในตัวเริ่มต้น

ข้อผิดพลาดทั่วไปกับ @StateObject

ข้อผิดพลาดที่พบบ่อยที่สุดคือการใช้ @ObservedObject แทน @StateObject สำหรับ View ที่ควรเป็นเจ้าของวัตถุ ในกรณีนี้ ทุกครั้งที่หลักถูกสร้างใหม่ วัตถุจะถูกสร้างขึ้นใหม่ ซึ่งนำไปสู่การสูญเสียข้อมูลที่สะสมทั้งหมด ข้อผิดพลาดนี้ร้ายกาจเป็นพิเศษในลำดับชั้นที่ซับซ้อนกับ NavigationStack หรือ TabView

  • การสูญเสียข้อมูลระหว่างการนำทาง — ถ้าหน้าจอย่อยใช้ @ObservedObject สำหรับ view model ของตัวเอง ข้อมูลจะถูกรีเซ็ตเมื่อกลับไปและเปิดอีกครั้ง
  • หน่วยความจำรั่ว — การสร้าง @StateObject ใน View หลักที่ไม่เคยถูกลบอาจนำไปสู่การสะสมของวัตถุถ้าหน้าจอย่อยแต่ละหน้าจอก็สร้าง @StateObject โดยไม่มีการควบคุม
  • การซ้ำซ้อนของวัตถุ — การส่ง ObservableObject ตัวเดียวไปยัง @StateObject หลายตัวใน View ต่างกันสร้างอินสแตนซ์อิสระหลายตัวที่ไม่ซิงโครไนซ์กัน

เพื่อหลีกเลี่ยงปัญหาเหล่านี้ ให้ปฏิบัติตามกฎง่ายๆ: หนึ่ง @StateObject ต่อแหล่งความจริงหนึ่งแหล่ง ถ้าข้อมูลควรถูกใช้ร่วมกันหลายหน้าจอ — สร้าง @StateObject ครั้งเดียวใน View รูทและส่งผ่าน @ObservedObject หรือ @EnvironmentObject ไปยังองค์ประกอบย่อย

swift
// ❌ Wrong: @ObservedObject for owning an object
struct BadView: View {
    @ObservedObject var vm = ViewModel() // will be recreated on each update!
}

// ✅ Correct: @StateObject for owning
struct GoodView: View {
    @StateObject var vm = ViewModel() // created once for View lifetime
}

คำถามที่พบบ่อย

ความแตกต่างระหว่าง @StateObject และ @State คืออะไร?

@State ทำงานกับชนิดค่า (struct, สตริง, ตัวเลข) และเก็บค่าโดยตรงในพื้นที่จัดเก็บ SwiftUI @StateObject ทำงานกับชนิดอ้างอิง — คลาสที่สอดคล้องกับ ObservableObject @State เหมาะสำหรับสถานะท้องถิ่นที่เรียบง่าย @StateObject สำหรับวัตถุที่ซับซ้อนที่มีตรรกะและคุณสมบัติที่เผยแพร่

ฉันสามารถใช้ @StateObject ใน iOS 13 ได้หรือไม่?

ไม่ @StateObject พร้อมใช้งานเฉพาะ iOS 14 ขึ้นไป สำหรับ iOS 13 ให้ใช้ @ObservedObject และสร้าง ObservableObject ใน View หลักผ่าน @State ด้วยการจัดการวงจรชีวิตด้วยตนเอง ทางเลือกคือใช้ @State กับ struct แทน class สำหรับข้อมูลที่ไม่ต้องการความหมายของการอ้างอิง

จะเกิดอะไรขึ้นถ้าฉันใช้ @StateObject ใน View ย่อยที่วัตถุถูกส่งมาจากหลัก?

View ย่อย จะสร้างสำเนาของตัวเอง ของ ObservableObject ซึ่งเป็นอิสระจากของหลักโดยสมบูรณ์ การเปลี่ยนแปลงในอันหนึ่งจะไม่ส่งผลต่ออีกอันหนึ่ง นี่เกือบจะเป็นข้อผิดพลาดเสมอ: ใช้ @ObservedObject เพื่อรับวัตถุจากหลักและ @StateObject เฉพาะเมื่อสร้างวัตถุใหม่ภายใน View

วัตถุที่สร้างผ่าน @StateObject ถูกทำลายเมื่อใด?

วัตถุจะถูกทำลายเมื่อ View ที่สร้างมัน ถูกลบออกจากลำดับชั้น SwiftUI โดยสมบูรณ์ สำหรับหน้าจอใน NavigationStack สิ่งนี้เกิดขึ้นเมื่อ pop จากสแต็กนำทาง สำหรับหน้าต่างโมดอล — เมื่อถูกปิด สำหรับ TabView — เมื่อเปลี่ยนแท็บ ถ้า View ไม่ได้ถูกแคช

จะส่งพารามิเตอร์ไปยัง @StateObject ระหว่างการเริ่มต้นได้อย่างไร?

ใช้ init ที่กำหนดเองพร้อมการเข้าถึง property wrapper ผ่านขีดล่าง: _viewModel = StateObject(wrappedValue: MyViewModel(param: value)) รูปแบบนี้ช่วยให้ส่งพารามิเตอร์ใดๆ ไปยัง ObservableObject ในขณะที่ยังคงรับประกันการสร้างวัตถุเพียงครั้งเดียวตลอดอายุของ View

สรุป

  • @StateObject — property wrapper สำหรับสร้างและเป็นเจ้าของ ObservableObject ภายใน View พร้อมใช้งานตั้งแต่ iOS 14
  • รับประกันการสร้างครั้งเดียว — วัตถุถูกเริ่มต้นหนึ่งครั้งและไม่ถูกสร้างใหม่เมื่อ View ถูกสร้างใหม่
  • แหล่งความจริง — @StateObject เป็นแหล่งความจริง ในขณะที่ @ObservedObject เป็นเพียงผู้สังเกตการณ์
  • วงจรชีวิต — วัตถุมีชีวิตตราบเท่าที่ View ยังอยู่ในลำดับชั้น SwiftUI และถูกทำลายเมื่อออกจากลำดับชั้น
  • การเริ่มต้นด้วยพารามิเตอร์ — ต้องการการเข้าถึง property wrapper ผ่าน _viewModel และ StateObject(wrappedValue:)
  • ข้อผิดพลาดด้านความเป็นเจ้าของ — การใช้ @ObservedObject เพื่อสร้างวัตถุทำให้สูญเสียข้อมูลเมื่อสร้างใหม่
  • หนึ่งวัตถุ — หนึ่ง @StateObject — สำหรับข้อมูลที่ใช้ร่วมกัน สร้าง @StateObject ใน View รูทและส่งไปยังย่อยผ่าน @ObservedObject

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ

อ่านเพิ่มเติม