November 8, 2026

Instinctivecode

Pengembangan aplikasi iOS & Mac, UX

Debugging Memori Swift: Panduan Melacak Retain Cycle di Aplikasi

Meja kerja pengembang dengan laptop menampilkan grafik memori dan catatan diagram retain cycle
Retain cycle adalah penyebab umum memory leak di aplikasi Swift. Panduan ini membandingkan teknik pelacakan dari Memory Graph hingga Instrument, dengan contoh kode yang bisa langsung diterapkan.

Retain cycle terjadi ketika dua objek saling memegang referensi kuat, sehingga ARC tidak pernah melepaskannya dari memori. Artikel ini membandingkan empat teknik melacak retain cycle di Swift, dari Memory Graph yang cepat sampai Instruments yang lebih rinci.

Apa Itu Retain Cycle dan Mengapa Sulit Ditemukan

Automatic Reference Counting (ARC) menghitung berapa banyak referensi kuat yang menunjuk ke sebuah objek. Ketika hitungannya mencapai nol, objek dibebaskan. Masalah muncul saat dua objek saling menahan: masing-masing menunggu yang lain dilepas lebih dulu. Keduanya tetap hidup meskipun tidak ada lagi bagian aplikasi yang membutuhkannya.

Retain cycle sulit dilacak karena sifatnya yang tidak selalu langsung terlihat. Aplikasi tetap berjalan normal, hanya memori yang perlahan membengkak. Gejalanya baru terasa setelah pengguna berpindah layar berkali-kali atau membuka-tutup modal dalam waktu lama.

Closure dan delegate adalah dua sumber paling umum. Closure menangkap self secara implisit saat mengakses properti atau metode, sementara delegate yang dideklarasikan sebagai referensi kuat menciptakan siklus dua arah dengan objek pemiliknya.

Teknik 1: Memeriksa Deklarasi Properti dan Closure

Sebelum membuka alat bantu, periksa dulu kode yang paling mungkin bermasalah. Cari properti bertipe closure yang ditugaskan ke objek yang sama, lalu periksa apakah closure tersebut memakai self tanpa daftar tangkapan.

Contoh berikut membuat siklus karena onSave menangkap self secara kuat:

final class FormViewController: UIViewController {
    var onSave: (() -> Void)?

    func setupHandler() {
        onSave = {
            self.simpanData()
        }
    }

    func simpanData() { }
}

Perbaikannya bergantung pada kebutuhan. Jika closure hanya dipanggil sekali dan tidak perlu menahan pemiliknya, gunakan [weak self]. Jika closure harus tetap berjalan meskipun pemiliknya sudah dilepas, gunakan [unowned self] dengan catatan pemilik dijamin hidup selama closure dipanggil.

onSave = { [weak self] in
    guard let self else { return }
    self.simpanData()
}

Untuk delegate, deklarasikan protokol dengan AnyObject dan properti delegate sebagai weak. Ini memastikan objek yang menjadi delegate tidak ditahan oleh pengirim pesan.

Teknik 2: Menggunakan Memory Graph Debugger di Xcode

Memory Graph Debugger menampilkan graf objek yang masih hidup saat aplikasi berjalan. Jalankan aplikasi di simulator atau perangkat, lakukan alur yang mencurigakan (misalnya buka dan tutup layar formulir), lalu klik ikon Memory Graph di bilah debug Xcode.

Xcode akan menandai objek yang berpotensi bocor dengan ikon ungu. Klik salah satu objek, lalu periksa daftar referensi yang menunjuk ke sana. Dari situ kamu bisa melihat siapa yang masih memegang objek tersebut dan apakah ada siklus yang jelas.

Teknik ini cepat dan tidak perlu mengubah kode. Kelemahannya, graf bisa sangat besar pada aplikasi dengan banyak objek, sehingga kamu perlu menyaring berdasarkan tipe atau memori yang dipakai. Jika kamu baru pertama kali memakai alat bantu Xcode, bandingkan dulu pendekatan debug di SwiftUI dan UIKit pada panduan alat bantu debug SwiftUI vs UIKit.

Teknik 3: Melacak Alokasi dengan Instruments

Instruments memberikan gambaran historis: berapa banyak instance suatu kelas dibuat dan berapa yang masih hidup setelah alur tertentu selesai. Buka template Allocations, jalankan aplikasi, lalu lakukan navigasi bolak-balik beberapa kali.

