Belajar Swift - Performance & Memory Optimization
Episode 14 of 23

Belajar Swift - Performance & Memory Optimization

Episode ini membahas optimasi performa dan memori di Swift: manajemen memori ARC dengan weak dan unowned references, profiling dengan Instruments untuk memory, allocations, dan Time Profiler, optimasi kode dengan value types dan lazy properties, serta strategi mengurangi startup time dan meningkatkan performa runtime.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Pendahuluan

Aplikasi yang berfungsi belum tentu terasa bagus. Aplikasi yang lambat atau boros memori akan ditinggalkan pengguna, apa pun fiturnya. Episode 14 membahas performance dan memory optimization di Swift: mengelola memori dengan ARC secara benar, memprofil dengan Instruments, dan menulis kode yang efisien tanpa mengorbankan keterbacaan.

Aturan utama episode ini: ukur dulu, optimasi kemudian. Optimasi tanpa data adalah spekulasi. Instruments memberi kalian fakta tentang di mana waktu dan memori benar-benar habis.

ARC dan Referensi Lemah

Retain Cycle dan weak

Episode 2 memperkenalkan ARC. Masalah muncul saat dua objek saling mereferensikan dengan kuat — retain cycle — sehingga keduanya tidak pernah dibebaskan:

Retain cycle dan solusinya
class Pemilik {
    var hewan: Hewan?
}
 
class Hewan {
    weak var pemilik: Pemilik?
}
 
let pemilik = Pemilik()
let kucing = Hewan()
pemilik.hewan = kucing
kucing.pemilik = pemilik

weak var pemilik: Pemilik? memutus salah satu sisi siklus. Referensi weak tidak menambah retain count; saat objek yang ditunjuk dibebaskan, nilai otomatis menjadi nil. Gunakan weak untuk relasi induk-anak yang tidak saling memiliki.

unowned untuk Referensi Non-Optional

Saat kalian yakin referensi selalu hidup selama objek yang memegangnya, gunakan unowned:

unowned reference
class Dokumen {
    unowned let pemilik: Pemilik
    init(pemilik: Pemilik) {
        self.pemilik = pemilik
    }
}

unowned let pemilik: Pemilik tidak menambah retain count dan tidak optional. Bedanya dengan weak: unowned tidak menjadi nil, sehingga mengaksesnya setelah objek dibebaskan adalah crash. Gunakan hanya jika lifecycle benar-benar terjamin.

Closure dan Capture List

Closure yang disimpan dalam properti juga bisa menciptakan retain cycle. Gunakan capture list:

Capture list pada closure
class Pengolah {
    var callback: (() -> Void)?
 
    func siapkan() {
        callback = { [weak self] in
            self?.lakukan()
        }
    }
 
    func lakukan() {
        print("Diproses")
    }
}

[weak self] di dalam capture list mencegah closure menahan self secara kuat. Pola ini wajib untuk closure yang disimpan lama — misalnya handler notification atau callback network.

Profiling dengan Instruments

Membuka Instruments

Instruments adalah alat profiling bawaan Xcode. Template yang paling sering dipakai:

  • Time Profiler: menemukan fungsi yang menghabiskan CPU paling banyak.
  • Allocations: melacak alokasi objek dan mengungkap kebocoran memori.
  • Memory Leaks: mendeteksi objek yang tidak pernah dibebaskan.
  • Network: mengamati request dan latensi jaringan.

Dari terminal, Instruments bisa dijalankan untuk otomasi:

Jalankan profiling dari CLI
xcrun xctrace record --template "Time Profiler" \
  --launch ./Aplikasi --output profil.trace

xcrun xctrace record --template "Time Profiler" merekam profil CPU dari aplikasi dan menyimpannya sebagai trace. Di Xcode, pilih template lalu tekan record — interaksi nyata dengan aplikasi selama perekaman menghasilkan data yang paling realistis.

Membaca Hasil Profiling

Pertanyaan yang kalian jawab dari data: fungsi apa yang paling lama? Berapa banyak objek yang dialokasikan per detik? Adakah objek yang tidak pernah dilepaskan? Fokus pada area yang benar-benar mahal — jangan optimasi kode yang hanya berjalan sekali.

Optimasi Kode Swift

Value Types dan Copy-on-Write

Struktur data yang diubah sesering mungkin sebaiknya memakai value types. Swift menerapkan copy-on-write untuk koleksi: salinan nyata baru terjadi saat nilai diubah:

Mengukur copy-on-write
var daftar1 = [Int](repeating: 1, count: 1_000_000)
var daftar2 = daftar1
daftar2[0] = 99

var daftar2 = daftar1 tidak langsung menyalin satu juta elemen — keduanya berbagi buffer sampai daftar2[0] = 99 memicu penyalinan. Value types juga membuat kode lebih mudah diprediksi untuk concurrency.

Lazy Properties

Properti lazy baru diinisialisasi saat pertama kali diakses, menghemat kerja saat objek dibuat:

Properti lazy
struct Analisa {
    lazy var hasilBerat = {
        (1...100_000).reduce(0, +)
    }()
}

lazy var hasilBerat = { ... }() menunda perhitungan mahal sampai benar-benar dibutuhkan. Cocok untuk nilai yang jarang diakses atau tergantung pada state setelah inisialisasi objek.

Mengurangi Startup dan Meningkatkan Runtime

Startup Time

Waktu startup adalah pengalaman pertama pengguna. Strategi pengurangannya:

  • Kurangi kerja di application(_:didFinishLaunchingWithOptions:) — pindahkan inisialisasi berat ke belakang atau saat idle.
  • Pakai lazy dan dependency injection untuk menunda pembuatan objek.
  • Hindari loading file dan decoding JSON besar di launch.
  • Batasi work di inisialisasi global dan static.

Runtime Performance

Di runtime, perhatikan pola yang umum memperlambat:

  • Pengukuran berulang di dalam loop — hoist perhitungan ke luar.
  • Allocation dalam hot path — gunakan kembali buffer yang ada.
  • String interpolation di loop besar — akumulasi dalam bentuk efisien lalu gabung.
Menghindari alokasi di loop
var teks = [String]()
teks.reserveCapacity(10_000)
for i in 0..<10_000 {
    teks.append("Baris \(i)")
}
let gabungan = teks.joined(separator: "\n")

teks.reserveCapacity(10_000) mengalokasikan buffer sekali untuk mencegah penggandaan berulang saat append. Memahami pola alokasi ini membuat kode kalian jauh lebih hemat tanpa perubahan perilaku.

Info

Ukur dampak setiap optimasi sebelum dan sesudah dengan Instruments. Jika tidak ada perbedaan terukur, optimasi itu kemungkinan menambah kompleksitas tanpa manfaat — batalkan dan fokus ke masalah yang nyata.

Penutup

Inti yang harus dibawa pulang:

  • weak memutus retain cycle; unowned dipakai saat lifecycle dijamin.
  • Capture list [weak self] mencegah closure menahan objek secara kuat.
  • Instruments menyediakan Time Profiler, Allocations, dan Leaks untuk data faktual.
  • Value types dengan copy-on-write membuat kode hemat dan aman untuk concurrency.
  • Properti lazy menunda perhitungan mahal sampai dibutuhkan.
  • Optimasi berdasarkan data, ukur sebelum dan sesudah perubahan.

Di episode 15 selanjutnya kita akan membahas testing dan quality assurance — unit testing dengan XCTest, UI testing dan snapshot testing, mocking dependencies dengan test doubles, serta continuous integration untuk project Swift. Kualitas kalian akan dijamin secara otomatis!

Belajar Swift - Performance & Memory Optimization | Belajar Swift