Unowned Reference: nó là gì, cú pháp và ứng dụng trong các ứng dụng di động

Tác giả: IT Sectr Đã đăng: 2026-03-30 Thời gian đọc: 9 phút

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 — tham chiếu không sở hữu không tự động về không; không tùy chọn, không tăng retain count
  • Đảm bảo — được sử dụng khi đối tượng được đảm bảo không thể bị giải phóng trước đối tượng tham chiếu đến nó
  • Khác biệt với weak — unowned không được đặt về nil (rủi ro crash), weak được đặt về nil (an toàn)
  • Kịch bản — cha-con với đảm bảo vòng đời, closure với unowned self, singleton và Service Locator
  • Rủi ro — truy cập vào đối tượng unowned đã giải phóng gây ra crash runtime (EXC_BAD_ACCESS)

Unowned Reference là gì?

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.

Cú pháp unowned trong Swift

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.

swift
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

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.

Unowned Optional

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.

Unowned vs Weak: khi nào sử dụng cái gì

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íWeakUnowned
Tùy chọnCó (Type?)Không (Type)
Đặt về không khi giải phóngTự động về nilKhông (rủi ro con trỏ lơ lửng)
Kiểu (let/var)Chỉ varlet hoặc var
Hiệu suấtChi phí bảng weakTối thiểu (con trỏ đơn giản)
An toànAn toàn (nil được kiểm tra)Rủi ro EXC_BAD_ACCESS
Đảm bảo vòng đờiKhông yêu cầuYêu cầu đảm bảo rõ ràng

Quy tắc thực tế

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.

Unowned self trong Closures

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.

Khi unowned self an toàn

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.

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

Khi unowned self nguy hiểm

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].

swift
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).

Rủi ro của unowned và cách tránh

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.

Tái cấu trúc và thay đổi đảm bảo

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.

Unowned trong hệ thống phân cấp UIKit

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.

Thực hành tốt nhất

Để giảm rủi ro khi sử dụng unowned, hãy tuân theo các quy tắc sau:

  • Ưu tiên weak theo mặc định — weak an toàn, unowned là tối ưu hóa, không phải tiêu chuẩn
  • Ghi chép đảm bảo — cho mỗi unowned, viết bình luận với lý do
  • Tránh unowned trong ViewController — vòng đời UIKit không thể đoán trước cho đảm bảo unowned
  • Chỉ sử dụng unowned cho closure đồng bộ — sorted, filter, map là ứng viên an toàn
  • Kiểm tra trong quá trình review mã — mỗi unowned yêu cầu lý do từ tác giả mã
  • Di chuyển sang weak khi có nghi ngờ nhỏ nhất — mất mát về khả năng đọc (một guard let) ít hơn crash trong sản phẩm
swift
// 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

Điều gì xảy ra khi truy cập tham chiếu unowned sau khi đối tượng được giải phóng?

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ó thể sử dụng unowned với giao thức không?

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 nào unowned an toàn hơn weak?

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ó sự khác biệt về hiệu suất giữa unowned và weak không?

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 ảnh hưởng đến đảm bảo unowned như thế nào?

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 Reference — tham chiếu không sở hữu không đặt về không; không tùy chọn, không tăng retain count
  • Đảm bảo — yêu cầu bằng chứng rõ ràng rằng đối tượng tồn tại ít nhất bằng mã tham chiếu đến nó
  • Cú phápunowned let hoặc unowned var; có thể không tùy chọn và tùy chọn (Swift 5.0+)
  • Unowned vs Weak — unowned nhanh hơn và sạch hơn, nhưng weak an toàn hơn; weak là lựa chọn mặc định
  • Closure — unowned self chỉ cho closure đồng bộ; không đồng bộ yêu cầu [weak self]
  • Tài liệu — mỗi unowned phải có bình luận giải thích đảm bảo
  • Khuyến nghị — khi nghi ngờ, chọn weak; unowned dành cho hợp đồng rõ ràng và được ghi chép

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.

Thảo luận dự án

Đọc thêm