Giriş

Kullanıcılar düşük frame rate’e, yavaş açılış sürelerine ve yüksek bellek kullanımına karşı acımasızdır. Bu rehberde Xcode Instruments ile performans sorunlarını sistematik olarak nasıl bulacağınızı ve düzelteceğinizi göreceğiz.

Instruments’a Giriş

Instruments’ı açmak için: Xcode → Product → Profile (⌘I)

Yaygın kullandığım template’ler:

  • Time Profiler — CPU darboğazları
  • Allocations — Bellek kullanımı ve leak’ler
  • Core Animation — Frame drop’ları
  • Network — Ağ isteklerinin analizi

1. Frame Drop’larını Tespit Etme

Hedef: 60 FPS (16.67ms/frame) veya ProMotion için 120 FPS.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// ❌ Main thread'de ağır iş
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(...)
    let image = UIImage(contentsOfFile: heavyImagePath) // Disk IO — main thread!
    cell.imageView?.image = image
    return cell
}

// ✅ Background thread'de yükle
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(...)
    Task {
        let image = await loadImageAsync(from: heavyImagePath)
        await MainActor.run {
            cell.imageView?.image = image
        }
    }
    return cell
}

2. Bellek Leak’lerini Bulma

En yaygın bellek leak kaynağı: retain cycle.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// ❌ Retain cycle
class ProfileViewController: UIViewController {
    var viewModel: ProfileViewModel!

    override func viewDidLoad() {
        viewModel.onUpdate = {
            self.updateUI() // self'i strong olarak tutar!
        }
    }
}

// ✅ Weak self kullan
override func viewDidLoad() {
    viewModel.onUpdate = { [weak self] in
        self?.updateUI()
    }
}

Combine’da da dikkat:

1
2
3
4
5
6
7
8
9
// ❌ Sink'te strong self
cancellable = publisher.sink { value in
    self.process(value)
}

// ✅
cancellable = publisher.sink { [weak self] value in
    self?.process(value)
}

3. Launch Time Optimizasyonu

App Store verilerine göre, 2 saniyenin üzerindeki launch time kullanıcı kaybına yol açıyor.

Pre-main süresini kısaltmak için:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// ❌ AppDelegate'de senkron yükleme
func application(_ application: UIApplication, didFinishLaunchingWithOptions...) -> Bool {
    Database.shared.setup()     // 300ms
    Analytics.shared.start()   // 200ms
    RemoteConfig.fetch()       // Network!
    return true
}

// ✅ Lazy ve async yükleme
func application(...) -> Bool {
    Task {
        await Analytics.shared.startAsync()
        await RemoteConfig.fetchAsync()
    }
    return true
}

4. SwiftUI Performansı

SwiftUI’da gereksiz re-render’ları önlemek:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// ❌ Tüm parent yeniden render olduğunda child da render olur
struct ParentView: View {
    @StateObject var viewModel = HeavyViewModel()

    var body: some View {
        ChildView(data: viewModel.data)
    }
}

// ✅ Equatable conformance ile optimize et
struct ChildView: View, Equatable {
    let data: DisplayData

    static func == (lhs: ChildView, rhs: ChildView) -> Bool {
        lhs.data == rhs.data
    }

    var body: some View { ... }
}

Core Animation Profiling

CALayer animasyonlarında offscreen rendering büyük sorun:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// ❌ Pahalı: GPU'da offscreen buffer oluşturur
layer.cornerRadius = 12
layer.masksToBounds = true
layer.shadow... // Aynı anda hem cornerRadius hem shadow

// ✅ UIBezierPath ile manual corner
let maskPath = UIBezierPath(roundedRect: bounds, cornerRadius: 12)
let maskLayer = CAShapeLayer()
maskLayer.path = maskPath.cgPath
layer.mask = maskLayer

Sonuç Kontrol Listesi

  • Main thread sadece UI işlemleri için kullanılıyor
  • weak self closure’larda doğru kullanılıyor
  • Görseller uygun boyutta yüklenip cache’leniyor
  • Launch time 2 saniyenin altında
  • Instruments’ta bellek profili sabit seyrediyor (leak yok)
  • 60 FPS scroll performansı sağlanıyor

Performans optimizasyonu bir kere yapılıp bırakılacak bir iş değil, sürekli bir pratik. Instruments’ı her feature development’ında çalıştırın.