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.

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.
Episode 2 memperkenalkan ARC. Masalah muncul saat dua objek saling mereferensikan dengan kuat — retain cycle — sehingga keduanya tidak pernah dibebaskan:
class Pemilik {
var hewan: Hewan?
}
class Hewan {
weak var pemilik: Pemilik?
}
let pemilik = Pemilik()
let kucing = Hewan()
pemilik.hewan = kucing
kucing.pemilik = pemilikweak 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.
Saat kalian yakin referensi selalu hidup selama objek yang memegangnya, gunakan unowned:
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 yang disimpan dalam properti juga bisa menciptakan retain cycle. Gunakan capture list:
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.
Instruments adalah alat profiling bawaan Xcode. Template yang paling sering dipakai:
Dari terminal, Instruments bisa dijalankan untuk otomasi:
xcrun xctrace record --template "Time Profiler" \
--launch ./Aplikasi --output profil.tracexcrun 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.
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.
Struktur data yang diubah sesering mungkin sebaiknya memakai value types. Swift menerapkan copy-on-write untuk koleksi: salinan nyata baru terjadi saat nilai diubah:
var daftar1 = [Int](repeating: 1, count: 1_000_000)
var daftar2 = daftar1
daftar2[0] = 99var 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.
Properti lazy baru diinisialisasi saat pertama kali diakses, menghemat kerja saat objek dibuat:
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.
Waktu startup adalah pengalaman pertama pengguna. Strategi pengurangannya:
application(_:didFinishLaunchingWithOptions:) — pindahkan inisialisasi berat ke belakang atau saat idle.lazy dan dependency injection untuk menunda pembuatan objek.Di runtime, perhatikan pola yang umum memperlambat:
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.
Inti yang harus dibawa pulang:
weak memutus retain cycle; unowned dipakai saat lifecycle dijamin.[weak self] mencegah closure menahan objek secara kuat.lazy menunda perhitungan mahal sampai dibutuhkan.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!