Weak Reference (referensi lemah) — adalah referensi ke objek yang tidak meningkatkan penghitung retensinya di ARC. Menurut Apple Swift Language Guide, 2026, referensi weak dideklarasikan dengan kata kunci weak dan selalu memiliki tipe opsional. Ketika objek dibebaskan, semua referensi weak ke objek tersebut secara otomatis diatur ke nil, yang mencegah dangling pointer dan menjadikan referensi lemah mekanisme aman untuk memutus retain cycle.
Utama
weak sebelum var; tipe selalu opsional (?)Weak Reference — adalah referensi tidak memiliki ke objek di ARC (Automatic Reference Counting). Berbeda dengan referensi strong yang meningkatkan retain count objek dan menjamin hidupnya, referensi weak memungkinkan objek dibebaskan meskipun masih ada referensi ke objek tersebut. Setelah dibebaskan, referensi weak secara otomatis diatur ke nil — ini disebut zeroing weak.
Zeroing weak — fitur kunci runtime Swift dan Objective-C. Ketika penghitung referensi objek mencapai nol dan objek didealokasi, runtime menelusuri semua referensi weak ke objek ini (disimpan dalam tabel weak khusus) dan mengaturnya ke nil. Ini menjamin bahwa akses ke memori yang dibebaskan (use-after-free) tidak mungkin melalui referensi weak — pembacaan apa pun mengembalikan nil.
Menurut Apple WWDC 2012 Session 406, referensi zeroing weak menghilangkan seluruh kelas bug crash terkait dangling pointer, yang umum dalam manajemen memori manual (MRR). Di MRR referensi lemah hanya ada sebagai __unsafe_unretained — mereka tidak menjadi nol dan akses ke objek yang dibebaskan menyebabkan EXC_BAD_ACCESS.
Mari kita lihat sintaksis deklarasi referensi weak di kedua bahasa ekosistem Apple. Meskipun runtime-nya sama, sintaksisnya berbeda, tetapi semantiknya identik.
Di Swift referensi weak dideklarasikan dengan kata kunci weak sebelum var. Tipe harus selalu opsional (Type?), karena referensi dapat menjadi nol kapan saja. Konstanta (let) tidak bisa weak — hanya variabel.
class ViewController: UIViewController {
// properti weak: hanya var, hanya optional
weak var delegate: ViewControllerDelegate?
weak var parentView: UIView?
weak var completionHandler: ((Bool) -> Void)? // ⚠️ closure tidak menyimpan weak
// ⬆️ Kesalahan: weak hanya dapat diterapkan ke class-types, bukan ke closure
}
Penting: weak hanya berlaku untuk instance kelas (class-types), AnyObject dan protokol yang mewarisi dari AnyObject. Struct, enum dan closure tidak bisa weak — mereka adalah tipe nilai dan tidak berpartisipasi dalam ARC.
Di Objective-C properti weak dideklarasikan melalui atribut __weak atau pengubah weak di property:
// Objective-C: weak property
@interface MyViewController : UIViewController
@property (weak, nonatomic) id<MyDelegate> delegate;
@end
// Variabel weak lokal
__weak MyObject *weakRef = someStrongObject;
Runtime Objective-C juga menyediakan zeroing weak, tetapi juga memblokir penggunaan weak dengan struktur C dan beberapa objek Core Foundation. Untuk mereka digunakan __unsafe_unretained — tanpa zeroing.
Referensi lemah — bukan solusi universal, melainkan alat untuk skenario tertentu. Menggunakan weak di mana-mana menyebabkan kompleksitas berlebihan dan mengurangi keterbacaan. Mari kita lihat skenario penerapan yang benar.
Delegat — skenario utama untuk weak. Objek pemilik (misalnya UITableView) menyimpan referensi strong ke dirinya sendiri, dan delegat (UIViewController) tidak boleh memiliki tabel. Apple SDK menjamin bahwa semua delegate dan dataSource bersifat weak. Untuk protokol Anda sendiri selalu gunakan weak var delegate.
Ketika objek anak perlu merujuk ke induk (misalnya ChildViewController untuk akses ke koordinator), gunakan referensi weak. Induk memiliki anak (strong), anak mengamati induk (weak) — retain cycle dikecualikan.
Capture list [weak self] — cara standar untuk menghindari retain cycle di closure yang disimpan sebagai properti kelas. Jika self dapat dibebaskan sebelum closure selesai — weak self wajib.
| Skenario | Weak | Strong |
|---|---|---|
| Delegate | ✅ Selalu weak | ❌ Retain cycle |
| Parent → Child | ❌ Tidak perlu (induk harus memiliki) | ✅ Strong |
| Child → Parent | ✅ Weak | ❌ Retain cycle |
| Callback asinkron | ✅ [weak self] | ❌ Risiko retain cycle |
| Ikatan kuat (owned) | ❌ unowned | ✅ Strong |
Aturan umum: jika objek A memiliki B (A → B strong), maka B → A harus weak atau unowned. Arah referensi strong harus selalu dari pemilik ke bawahan.
Baik weak maupun unowned tidak meningkatkan retain count, tetapi berbeda dalam perilaku setelah dealokasi objek. Pilihan di antara mereka adalah masalah jaminan masa hidup.
Weak: secara otomatis menjadi nol (nil), tipe selalu optional, memerlukan unwrap sebelum digunakan. Aman — akses ke nil tidak menyebabkan crash.
Unowned: tidak menjadi nol, tipe non-optional. Jika objek telah dibebaskan, referensi unowned menjadi dangling pointer — akses ke sana menyebabkan runtime crash. Unowned mengasumsikan bahwa objek hidup tidak lebih pendek dari pihak yang mereferensikan.
Weak pilih jika: objek dapat didealokasi kapan saja (delegat setelah menutup layar), Anda tidak mengontrol masa hidup objek, atau ragu tentang jaminan. Weak — pilihan aman universal.
Unowned pilih jika: objek dijamin tidak dapat dibebaskan lebih awal dari yang mereferensikan (misalnya Customer → CreditCard, di mana kartu tidak ada tanpa pelanggan). Unowned memberikan API non-optional tanpa unwrap, yang lebih nyaman dalam kode.
class Order {
let id: Int
var items: [Item] = []
init(id: Int) { self.id = id }
// Ikatan kuat: Order memiliki Item
func addItem(name: String) {
let item = Item(name: name, order: self)
items.append(item)
}
}
class Item {
let name: String
unowned let order: Order // ✅ unowned — Item tidak hidup tanpa Order
init(name: String, order: Order) {
self.name = name
self.order = order
}
}
// Contoh dengan weak: delegat tanpa jaminan masa hidup
protocol NetworkServiceDelegate: AnyObject {
func didReceiveResponse(data: Data)
}
class NetworkService {
weak var delegate: NetworkServiceDelegate? // ✅ weak — delegat bisa pergi
}
Dalam contoh Item menggunakan unowned, karena elemen pesanan tidak dapat ada tanpa pesanan itu sendiri — jaminan masa hidup sangat kuat. NetworkService menggunakan weak, karena delegat (misalnya ViewController) dapat ditutup dan dibebaskan kapan saja.
Referensi lemah — alat yang kuat, tetapi memiliki keterbatasan yang penting dipahami untuk penerapan yang benar dalam pengembangan iOS.
Referensi lemah lebih lambat dari strong: setiap akses runtime memeriksa apakah objek telah dibebaskan (lookup di tabel weak). Di sebagian besar skenario perbedaannya tidak terasa, tetapi di loop panas dengan jutaan akses weak bisa menjadi hambatan. Untuk skenario dengan beban tinggi gunakan strong dan reorganisasi arsitektur.
Struct, enum, tuple — tipe nilai yang tidak berpartisipasi dalam ARC. Mencoba mendeklarasikan weak struct menyebabkan kesalahan kompilasi. Untuk menyimpan referensi lemah ke tipe nilai gunakan pembungkus dalam class-type atau closure.
Zeroing weak aman untuk thread: jika objek dibebaskan di satu thread, referensi weak menjadi nol di semua thread secara atomik. Namun jarak antara membaca referensi weak dan menggunakannya dapat menyebabkan kondisi balapan — objek dibebaskan antara mendapatkan referensi weak dan menggunakannya. Solusi: strong-pengambilan referensi lemah ke variabel lokal.
// Kondisi balapan dengan weak di multi-threading
func performAsync() {
weak var weakSelf = self
queue.async {
// ⚠️ weakSelf bisa nil antara pemeriksaan dan penggunaan
if weakSelf != nil {
weakSelf!.doSomething() // CRASH jika menjadi nil
}
}
}
// ✅ Perbaikan: pengambilan kuat untuk durasi penggunaan
func performAsyncSafe() {
queue.async { [weak self] in
guard let strongSelf = self else { return }
strongSelf.doSomething() // strongSelf — referensi strong lokal
}
}
Dalam varian aman weak self diambil, kemudian segera dibuka ke variabel strong lokal strongSelf. Jika self masih hidup — akan tetap hidup selama eksekusi blok. Jika tidak — guard aktif dan kode tidak dijalankan. Idiom ini — pola standar untuk closure asinkron di Swift.
IBOutlet di Interface Builder harus weak, karena hierarki view sudah menyimpan referensi strong ke subview. Menduplikasi referensi strong di kontroler tidak membuat retain cycle, tetapi berlebihan. Referensi lemah ke outlet — rekomendasi Apple, meskipun banyak pengembang menggunakan strong untuk menyederhanakan kode.
Pertanyaan yang Sering Diajukan
Tidak, weak hanya bisa menunjuk ke objek yang ada atau nil. Saat membuat objek baru, Anda mendapatkan referensi strong terlebih dahulu (melalui inisialisasi), dan baru kemudian dapat menetapkan referensi lemah. weak nil di awal — keadaan normal.
Weak didasarkan pada ARC yang hanya mengelola tipe referensi (kelas). Tipe nilai (struct, enum) disalin saat ditugaskan dan tidak memiliki retain count. Untuk hubungan lemah tipe nilai gunakan closure atau pembungkus di class dengan properti weak.
Setiap akses ke referensi weak melakukan lookup di tabel runtime. Dalam loop dengan jutaan iterasi ini bisa 2–5 kali lebih lambat dari referensi strong. Untuk jalur panas salin weak ke variabel strong lokal sebelum loop.
Ketika semua referensi strong ke objek hilang — di akhir scope, saat mengatur ulang properti, saat menutup layar. Di lingkungan multi-thread ini bisa terjadi di antara dua baris kode. Selalu periksa weak melalui guard let atau if let.
Secara semantik identik: keduanya menyediakan zeroing weak. Perbedaan: Swift memerlukan tipe optional dan var, Objective-C menggunakan pengubah property. Objective-C juga mendukung __unsafe_unretained — referensi lemah tanpa zeroing (risiko dangling pointer).
Ringkasan
weak var + tipe optional; hanya class-types dan protokol AnyObjectKami akan mengembangkan aplikasi seluler turnkey
IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.
Baca juga