@ObservedObject là một Property Wrapper trong SwiftUI dùng để quan sát một thể hiện ObservableObject được truyền từ bên ngoài. Khác với @StateObject, @ObservedObject không tạo ra đối tượng — nó đăng ký thay đổi của một đối tượng đã tồn tại. Theo Tài liệu dành cho nhà phát triển Apple (2025), @ObservedObject được sử dụng trong các view con cần theo dõi dữ liệu thuộc về view cha. @ObservedObject cung cấp kết nối phản ứng mà không cần quản lý vòng đời của đối tượng.
Những điểm chính
@ObservedObject là một Property Wrapper đăng ký một view vào các thay đổi của ObservableObject. ObservableObject là một giao thức từ framework Combine yêu cầu triển khai một publisher objectWillChange. Khi bất kỳ thuộc tính nào được đánh dấu @Published thay đổi bên trong ObservedObject, publisher sẽ gửi tín hiệu và SwiftUI vẽ lại tất cả các view đã đăng ký qua @ObservedObject.
Đặc điểm chính của @ObservedObject là không có quyền sở hữu. View không chịu trách nhiệm tạo hay hủy đối tượng. Đối tượng được tạo trong view cha (qua @StateObject) hoặc được tiêm qua @EnvironmentObject. View con chỉ quan sát thay đổi và nhận cập nhật. Nếu đối tượng bị thay thế trong view cha, @ObservedObject sẽ chuyển sang thể hiện mới.
@ObservedObject phù hợp cho các kịch bản chia sẻ dữ liệu: mô hình người dùng, cài đặt chung, trạng thái kết nối máy chủ. Khi nhiều view ở các cấp độ khác nhau cần hiển thị cùng một dữ liệu, @ObservedObject trong mỗi view tạo ra các đăng ký độc lập nhưng nhất quán cho một nguồn duy nhất.
Sự khác biệt giữa @ObservedObject và @StateObject là một trong những chủ đề phổ biến nhất trong các cuộc phỏng vấn SwiftUI. Quy tắc chính: @StateObject tạo và sở hữu đối tượng, @ObservedObject quan sát đối tượng đã tồn tại. Vi phạm quy tắc này dẫn đến mất dữ liệu không mong muốn hoặc khởi tạo hai lần.
| Đặc điểm | @StateObject | @ObservedObject |
|---|---|---|
| Tạo đối tượng | Có, khi khởi tạo view | Không, nhận đối tượng đã có |
| Quyền sở hữu | View hiện tại | Thành phần cha |
| Thể hiện duy nhất | Có, trong toàn bộ vòng đời | Không, có thể bị thay thế |
| Tạo lại khi render | Không, được giữ nguyên | Phụ thuộc vào view cha |
| Nơi sử dụng | View gốc sở hữu | View con |
@StateObject đảm bảo đối tượng được tạo một lần và tồn tại qua các lần khởi tạo lặp lại của cấu trúc view. @ObservedObject nhận đối tượng từ bên ngoài và được tạo lại mỗi khi cấu trúc cha khởi tạo. Nếu view cha sử dụng @StateObject cho đối tượng, các view con có thể áp dụng @ObservedObject một cách an toàn — đối tượng sẽ là duy nhất trong toàn bộ hệ thống phân cấp.
Cơ chế theo dõi của @ObservedObject dựa trên Combine và giao thức ObservableObject. Trong quá trình khởi tạo, SwiftUI gọi publisher objectWillChange — đối tượng phải phát ra tín hiệu trước khi thay đổi thuộc tính @Published. Combine truyền tín hiệu vào đồ thị phụ thuộc của SwiftUI, đánh dấu tất cả các view phụ thuộc là cần cập nhật. Điều này xảy ra đồng bộ trước khi giá trị thay đổi.
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")
}
}
}
Trong ví dụ, WeatherService là một ObservableObject với hai thuộc tính @Published. WeatherView khai báo @ObservedObject var weather: WeatherService, nhận thể hiện từ view cha. Khi temperature thay đổi, objectWillChange kích hoạt trước khi đặt giá trị mới, SwiftUI vẽ lại WeatherView và hiển thị nhiệt độ thực tế. Việc đăng ký được SwiftUI quản lý tự động — nhà phát triển không cần gọi sink hay dispose.
Mẫu đầu tiên là truyền mô hình qua bộ khởi tạo. View cha tạo ObservableObject qua @StateObject và truyền nó cho các view con dưới dạng @ObservedObject. Đây là cách truyền dữ liệu phân cấp tiêu chuẩn, nơi view gốc quản lý vòng đời của mô hình và tất cả các thành phần lồng nhau đăng ký thay đổi.
Mẫu thứ hai là EnvironmentObject, một phiên bản toàn cục của @ObservedObject qua môi trường SwiftUI. Đối tượng được tiêm ở cấp scene hoặc view gốc và tự động có sẵn cho tất cả các thành phần con mà không cần truyền rõ ràng qua bộ khởi tạo. Bên trong view con, @EnvironmentObject hoạt động tương tự @ObservedObject nhưng nhận đối tượng từ môi trường.
Mẫu thứ ba là kết hợp nhiều ObservableObject. Trong các ứng dụng phức tạp, một view có thể quan sát nhiều đối tượng: @ObservedObject var user: UserService, @ObservedObject var network: NetworkMonitor. Điều này phân tách trách nhiệm giữa các dịch vụ và duy trì khả năng kiểm thử của mỗi thành phần.
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 quan sát UserViewModel và NetworkMonitor. Mỗi đối tượng xử lý miền dữ liệu riêng và thông báo độc lập cho view về các thay đổi. Nếu mạng bị ngắt kết nối, NetworkMonitor thay đổi isConnected và SwiftUI vẽ lại DashboardView, cập nhật màu chỉ báo. Kết hợp ObservableObject là cách ưa thích để tổ chức dữ liệu trong các ứng dụng SwiftUI.
@Published là một Property Wrapper từ Combine tự động thêm publisher vào một thuộc tính bên trong ObservableObject. Khi thuộc tính @Published thay đổi, Combine tạo ra một sự kiện qua publisher objectWillChange. SwiftUI đăng ký publisher này khi sử dụng @ObservedObject hoặc @StateObject và vẽ lại view ở mỗi giá trị mới.
@Published hỗ trợ tất cả các kiểu, bao gồm tùy chọn, bộ sưu tập và cấu trúc tùy chỉnh. Tuy nhiên, đối với bộ sưu tập (mảng, từ điển), SwiftUI chỉ theo dõi việc thay thế tham chiếu, không phải thay đổi nội dung. Để phát hiện việc thêm hoặc xóa phần tử, bạn cần gán lại toàn bộ bộ sưu tập hoặc sử dụng ObservableObject với objectWillChange.send() thủ công.
Một chi tiết quan trọng: @Published chỉ nên được sử dụng bên trong một class triển khai ObservableObject. Sử dụng @Published bên ngoài ObservableObject sẽ gây ra lỗi biên dịch. Ngoài ra, @Published không thể áp dụng cho các thuộc tính khởi tạo trễ (lazy var) hoặc thuộc tính tính toán.
Lỗi nghiêm trọng nhất là sử dụng @ObservedObject để tạo một đối tượng. Nếu bạn viết @ObservedObject var model = UserViewModel() trong view cha, mỗi lần render sẽ tạo một thể hiện UserViewModel mới. Dữ liệu sẽ bị mất và các đăng ký @Published sẽ được tạo lại. Luôn sử dụng @StateObject để tạo và @ObservedObject chỉ để nhận đối tượng đã có sẵn.
Lỗi thứ hai là sửa đổi thuộc tính @Published bên ngoài luồng chính. ObservableObject sử dụng Combine, yêu cầu gửi thay đổi trên luồng chính (main actor). Nếu bạn thay đổi @Published trong hàng đợi nền, SwiftUI có thể vẽ lại view không đúng lúc, gây ra điều kiện tranh đua. Sử dụng DispatchQueue.main.async hoặc @MainActor cho các cập nhật.
Vấn đề thứ ba là cập nhật vòng lặp. Nếu một thay đổi @Published gây ra các tác dụng phụ làm thay đổi @Published một lần nữa, sẽ xảy ra vòng lặp vẽ lại vô hạn. Giải pháp: sử dụng cờ bảo vệ (isUpdating) hoặc tách logic ra các ObservableObject khác nhau với ranh giới trách nhiệm rõ ràng.
Câu hỏi thường gặp
Có, SwiftUI hỗ trợ @ObservedObject var model: UserViewModel?. Tuy nhiên, view sẽ không đăng ký thay đổi khi đối tượng là nil. Khi một giá trị được gán, đăng ký sẽ tự động kích hoạt.
@ObservedObject nhận đối tượng qua bộ khởi tạo, @EnvironmentObject qua môi trường SwiftUI. @EnvironmentObject không yêu cầu truyền rõ ràng qua hàm tạo, nhưng đối tượng phải được tiêm ở cấp cao nhất của hệ thống phân cấp.
Gọi objectWillChange.send() trước khi thay đổi thuộc tính. Điều này hữu ích khi @Published không phù hợp (ví dụ: cho thuộc tính tính toán hoặc thao tác bộ sưu tập nơi bạn cần báo cáo thay đổi trước khi biến đổi).
@ObservedObject và @Published theo dõi việc thay thế tham chiếu, không phải thay đổi nội dung bộ sưu tập. Để kích hoạt vẽ lại, bạn cần gán lại mảng: items.append(newItem) → items = items hoặc sử dụng objectWillChange.send() trước khi biến đổi.
Không, @ObservedObject là Property Wrapper của SwiftUI chỉ khả dụng bên trong các kiểu triển khai giao thức View. Đối với các struct thông thường, hãy sử dụng Combine trực tiếp với ObservableObjectPublisher.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm