Unowned Reference (การอ้างอิงแบบไม่มีเจ้าของ) คือการอ้างอิงแบบไม่เป็นเจ้าของใน Swift ที่ไม่เพิ่ม retain count ของออบเจ็กต์และไม่เหมือนกับ weak ตรงที่ไม่ถูกตั้งค่าเป็น nil หลังจากออบเจ็กต์ถูกปลดปล่อย ตาม Apple Swift Language Guide, 2026 จะใช้ unowned เมื่อรับประกันว่าออบเจ็กต์มีอายุอย่างน้อยเท่ากับออบเจ็กต์ที่อ้างอิงถึงมัน ไม่เหมือนกับ Weak Reference ตรงที่ unowned ไม่จำเป็นต้อง unwrap — มันเป็นชนิดที่ไม่จำเป็นต้องเลือก (non-optional) ซึ่งทำให้โค้ดสะอาดขึ้น แต่ส่งต่อความรับผิดชอบในการรับประกันอายุให้กับนักพัฒนา
ประเด็นสำคัญ
Unowned Reference คือการอ้างอิงแบบไม่เป็นเจ้าของไปยังออบเจ็กต์ใน ARC ที่ไม่เพิ่ม retain count ของมัน ไม่เหมือนกับ weak ตรงที่การอ้างอิง unowned จะไม่ถูกทำให้เป็นศูนย์หลังจากการจัดสรรหน่วยความจำของออบเจ็กต์: มันยังคงชี้ไปยังหน่วยความจำที่ถูกปลดปล่อยแล้ว การเข้าถึงการอ้างอิงดังกล่าวทำให้เกิดการหยุดทำงานขณะ运行时ด้วย EXC_BAD_ACCESS
คำว่า “ไม่มีเจ้าของ” สะท้อนถึงความหมาย: ออบเจ็กต์มีอยู่ แต่ไม่มีใครรับผิดชอบอายุของมัน นักพัฒนาประกาศอย่างชัดเจน: “ฉันรับประกันว่าออบเจ็กต์นี้จะมีชีวิตอยู่ตราบเท่าที่ฉันอ้างอิงถึงมัน” คอมไพเลอร์ไม่ตรวจสอบการรับประกันนี้ — มันเป็นสัญญาในระดับนักพัฒนา
ตาม Swift.org Documentation, 2026 การอ้างอิง unowned เป็นที่นิยมมากกว่า weak ในสถานการณ์ที่มีการรับประกันอายุเพราะ: ไม่ต้องการชนิดที่เลือกได้ (โค้ดสะอาดกว่า), ไม่ต้องการ unwrap (ลด force-unwrap หรือ guard let), และไม่มีค่าใช้จ่ายในการบำรุงรักษาตาราง weak สำหรับการทำให้เป็นศูนย์ อย่างไรก็ตาม การละเมิดสัญญาใด ๆ จะส่งผลให้เกิดการหยุดทำงาน
ใน Swift การอ้างอิง unowned ถูกประกาศด้วยคำหลัก unowned ก่อน let หรือ var ไม่เหมือนกับ weak ตรงที่ unowned สามารถเป็น ทั้ง let และ var และไม่ต้องการชนิดที่เลือกได้ คุณสมบัตินี้ทำให้ unowned สะดวกสำหรับการอ้างอิงที่ไม่สามารถเป็น nil ตามตรรกะของโดเมน
class Country {
let name: String
var capital: City! // จะถูกตั้งค่าหลังจากการเริ่มต้น
init(name: String) { self.name = name }
}
class City {
let name: String
unowned let country: Country // ✅ unowned let — การรับประกันอายุ
init(name: String, country: Country) {
self.name = name
self.country = country
}
}
// การใช้งาน
let france = Country(name: "France")
let paris = City(name: "Paris", country: france)
france.capital = paris
// ✅ Country → City (strong), City → Country (unowned) — ไม่มี retain cycle
ในตัวอย่างนี้ City unowned let country — เมืองไม่สามารถดำรงอยู่ได้โดยไม่มีประเทศ หากประเทศหายไป เมือง (และการอ้างอิง) จะสูญเสียความหมาย ทางความหมายแล้ว นี่เป็นกรณีที่เหมาะสำหรับ unowned: การรับประกันอายุมีอยู่ ไม่จำเป็นต้องเลือก ไม่เกิด retain cycle
unowned var อนุญาตให้ใช้ได้แต่พบได้น้อยกว่า ใช้เมื่อการอ้างอิงอาจถูกแทนที่ (เช่น การเชื่อมลูกเข้ากับพ่อแม่คนอื่น) เมื่อกำหนดค่าใหม่ การจัดสรรหน่วยความจำของออบเจ็กต์เก่าเป็นความรับผิดชอบของเจ้าของภายนอก
ใน Swift 5.0+ ได้มีการนำการสนับสนุน unowned แบบเลือกได้ (unowned let x: Type?) มาใช้ นี่คือการประนีประนอม: unowned รับประกันว่าหากการอ้างอิงไม่ใช่ nil ออบเจ็กต์จะยังมีชีวิตอยู่ พฤติกรรมเมื่อถูกจัดสรรหน่วยความจำคือการหยุดทำงาน เช่นเดียวกับ unowned ทั่วไป
การเลือกระหว่าง unowned และ weak เป็นหนึ่งในการตัดสินใจที่พบบ่อยเมื่อออกแบบสถาปัตยกรรม Swift มาดูเกณฑ์และคำแนะนำสำหรับแต่ละกรณีกัน
| เกณฑ์ | Weak | Unowned |
|---|---|---|
| เลือกได้ | ใช่ (Type?) | ไม่ (Type) |
| ทำให้เป็นศูนย์เมื่อจัดสรร | อัตโนมัติเป็น nil | ไม่ (เสี่ยงพอยน์เตอร์ลอย) |
| ชนิด (let/var) | var เท่านั้น | let หรือ var |
| ประสิทธิภาพ | ค่าใช้จ่ายตาราง weak | น้อยที่สุด (พอยน์เตอร์ธรรมดา) |
| ความปลอดภัย | ปลอดภัย (ตรวจสอบ nil) | เสี่ยง EXC_BAD_ACCESS |
| การรับประกันอายุ | ไม่จำเป็น | ต้องการการรับประกันที่ชัดเจน |
ใช้ weak หากมีข้อสงสัยแม้เพียงเล็กน้อยเกี่ยวกับอายุของออบเจ็กต์ Weak ปลอดภัย ชัดเจน และไม่ต้องการหลักฐาน ใช้ unowned เฉพาะเมื่อคุณสามารถกำจัดสถานการณ์ทั้งหมดที่ออบเจ็กต์อาจถูกจัดสรรก่อนได้ กรณีทั่วไป: ลูกที่ไม่มีพ่อแม่ดำรงอยู่ไม่ได้; closure ที่ทำงานแบบซิงโครนัส; การเข้าถึงออบเจ็กต์ภายใน initializer ของมัน
ตาม Airbnb Swift Style Guide, 2025 ในฐานโค้ดขนาดใหญ่ แนะนำให้ใช้ weak เป็นค่าเริ่มต้น และใช้ unowned เฉพาะกับความคิดเห็นที่ชัดเจนซึ่งอธิบายการรับประกันอายุ ซึ่งช่วยลดความเสี่ยงของการหยุดทำงานที่ไม่ชัดเจนระหว่างการปรับโครงสร้าง
Closures เป็นกรณีการใช้งานที่พบบ่อยเป็นอันดับสองสำหรับ unowned รองจากความสัมพันธ์พ่อแม่-ลูก รายการจับ [unowned self] ใช้เมื่อรับประกันว่า self มีอายุยืนกว่า closure มาดูสถานการณ์ที่ถูกต้องและไม่ถูกต้องกัน
Closure แบบซิงโครนัส — sorted, filter, map พวกมันทำงานทันทีในเธรดปัจจุบัน self ยังมีชีวิตอยู่อย่างแน่นอน รายการจับกับ unowned เป็นที่ยอมรับได้ที่นี่และให้โค้ดที่สะอาดกว่า
class DataProcessor {
var items: [Int] = [3, 1, 4, 1, 5]
func processSorted() {
// ✅ unowned self — sorted ทำงานแบบซิงโครนัส self รับประกันว่ายังมีชีวิต
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 แบบอะซิงโครนัส — ที่มีความล่าช้า คำขอเครือข่าย แอนิเมชัน Self อาจถูกจัดสรรหน่วยความจำระหว่างการจัดกำหนดการ closure และการทำงานของมัน ที่นี่ unowned self นำไปสู่การหยุดทำงาน ใช้ [weak self]
class NetworkLoader {
func loadData() {
// ❌ อันตราย: unowned self ใน closure แบบอะซิงโครนัส
URLSession.shared.dataTask(with: url) { [unowned self] data, _, _ in
self.handleResponse(data) // หยุดทำงานหาก self ถูกปลดปล่อย
}.resume()
}
func handleResponse(_ data: Data?) { }
// ✅ ถูกต้อง: weak self + guard
func loadDataSafe() {
URLSession.shared.dataTask(with: url) { [weak self] data, _, _ in
guard let self else { return }
self.handleResponse(data)
}.resume()
}
}
จำกฎไว้: unowned self — เฉพาะสำหรับ closure แบบซิงโครนัสที่ทำงานทันที สำหรับ closure แบบอะซิงโครนัส ให้ใช้ weak self + guard let เสมอ ข้อยกเว้น: หากคุณเก็บการอ้างอิงไปยังออบเจ็กต์อย่างชัดเจนจนกว่า closure จะเสร็จสมบูรณ์ (เช่น การเก็บการอ้างอิงที่แข็งแกร่งในตัวแปรอื่น)
Unowned เป็นเครื่องมือที่ทรงพลังแต่อันตราย มาดูสถานการณ์จริงที่ unowned อาจนำไปสู่การหยุดทำงานและวิธีการลดความเสี่ยงกัน
ความเสี่ยงหลักของ unowned คือการเปลี่ยนแปลงในตรรกะทางธุรกิจที่ทำให้การรับประกันอายุเป็นโมฆะ นักพัฒนาปรับโครงสร้างโค้ด: เปลี่ยนความเป็นเจ้าของ, แนะนำการปลดปล่อยที่เลื่อนออกไป, เพิ่มการแคช — และการอ้างอิง unowned กลายเป็นระเบิดเวลา คอมไพเลอร์จะไม่เตือน — เฉพาะการหยุดทำงานบนอุปกรณ์ของผู้ใช้
คำแนะนำ: ใช้ unowned เฉพาะเมื่อการรับประกันอายุชัดเจนและถูกบันทึกเป็นเอกสาร เพิ่มความคิดเห็นในแต่ละ unowned: ทำไมการอ้างอิงนี้ถึงปลอดภัยและภายใต้เงื่อนไขใดที่มันอาจถูกละเมิด
UIKit เป็นพื้นที่ที่มีความเสี่ยงสูงสำหรับ unowned ViewController สามารถถูกจัดสรรหน่วยความจำได้ตลอดเวลาในระหว่างการนำทาง (pop, dismiss), การโหลดหน่วยความจำออก หรือการเปลี่ยนทิศทาง หากคุณส่ง ViewController ไปยัง closure ด้วย unowned self self อาจเป็น nil เมื่อกลับจากพื้นหลังหรือเมื่อแอนิเมชันเสร็จสมบูรณ์
เพื่อลดความเสี่ยงเมื่อใช้ unowned ให้ปฏิบัติตามกฎเหล่านี้:
// ตัวอย่าง: การอ้างอิง unowned ที่ถูกบันทึกเป็นเอกสารพร้อมเหตุผลที่ชัดเจน
class InvoiceLineItem {
let productName: String
let price: Decimal
// unowned Invoice — InvoiceLineItem ไม่สามารถดำรงอยู่ได้หากไม่มี Invoice
// Invoice สร้าง Item และลบมันเมื่อถูกลบ
// การรับประกัน: Invoice มีอายุอย่างน้อยเท่ากับ Item
unowned let invoice: Invoice
init(productName: String, price: Decimal, invoice: Invoice) {
self.productName = productName
self.price = price
self.invoice = invoice
}
}
// นี่คือการรับประกันที่แข็งแกร่ง: Invoice ลบ Item ทั้งหมดใน deinit
// การละเมิดการรับประกัน = บั๊กในตรรกะทางธุรกิจที่ต้องแก้ไข
การบันทึกการรับประกันเป็นเอกสารเป็นมาตรฐานทางวิชาชีพ ในโครงการขนาดใหญ่ (Airbnb, Uber) การตรวจสอบโค้ดต้องมีเหตุผลสำหรับแต่ละ unowned หากการรับประกันไม่ชัดเจน ให้ใช้ weak ความคิดเห็นบน unowned ช่วยให้นักพัฒนาในอนาคตเข้าใจว่าทำไมไม่ใช้ weak ที่นี่และเงื่อนไขใดที่อาจทำลายการรับประกัน
คำถามที่พบบ่อย
การหยุดทำงานขณะ运行时 ด้วย EXC_BAD_ACCESS Swift ไม่ตรวจสอบความถูกต้องของการอ้างอิง unowned เมื่อเข้าถึง — มันเป็นเพียงพอยน์เตอร์ “ดิบ” หากออบเจ็กต์ถูกปลดปล่อย หน่วยความจำจะถูกเขียนทับและการเข้าถึงมันจะสิ้นสุดอย่างร้ายแรง นี่เป็นข้อยกเว้นที่ไม่สามารถจับได้ (ไม่ใช่ try-catch)
ได้ ถ้าโปรโตคอลสืบทอดจาก AnyObject Unowned ทำงานกับชนิดอ้างอิงทั้งหมด: คลาส, โปรโตคอล AnyObject, ออบเจ็กต์ Objective-C ชนิดค่า (struct, enum) ไม่รองรับ unowned เพราะพวกมันไม่มีส่วนร่วมใน ARC
เมื่อการรับประกันอายุสมบูรณ์และชัดเจน — unowned ปลอดภัยกว่าจากมุมมองการออกแบบ: ไม่ต้องการ unwrap, ไม่สามารถเป็น nil, และไม่ปิดบังข้อผิดพลาด หากออบเจ็กต์ไม่สามารถดำรงอยู่ได้โดยไม่มีพ่อแม่ unowned ทำให้มันเป็นสัญญาที่ชัดเจน ในขณะที่ weak ทำให้การรับประกันไม่ชัดเจน
ใช่: unowned เร็วกว่าเพราะไม่ต้องการเข้าถึงตาราง weak ขณะ运行时เพื่อทำให้เป็นศูนย์ ในแอปพลิเคชันส่วนใหญ่ความแตกต่างไม่รู้สึกได้ แต่ในสถานการณ์ที่มีโหลดสูงที่มีการเข้าถึงหลายล้านครั้ง unowned อาจเร็วกว่า 10–20% ในการอ่าน
การปรับโครงสร้าง เป็นอันตรายหลักสำหรับ unowned การเปลี่ยนอายุของออบเจ็กต์ (การแคช, การดำเนินการแบบอะซิงโครนัส, การใช้ซ้ำ) อาจทำลายการรับประกัน คอมไพเลอร์จะไม่เตือน วิธีแก้ไข: ย้ายไปใช้ weak เมื่อเปลี่ยนสถาปัตยกรรมหรือเพิ่มความคิดเห็นเตือน
สรุป
unowned let หรือ unowned var; สามารถเป็นแบบไม่เลือกและเลือกได้ (Swift 5.0+)เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม