Unowned Reference (tham chiếu không sở hữu) là một tham chiếu không sở hữu trong Swift không làm tăng retain count của đối tượng và, không giống như weak, không được đặt thành nil sau khi đối tượng được giải phóng. Theo Apple Swift Language Guide, 2026, unowned được sử dụng khi được đảm bảo rằng đối tượng tồn tại ít nhất bằng thời gian tồn tại của đối tượng tham chiếu đến nó. Không giống như Weak Reference, unowned không yêu cầu unwrap — nó là kiểu không tùy chọn, giúp mã sạch hơn nhưng đặt trách nhiệm đảm bảo vòng đời lên nhà phát triển.
Những Điểm Chính
Unowned Reference là một tham chiếu không sở hữu đến một đối tượng trong ARC không làm tăng retain count của nó. Không giống như weak, tham chiếu unowned không bị đặt về không sau khi đối tượng được giải phóng: nó tiếp tục trỏ đến bộ nhớ đã được giải phóng. Truy cập vào tham chiếu như vậy gây ra crash runtime với EXC_BAD_ACCESS.
Thuật ngữ “không sở hữu” phản ánh ý nghĩa: đối tượng tồn tại, nhưng không ai chịu trách nhiệm về vòng đời của nó. Nhà phát triển tuyên bố rõ ràng: “tôi đảm bảo rằng đối tượng này sẽ tồn tại miễn là tôi tham chiếu đến nó.” Trình biên dịch không xác minh đảm bảo này — đó là một hợp đồng ở cấp độ nhà phát triển.
Theo Swift.org Documentation, 2026, tham chiếu unowned được ưa chuộng hơn weak trong các kịch bản có vòng đời được đảm bảo vì chúng: không yêu cầu kiểu tùy chọn (mã sạch hơn), không yêu cầu unwrap (ít force-unwrap hoặc guard let hơn), và không có chi phí bảo trì bảng weak để đặt về không. Tuy nhiên, bất kỳ vi phạm hợp đồng nào cũng dẫn đến crash.
Trong Swift, tham chiếu unowned được khai báo với từ khóa unowned trước let hoặc var. Không giống như weak, unowned có thể là cả let và var, và không yêu cầu kiểu tùy chọn. Thuộc tính này làm cho unowned thuận tiện cho các tham chiếu không thể là nil theo logic miền.
class Country {
let name: String
var capital: City! // sẽ được đặt sau khi khởi tạo
init(name: String) { self.name = name }
}
class City {
let name: String
unowned let country: Country // ✅ unowned let — đảm bảo vòng đời
init(name: String, country: Country) {
self.name = name
self.country = country
}
}
// Sử dụng
let france = Country(name: "France")
let paris = City(name: "Paris", country: france)
france.capital = paris
// ✅ Country → City (strong), City → Country (unowned) — không có retain cycle
Trong ví dụ này, City unowned let country — một thành phố không thể tồn tại mà không có quốc gia. Nếu quốc gia biến mất, thành phố (và tham chiếu) mất đi ý nghĩa của chúng. Về mặt ngữ nghĩa, đây là trường hợp lý tưởng cho unowned: đảm bảo vòng đời tồn tại, không cần tùy chọn, retain cycle không xảy ra.
unowned var được phép nhưng ít phổ biến hơn. Nó được sử dụng khi tham chiếu có thể được thay thế (ví dụ: gắn lại con vào một cha khác). Khi gán lại, việc giải phóng đối tượng cũ là trách nhiệm của chủ sở hữu bên ngoài.
Trong Swift 5.0+, hỗ trợ cho unowned tùy chọn (unowned let x: Type?) đã được giới thiệu. Đây là một sự thỏa hiệp: unowned đảm bảo rằng nếu tham chiếu không phải nil, đối tượng còn sống. Hành vi khi giải phóng là crash, giống như unowned thông thường.
Sự lựa chọn giữa unowned và weak là một trong những quyết định thường xuyên khi thiết kế kiến trúc Swift. Hãy xem xét các tiêu chí và khuyến nghị cho từng trường hợp.
| Tiêu chí | Weak | Unowned |
|---|---|---|
| Tùy chọn | Có (Type?) | Không (Type) |
| Đặt về không khi giải phóng | Tự động về nil | Không (rủi ro con trỏ lơ lửng) |
| Kiểu (let/var) | Chỉ var | let hoặc var |
| Hiệu suất | Chi phí bảng weak | Tối thiểu (con trỏ đơn giản) |
| An toàn | An toàn (nil được kiểm tra) | Rủi ro EXC_BAD_ACCESS |
| Đảm bảo vòng đời | Không yêu cầu | Yêu cầu đảm bảo rõ ràng |
Sử dụng weak nếu có bất kỳ nghi ngờ nào về vòng đời của đối tượng. Weak an toàn, rõ ràng và không yêu cầu bằng chứng. Sử dụng unowned chỉ khi bạn có thể loại trừ tất cả các kịch bản mà đối tượng có thể bị giải phóng sớm hơn. Các trường hợp điển hình: con không tồn tại nếu không có cha; closure thực thi đồng bộ; truy cập vào đối tượng trong bộ khởi tạo của nó.
Theo Airbnb Swift Style Guide, 2025, trong các cơ sở mã lớn, khuyến nghị sử dụng weak theo mặc định và unowned chỉ với một bình luận rõ ràng giải thích đảm bảo vòng đời. Điều này giảm rủi ro crash không rõ ràng trong quá trình tái cấu trúc.
Closures là trường hợp sử dụng phổ biến thứ hai cho unowned sau mối quan hệ cha-con. Danh sách capture [unowned self] được sử dụng khi được đảm bảo rằng self tồn tại lâu hơn closure. Hãy xem xét các kịch bản đúng và sai.
Closure đồng bộ — sorted, filter, map. Chúng thực thi ngay lập tức trong luồng hiện tại, self chắc chắn còn sống. Danh sách capture với unowned được chấp nhận ở đây và tạo ra mã sạch hơn.
class DataProcessor {
var items: [Int] = [3, 1, 4, 1, 5]
func processSorted() {
// ✅ unowned self — sorted thực thi đồng bộ, self được đảm bảo sống
let sorted = items.sorted { [unowned self] a, b in
return self.customCompare(a, b)
}
}
func customCompare(_ a: Int, _ b: Int) -> Bool { return a < b }
}
Closure không đồng bộ — với độ trễ, yêu cầu mạng, hoạt ảnh. Self có thể bị giải phóng giữa lúc lên lịch closure và thực thi nó. Ở đây unowned self dẫn đến crash. Sử dụng [weak self].
class NetworkLoader {
func loadData() {
// ❌ NGUY HIỂM: unowned self trong closure không đồng bộ
URLSession.shared.dataTask(with: url) { [unowned self] data, _, _ in
self.handleResponse(data) // CRASH nếu self bị giải phóng
}.resume()
}
func handleResponse(_ data: Data?) { }
// ✅ ĐÚNG: weak self + guard
func loadDataSafe() {
URLSession.shared.dataTask(with: url) { [weak self] data, _, _ in
guard let self else { return }
self.handleResponse(data)
}.resume()
}
}
Hãy nhớ quy tắc: unowned self — chỉ cho closure đồng bộ thực thi ngay lập tức. Đối với closure không đồng bộ, luôn sử dụng weak self + guard let. Ngoại lệ: nếu bạn giữ rõ ràng một tham chiếu đến đối tượng cho đến khi closure hoàn thành (ví dụ: giữ tham chiếu mạnh trong một biến khác).
Unowned là một công cụ mạnh mẽ nhưng nguy hiểm. Hãy xem xét các kịch bản thực tế nơi unowned có thể dẫn đến crash và các phương pháp giảm thiểu rủi ro.
Rủi ro chính của unowned là sự thay đổi trong logic kinh doanh làm mất hiệu lực đảm bảo vòng đời. Nhà phát triển tái cấu trúc mã: thay đổi quyền sở hữu, giới thiệu giải phóng trì hoãn, thêm bộ đệm — và tham chiếu unowned trở thành quả bom hẹn giờ. Trình biên dịch sẽ không cảnh báo — chỉ crash trên thiết bị của người dùng.
Khuyến nghị: chỉ sử dụng unowned khi đảm bảo vòng đời rõ ràng và được ghi chép. Thêm bình luận vào mỗi unowned: tại sao tham chiếu này an toàn và trong điều kiện nào nó có thể bị vi phạm.
UIKit là khu vực rủi ro cao cho unowned. ViewController có thể bị giải phóng bất cứ lúc nào trong quá trình điều hướng (pop, dismiss), dỡ bộ nhớ hoặc thay đổi hướng. Nếu bạn truyền ViewController vào closure với unowned self, self có thể là nil khi quay lại từ nền hoặc khi hoàn thành hoạt ảnh.
Để giảm rủi ro khi sử dụng unowned, hãy tuân theo các quy tắc sau:
// Ví dụ: tham chiếu unowned được ghi chép với lý do rõ ràng
class InvoiceLineItem {
let productName: String
let price: Decimal
// unowned Invoice — InvoiceLineItem không thể tồn tại nếu không có Invoice.
// Invoice tạo Item và xóa nó khi bị xóa.
// Đảm bảo: Invoice tồn tại ít nhất bằng Item.
unowned let invoice: Invoice
init(productName: String, price: Decimal, invoice: Invoice) {
self.productName = productName
self.price = price
self.invoice = invoice
}
}
// Đây là đảm bảo mạnh: Invoice xóa tất cả Item trong deinit.
// vi phạm đảm bảo = lỗi trong logic kinh doanh cần được sửa.
Ghi chép đảm bảo là một tiêu chuẩn chuyên nghiệp. Trong các dự án lớn (Airbnb, Uber), review mã yêu cầu lý do cho mỗi unowned. Nếu đảm bảo không rõ ràng, hãy sử dụng weak. Bình luận trên unowned giúp các nhà phát triển tương lai hiểu tại sao weak không được sử dụng ở đây và những điều kiện nào có thể phá vỡ đảm bảo.
Các câu hỏi thường gặp
Crash runtime với EXC_BAD_ACCESS. Swift không kiểm tra tính hợp lệ của tham chiếu unowned khi truy cập — nó chỉ là một con trỏ “thô”. Nếu đối tượng được giải phóng, bộ nhớ bị ghi đè và truy cập vào nó kết thúc một cách nghiêm trọng. Đây là một ngoại lệ không thể bắt được (không phải try-catch).
Có, nếu giao thức kế thừa từ AnyObject. Unowned hoạt động với tất cả các kiểu tham chiếu: lớp, giao thức AnyObject, đối tượng Objective-C. Kiểu giá trị (struct, enum) không hỗ trợ unowned vì chúng không tham gia vào ARC.
Khi đảm bảo vòng đời tuyệt đối và rõ ràng — unowned an toàn hơn từ góc độ thiết kế: nó không yêu cầu unwrap, không thể là nil và không che giấu lỗi. Nếu một đối tượng không thể tồn tại mà không có cha, unowned biến nó thành một hợp đồng rõ ràng, trong khi weak làm mờ đảm bảo.
Có: unowned nhanh hơn vì nó không yêu cầu truy cập vào bảng weak runtime để đặt về không. Trong hầu hết các ứng dụng, sự khác biệt không đáng kể, nhưng trong các kịch bản tải cao với hàng triệu truy cập, unowned có thể nhanh hơn 10–20% khi đọc.
Tái cấu trúc là mối nguy hiểm chính cho unowned. Thay đổi vòng đời của đối tượng (bộ đệm, hoạt động không đồng bộ, tái sử dụng) có thể phá vỡ đảm bảo. Trình biên dịch sẽ không cảnh báo. Giải pháp: di chuyển sang weak khi thay đổi kiến trúc hoặc thêm bình luận cảnh báo.
Tổng kết
unowned let hoặc unowned var; có thể không tùy chọn và tùy chọn (Swift 5.0+)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