Strong Reference (referensi kuat) — adalah mekanisme standar manajemen memori di mana objek tetap berada di memori selama setidaknya ada satu referensi aktif yang menunjuk ke sana. Berbeda dengan referensi lemah, referensi kuat meningkatkan penghitung referensi objek dan mencegah pelepasan otomatisnya. Menurut Apple Developer Documentation, ARC secara otomatis mengelola masa hidup objek di Swift dan Objective-C. Memahami cara kerja referensi kuat sangat penting untuk mencegah kebocoran memori dan dependensi siklik dalam aplikasi mobile.
Poin utama
Strong Reference — adalah jenis referensi ke objek yang mencegah penghancurannya oleh pengumpul sampah atau sistem manajemen memori. Selama setidaknya ada satu referensi kuat ke objek, memori di bawahnya tidak dibebaskan. Ini adalah mekanisme dasar yang menjadi dasar ARC di Swift dan Objective-C serta pengumpulan sampah di Java dan Kotlin.
Konsep referensi kuat sangat fundamental untuk semua bahasa dengan manajemen memori otomatis. Dalam sistem dengan ARC, setiap referensi kuat meningkatkan penghitung referensi objek. Ketika penghitung turun ke nol, objek segera dialokasikan. Di Java dan Kotlin dengan pengumpul sampah, referensi kuat memastikan bahwa objek dapat dijangkau dan tidak akan dikumpulkan oleh GC.
Menurut data WWDC 2021, sekitar 35% kebocoran memori di aplikasi iOS terkait dengan penggunaan referensi kuat yang tidak tepat dan siklus retensi. Dalam pengembangan Android, kebocoran melalui strong reference implisit di penutupan dan panggilan balik adalah penyebab paling umum kedua masalah memori setelah Context Leak.
Untuk bekerja secara efisien dengan memori, perlu memahami perbedaan antara referensi strong, weak dan unowned serta memilih jenis referensi yang tepat tergantung pada kepemilikan dan masa hidup objek.
Sebelum diperkenalkannya ARC, pengembang secara manual memanggil retain dan release untuk setiap objek, yang menyebabkan banyak kesalahan. ARC, diperkenalkan oleh Apple pada tahun 2011 dengan rilis LLVM 3.0, mengotomatiskan proses ini dengan menganalisis grafik kepemilikan pada tahap kompilasi. Kompiler sendiri menyisipkan panggilan retain, release dan autorelease di tempat yang tepat.
Menurut Clang Static Analyzer, pengenalan ARC mengurangi jumlah bug terkait memori di aplikasi iOS sebesar 70%. Bagi pengembang, ini berarti manajemen memori menjadi lebih aman, tetapi pada saat yang sama muncul kebutuhan untuk memahami bagaimana referensi kuat bekerja di balik layar — untuk menghindari retain cycles.
Di Kotlin dan Java, peran ARC dilakukan oleh pengumpul sampah, tetapi prinsip referensi kuat tetap sama: GC Roots — ini adalah titik masuk di mana objek dipertahankan oleh referensi kuat. Selama objek dapat dijangkau melalui rantai referensi kuat dari GC Root, objek tersebut tidak akan dikumpulkan.
ARC (Automatic Reference Counting) bekerja berdasarkan prinsip penghitungan referensi untuk setiap objek di heap. Ketika referensi kuat baru ke objek dibuat, penghitung meningkat (retain). Ketika referensi dihancurkan atau ditimpa, penghitung menurun (release). Ketika penghitung mencapai nol, objek segera dihapus dari memori.
Mari kita lihat contoh di Swift. Saat membuat instance kelas, ARC mengalokasikan memori dan menetapkan retain count ke 1. Setiap penugasan baru ke variabel lain meningkatkan penghitung. Ketika variabel keluar dari ruang lingkup, penghitung menurun:
class ProfileViewController {
var nameLabel: String?
var avatarImage: UIImage?
func loadProfile() {
// retain count = 1 untuk instance baru
let user = User(name: "Ivan")
// retain count = 2 setelah penugasan nameLabel
nameLabel = user.name
// keluar dari metode — user keluar dari scope, retain count = 1
}
}
Dalam kode ini, ARC memastikan bahwa objek User tetap di memori selama setidaknya ada satu referensi kuat ke sana. Ketika fungsi loadProfile selesai, variabel lokal user dihancurkan, tetapi nameLabel masih menahan objek. Memori akan dibebaskan hanya ketika nameLabel tidak lagi ada atau ditimpa.
Di Kotlin, perilaku serupa disediakan melalui GC Roots. Selama ada rantai strong references yang dapat dilacak dari akar pengumpul sampah (misalnya, bidang statis atau thread aktif), objek tetap di memori. Perbedaannya adalah bahwa GC tidak membebaskan memori secara instan — ini terjadi secara asinkron setelah analisis keterjangkauan.
Di ARC, pelepasan terjadi secara sinkron pada saat penghitung dinolkan. Di Swift dan Objective-C, Anda tahu persis kapan objek akan dihapus. Di Kotlin dan Java, momen pelepasan tidak dapat diprediksi, tetapi ini dikompensasi oleh skema deteksi dependensi siklik yang lebih fleksibel di tingkat pengumpul sampah.
Retain cycle (siklus retensi) — situasi di mana dua atau lebih objek memiliki referensi kuat timbal balik satu sama lain. Akibatnya, retain count mereka tidak pernah turun ke nol, dan memori tidak dibebaskan bahkan setelah objek tidak lagi dibutuhkan oleh aplikasi.
Contoh klasik: view controller induk menahan objek anak dengan referensi kuat, dan ia pada gilirannya menahan induk dengan referensi kuat. Ini tipikal untuk situasi dengan delegasi, penutupan, dan ekspresi lambda bersarang. Menurut Instruments Leaks, retain cycles merupakan hingga 60% dari semua kebocoran memori di aplikasi yang menggunakan ARC.
class ParentViewController: UIViewController {
var child: ChildViewController?
func setupChild() {
child = ChildViewController()
// retain cycle: parent menahan child, child menahan parent melalui closure
child?.onEvent = {
self.handleEvent()
}
}
func handleEvent() {}
}
Masalahnya di sini adalah bahwa penutupan onEvent menangkap self (ParentViewController) dengan referensi kuat, dan ParentViewController sendiri menahan child dengan referensi kuat. Kedua objek tidak akan pernah dibebaskan. Solusinya — gunakan weak self di penutupan untuk memutus siklus.
Di Kotlin, siklus serupa muncul saat menggunakan lambda yang menangkap objek eksternal. Pengumpul sampah JVM dapat mendeteksi siklus semacam itu seiring waktu, tetapi hanya jika objek tidak dapat dijangkau dari GC Roots. Jika siklus terkait dengan thread aktif atau konteks UI, kebocoran tetap ada sepanjang masa hidup aplikasi.
Memahami perbedaan antara jenis referensi — kunci manajemen memori yang aman. Strong Reference meningkatkan retain count. Weak Reference tidak meningkatkan retain count dan secara otomatis menjadi nil saat objek dibebaskan. Unowned Reference juga tidak meningkatkan retain count, tetapi tidak dinolkan — merujuknya setelah pembebasan menyebabkan crash.
| Jenis referensi | Retain count | Keamanan | Kapan digunakan |
|---|---|---|---|
| Strong | +1 | Aman (default) | Kepemilikan objek, relasi parent → child |
| Weak | Tidak berubah | Peniadaan otomatis (safe) | Delegasi, callback, referensi balik |
| Unowned | Tidak berubah | Risiko crash saat referensi tertunda | Ketika objek dipastikan hidup lebih lama dari pemilik |
Pemilihan jenis referensi ditentukan oleh hubungan kepemilikan. Jika objek B adalah bagian dari A dan tidak dapat ada tanpanya — gunakan Strong. Jika B dapat ada secara independen dan merujuk ke A untuk pemberitahuan — gunakan Weak. Unowned jarang diterapkan — hanya ketika masa hidup objek anak secara ketat tidak melebihi masa hidup induk.
Apple Developer Documentation merekomendasikan: secara default gunakan strong untuk semua hubungan kepemilikan. Jika perlu menghindari retain cycle — tentukan referensi mana yang harus lemah. Biasanya ini adalah referensi balik dalam hierarki (child → parent). Di Kotlin, peran serupa dimiliki oleh WeakReference dari java.lang.ref, yang diterapkan untuk cache dan pola observer.
Mendeteksi retain cycles — langkah pertama. Kedua — menghilangkannya dengan benar. Alat utama untuk memerangi siklus referensi kuat adalah mengganti salah satu referensi dengan weak atau unowned. Dalam bahasa dengan pengumpulan sampah, tambahan diterapkan WeakReference dengan pemeriksaan null manual sebelum setiap akses.
Di Swift dan Objective-C, perbaikan paling umum adalah menambahkan [weak self] di penutupan. Ini memastikan bahwa penutupan tidak menahan objek setelah dibebaskan. Di Kotlin, untuk tujuan serupa digunakan wrapper WeakReference atau pembersihan eksplisit referensi di onDestroy.
class NetworkService {
func fetchData(completion: @escaping (Data?) -> Void) {
// penangkapan melalui weak self — retain cycle dikecualikan
URLSession.shared.dataTask(
with: URL(string: "https://api.example.com")!
) { [weak self] data, response, error in
guard let self else { return }
completion(data)
}.resume()
}
}
Dalam contoh ini, [weak self] memastikan bahwa NetworkService tidak akan ditahan oleh penutupan setelah tidak lagi diperlukan. Jika self telah dibebaskan sebelum permintaan selesai — guard let self else { return } keluar dari penutupan tanpa memanggil completion.
Untuk diagnosa retain cycles, gunakan Instruments Leaks untuk iOS atau Android Profiler + LeakCanary untuk Android. Alat-alat ini menunjukkan grafik retensi yang tepat dan menunjukkan referensi kuat mana yang mencegah pembebasan objek. Profiling memori secara teratur harus menjadi bagian dari pipeline CI/CD setiap proyek mobile.
Swift dan Kotlin menggunakan mekanisme manajemen memori yang fundamentally berbeda, tetapi konsep referensi kuat ada di keduanya. Di Swift, ARC diterapkan dengan pelepasan sinkron saat retain count = 0. Di Kotlin, digunakan GC pelacak yang secara asinkron membersihkan objek yang tidak dapat dijangkau.
| Parameter | Swift (ARC) | Kotlin (JVM GC) |
|---|---|---|
| Mekanisme | Penghitungan referensi (retain count) | Pelacakan keterjangkauan (GC Roots) |
| Pelepasan | Sinkron (saat penghitung dinolkan) | Asinkron (melalui siklus GC) |
| Retain cycle | Tidak terdeteksi secara otomatis | GC dapat mendeteksi, tetapi tidak segera |
| Weak ref | weak (peniadaan otomatis) | WeakReference (pemeriksaan manual) |
Perbedaan praktis utama: di Swift, retain cycle — adalah kebocoran yang terjamin. Di Kotlin, GC dapat memutus siklus jika objek tidak dapat dijangkau dari akar, tetapi masa hidup objek yang bocor tetap tidak dapat diprediksi. Oleh karena itu, di kedua bahasa, strategi terbaik adalah menghindari siklus referensi kuat pada tahap desain.
Untuk Swift, gunakan weak di pola delegasi dan penutupan. Untuk Kotlin — WeakReference atau komponen Lifecycle-aware yang secara otomatis membersihkan referensi saat pemilik dihancurkan. Dalam kedua pendekatan, tujuannya sama — mengecualikan referensi kuat di tempat yang menciptakan rantai retensi yang tidak dapat diputus.
Pertanyaan yang sering diajukan
Strong Reference meningkatkan retain count objek dan mencegah pelepasannya selama referensi ada. Weak Reference tidak mengubah retain count dan secara otomatis menjadi nil ketika objek dihapus dari memori. Referensi kuat digunakan untuk kepemilikan, referensi lemah — untuk hubungan balik dan delegasi.
Retain cycle — saling mengunci di mana dua objek saling menahan dengan referensi kuat. Retain count mereka tidak pernah turun ke nol, memori tidak dibebaskan. Ini menyebabkan kebocoran memori: objek tetap di heap selamanya, aplikasi mengonsumsi semakin banyak sumber daya dan akhirnya crash dengan OutOfMemory.
Gunakan Instruments Leaks dari Xcode — mulai profiling dengan template Leaks, jalankan skenario di aplikasi dan periksa indikator kebocoran. Untuk diagnosis yang akurat, beralihlah ke tab Cycles & Roots — ini akan menunjukkan grafik strong reference timbal balik yang membentuk siklus yang tidak dapat diputus.
Unowned terapkan ketika masa hidup objek anak pasti tidak melebihi masa hidup induk — misalnya, saat mengikat objek ke ruang lingkup yang ditentukan secara ketat. Jika ragu, gunakan Weak, karena merujuk ke unowned yang telah dibebaskan menyebabkan crash aplikasi.
Secara tidak langsung — ya. Setiap retain dan release di ARC adalah operasi atomik dengan overhead. Dengan jumlah objek yang besar dalam siklus, ini dapat mempengaruhi kinerja. Namun masalah utamanya bukanlah kecepatan ARC, melainkan kebocoran memori karena jenis referensi yang salah dipilih.
Kesimpulan
Kami 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