Setelah beberapa putaran, periksa jumlah instance kelas yang dicurigai. Jika angkanya terus naik dan tidak turun, kemungkinan besar ada retain cycle. Kamu bisa memakai filter di panel statistics untuk mempersempit ke kelas tertentu.

Bandingkan hasilnya dengan Memory Graph: Instruments menunjukkan pola pertumbuhan, sedangkan Memory Graph menunjukkan siapa yang memegang objek. Keduanya saling melengkapi dan sebaiknya dipakai bergantian.

Teknik 4: Pemeriksaan Otomatis dengan Unit Test

Retain cycle bisa dicegah lewat pengujian. Buat instance view controller, jalankan viewDidLoad dan loadViewIfNeeded, lalu lepaskan referensi dari test dan periksa apakah objek benar-benar terdealokasi.

func testFormViewControllerDeallocates() {
    weak var weakVC: FormViewController?
    autoreleasepool {
        let vc = FormViewController()
        vc.loadViewIfNeeded()
        vc.setupHandler()
        weakVC = vc
    }
    XCTAssertNil(weakVC, "FormViewController tidak dilepas, cek retain cycle")
}

Tambahkan pengujian serupa untuk setiap layar yang memiliki closure atau delegate. Retain cycle baru akan langsung terdeteksi saat pengujian dijalankan, bukan setelah muncul di laporan pengguna.

Jika aplikasimu juga menyimpan data dari formulir atau sinkronisasi, pastikan siklus tidak berasal dari lapisan penyimpanan. Panduan offline mode vs sinkronisasi cloud di aplikasi iOS membahas pola penyimpanan yang bisa memengaruhi masa hidup objek.

Membandingkan Keempat Teknik

Teknik Kelebihan Keterbatasan Cocok untuk
Pemeriksaan kode Cepat, tanpa alat Hanya menemukan pola umum Review awal
Memory Graph Menunjukkan siapa memegang objek Graf bisa besar Investigasi langsung
Instruments Melihat pola pertumbuhan memori Perlu menjalankan ulang alur Analisis jangka panjang
Unit test Otomatis dan berulang Perlu setup awal Pencegahan regresi

Dalam praktik, alur yang efisien adalah memulai dari pemeriksaan kode, memverifikasi dengan Memory Graph, lalu mengunci hasilnya dengan unit test. Instruments dipakai saat masalahnya tidak jelas atau muncul hanya pada alur tertentu.

Untuk aplikasi yang juga menangani koneksi jaringan, retain cycle bisa tersembunyi di dalam objek yang menangani sesi. Memantau koneksi dengan alat seperti yang dijelaskan di panduan lengkap monitoring jaringan dengan ping dan DNS membantu memisahkan masalah memori dari masalah jaringan.

Jika aplikasimu mengirim data lewat jaringan publik, pertimbangkan juga perlindungan koneksi. Perbandingan Mozilla VPN vs Proton VPN membahas pilihan yang bisa dipakai saat menguji aplikasi di jaringan luar.

Ringkasan

Retain cycle di Swift paling cepat dilacak dengan memeriksa closure dan delegate terlebih dahulu, lalu memverifikasi lewat Memory Graph dan Instruments. Setelah ditemukan, kunci perbaikannya dengan unit test agar tidak muncul lagi di kemudian hari.

Pertanyaan yang Sering Diajukan

Apa bedanya weak dan unowned saat memperbaiki retain cycle?

weak membuat referensi menjadi opsional dan otomatis menjadi nil saat objek dilepas. unowned tidak opsional dan mengasumsikan objek selalu hidup selama referensi dipakai; jika objek sudah dilepas, mengaksesnya menyebabkan crash.

Apakah retain cycle selalu terlihat di Memory Graph?

Tidak selalu. Memory Graph menampilkan objek yang masih hidup saat kamu memicunya. Jika siklus baru terbentuk setelah snapshot diambil atau objek sedang tidak aktif, kamu perlu mengulangi pengambilan snapshot pada waktu yang tepat.

Bisakah retain cycle terjadi tanpa closure?

Bisa. Dua objek yang saling menyimpan referensi kuat lewat properti biasa juga membentuk siklus. Selain itu, timer yang dijadwalkan dengan target kuat dan observer notifikasi tanpa pelepasan juga dapat menahan objek lebih lama dari yang seharusnya.