---
title: 'Kapanışlarda Yakalama'
source: 'https://academia.sh/tr/kurslar/swift/kapanislarda-yakalama'
course: 'Swift Dili'
language: tr
updated: '2026-10-21T05:03:12+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - "Özellikte saklanan bir kapanışın self'i güçlü yakalayınca tutma döngüsü kurduğunu deinit çıktısından okur"
    - 'Weak self yakalama listesiyle döngüyü kırar ve kapanışın nesne gittikten sonra boş çalıştığını gözler'
    - 'Yakalama listesinin kapanış kurulurken bir kez değerlendirildiğini, listesiz yakalamanın her çağrıda güncel değeri okuduğunu ölçer'
    - 'Saklanmayan bir kapanışın self kullanmasının döngü kurmadığını ayırt eder'
---

# Kapanışlarda Yakalama

Bir nesnenin kendi özelliğinde sakladığı kapanışın self'i yakalayınca deinit'i engellediğini, weak self yakalama listesiyle çözüldüğünü ve yakalama listesinin kapanış kurulurken bir kez değerlendirildiğini ölçer.

Önceki ders tutma döngüsünü iki sınıf özelliği arasında kurdu. Aynı döngü bir sınıfın kendi özelliğinde sakladığı **kapanış** üzerinden de kurulur ve pratikte en sık rastlanan biçim budur. Kapanış, gövdesinde kullandığı dış değerleri kendi içine alır; buna **yakalama** denir. Kapanışın gövdesi `self`'i kullanıyorsa kapanış örneği güçlü referansla yakalar. Örnek de kapanışı bir özelliğinde tutuyorsa iki taraf birbirini tutar.

Bu ders döngüyü bir kez kurar, `[weak self]` **yakalama listesiyle** kırar ve ardından yakalama listesinin başka bir özelliğini ölçer: listenin içindeki ifade kapanışın her çağrısında değil, kapanış kurulurken bir kez değerlendirilir. Sonunda bu sorunun sınırını çizen bir ölçüm gelir: her kapanış döngü kurmaz.

```swift
// Probe.swift — bir ifadenin ne zaman degerlendirildigini kaydeder
final class Probe {
    let label: String
    private(set) var evaluations = 0
    private(set) var uses = 0
    private var evaluatedBeforeUse = false

    init(_ label: String) {
        self.label = label
    }

    func eval<T>(_ value: T) -> T {
        evaluations += 1
        if uses == 0 { evaluatedBeforeUse = true }
        return value
    }

    func use<T>(_ access: () -> T) -> T {
        uses += 1
        return access()
    }

    var moment: String {
        if evaluations == 0 { return "hic" }
        if evaluations > 1 { return "her-kullanimda" }
        return evaluatedBeforeUse ? "hemen" : "ilk-kullanimda"
    }

    func report() {
        print("\(label): \(evaluations) degerlendirme, \(uses) kullanim -> \(moment)")
    }
}

func tally(_ probes: [Probe]) {
    for moment in ["hemen", "ilk-kullanimda", "her-kullanimda", "hic"] {
        let count = probes.filter { $0.moment == moment }.count
        print("\(moment): \(count)")
    }
}
```

## Kapanış Örneği Tutarsa

`Button` sınıfı `onTap` adında isteğe bağlı bir kapanış saklar. `install` yöntemi bu özelliğe, gövdesinde `self.name` okuyan bir kapanış atar. Her turda bir `Button` kurulur, kapanışı yüklenir, bir kez çağrılır ve ad bırakılır.

```swift
// main.swift — kapanis self'i guclu yakalayinca deinit kosmaz
final class Button {
    let name: String
    let owner: Probe
    var onTap: (() -> Void)?

    init(_ name: String, owner: Probe) {
        self.name = name
        self.owner = owner
    }

    func install() {
        onTap = { print("dokundu \(self.name)") }
    }

    deinit {
        print("deinit dugme \(name)")
        _ = owner.eval(name)
    }
}

let trapped = Probe("kapanis-dongusu")

for round in 1...2 {
    var button: Button? = Button("dugme-\(round)", owner: trapped)
    button?.install()
    button?.onTap?()
    _ = trapped.use { button = nil }
    print("birakildi \(round): \(trapped.evaluations) deinit")
}

trapped.report()
```

```
dokundu dugme-1
birakildi 1: 0 deinit
dokundu dugme-2
birakildi 2: 0 deinit
kapanis-dongusu: 0 degerlendirme, 2 kullanim -> hic
```

Kapanış çağrıldı ve ad yazıldı, ama bırakmadan sonra `deinit` satırı hiç görünmez. `Button` kapanışı `onTap` ile tutuyor, kapanış da `self` üzerinden `Button`'ı tutuyor. Dışarıdaki ad gittiğinde sayaç bire düşer, sıfıra inmez. Önceki dersin kuralı burada da geçerli: `kapanis-dongusu` iki kullanım ve sıfır değerlendirme kaydeder, anı `hic` olur.

## Weak Self ile Kırmak

Çözüm kapanışın başına bir yakalama listesi koymaktır. `[weak self]` listesi kapanışa `self`'in zayıf bir kopyasını verir; kapanış artık örneği tutmaz ama örnek gidince `self` isteğe bağlı olur ve `nil` olabilir. Gövde bu yüzden `guard let self` ile önce örneğin yaşayıp yaşamadığına bakar. Ölçüm kapanışı iki kez çağırır: ad bırakılmadan önce ve bırakıldıktan sonra. Bırakılma sonrası çağrı için kapanış ayrı bir değişkende saklanır.

```swift
// main.swift — [weak self] dongunun onune gecer
final class Button {
    let name: String
    let owner: Probe
    var onTap: (() -> Void)?

    init(_ name: String, owner: Probe) {
        self.name = name
        self.owner = owner
    }

    func install() {
        onTap = { [weak self] in
            guard let self else {
                print("dugme yok")
                return
            }
            print("dokundu \(self.name)")
        }
    }

    deinit {
        print("deinit dugme \(name)")
        _ = owner.eval(name)
    }
}

let broken = Probe("zayif-yakalama")

for round in 1...2 {
    var button: Button? = Button("dugme-\(round)", owner: broken)
    button?.install()
    let saved = button?.onTap
    saved?()
    _ = broken.use { button = nil }
    saved?()
    print("birakildi \(round): \(broken.evaluations) deinit")
}

broken.report()
```

```
dokundu dugme-1
deinit dugme dugme-1
dugme yok
birakildi 1: 1 deinit
dokundu dugme-2
deinit dugme dugme-2
dugme yok
birakildi 2: 2 deinit
zayif-yakalama: 2 degerlendirme, 2 kullanim -> her-kullanimda
```

Bu kez her turda `deinit` satırı bırakmanın hemen ardından çıkar. Kapanış `Button`'ı tutmuyordu, bu yüzden dışarıdaki ad gidince sayaç sıfıra indi. Bırakmadan sonra aynı kapanış hâlâ çağrılabilir ama `guard let self` başarısız olur ve `dugme yok` yazılır. Kapanış artık boş çalışıyor: örnek gitti, kapanış kaldı. `zayif-yakalama` iki kullanım ve iki değerlendirme kaydeder; bu iki tur boyunca her kullanımda bir `deinit` koşar ve anı `her-kullanimda` olur.

## Yakalama Listesi Ne Zaman Değerlendirilir

`[weak self]` yakalama listesinin yalnız bir kullanımıdır. Liste aslında kapanışa bir değer de verebilir: `[snapshot = ifade]` biçiminde yazılan her giriş, kapanış **kurulurken** bir kez değerlendirilir ve kapanışın içinde sabit bir kopya olur. Listesiz yakalanan bir değişken ise kapanışın her çağrısında güncel değeriyle okunur. Aşağıdaki ölçüm bu farkı iki `Probe` ile gösterir. Önce ikisi de `level` değerini 1 iken kurulur, sonra `level` 5 olur, sonra her kapanış iki kez çağrılır.

```swift
// main.swift — yakalama listesi kurulurken, listesiz okuma her cagrida
let listed = Probe("yakalama-listesi")
let live = Probe("listesiz-yakalama")

do {
    var level = 1
    let withList = { [snapshot = listed.eval(level)] in snapshot }
    let withoutList = { live.eval(level) }
    level = 5
    print("listeli: \(listed.use { withList() }) \(listed.use { withList() })")
    print("listesiz: \(live.use { withoutList() }) \(live.use { withoutList() })")
}

listed.report()
live.report()
```

```
listeli: 1 1
listesiz: 5 5
yakalama-listesi: 1 degerlendirme, 2 kullanim -> hemen
listesiz-yakalama: 2 degerlendirme, 2 kullanim -> her-kullanimda
```

Listeli kapanış iki çağrıda da 1 döndürür: kopya `level` hâlâ 1 iken alınmıştı ve sonraki atama onu değiştirmedi. Listesiz kapanış iki çağrıda da 5 döndürür, çünkü değişkeni her çağrıda yeniden okur. Probe çıktısı bunu iki farklı an olarak kaydeder. `yakalama-listesi` tek değerlendirme kaydeder ve bu değerlendirme ilk kullanımdan önce olduğu için anı **hemen**dir. `listesiz-yakalama` iki değerlendirme kaydeder, bu yüzden `her-kullanimda` olur. Bu dersin anı **hemen**dir: yakalama listesindeki ifade kapanış kurulduğu noktada bir kez değerlendirilir.

## İki Kapanış Aynı Değişkeni Paylaşır

Yakalama bir değerin kopyalanması değil, çoğu zaman bir değişkenin paylaşılmasıdır. Aynı yerel değişkeni yakalayan iki kapanış, o değişkenin aynı kutusuna bakar. Bu ölçüm bir `total` değişkenini yakalayan bir artırıcı ile bir okuyucu kapanış kurar. Artırıcı her çağrıda `Probe` üzerinden bir değerlendirme yapar ve okuyucu, artırıcının yaptığı değişikliği görür mü, buna bakar.

```swift
// main.swift — iki kapanis ayni degiskeni yakalar
let shared = Probe("ortak-degisken")
do {
    var total = 0
    let add = { total += shared.eval(10) }
    let read = { total }
    print("baslangic \(read())")
    shared.use { add() }
    print("birinci artis \(read())")
    shared.use { add() }
    print("ikinci artis \(read())")
}
shared.report()
```

```
baslangic 0
birinci artis 10
ikinci artis 20
ortak-degisken: 2 degerlendirme, 2 kullanim -> her-kullanimda
```

`read` kapanışı artırıcının yaptığı değişikliği görür: `baslangic 0`, ardından `birinci artis 10` ve `ikinci artis 20`. İki kapanış `total` değerinin ayrı kopyalarını tutsaydı okuyucu hep 0 verirdi. Kapanışlar aynı kutuya bakar ve kutuyu değiştiren her çağrı öbürüne görünür. `ortak-degisken` iki kullanımda iki değerlendirme sayar ve `her-kullanimda` der. Önceki bölümdeki yakalama listesi bu paylaşımı keser: `[total]` yazılan bir kapanış, değişkeni değil o andaki değeri alır. Paylaşım varsayılandır; kopya ise açıkça istenen bir davranıştır.

## Kaçan ve Kaçmayan Kapanış

Bir kapanış, kendisini alan işlevden sonra da yaşayabilir. Yaşayacaksa parametresi `@escaping` olarak işaretlenir; işaretlenmemiş kapanış işlev dönmeden bitmek zorundadır. Bu iki biçim `Probe` ile ölçülür. Kaçmayan kapanış `run` işlevinde hemen çağrılır. Kaçan kapanış `store` işlevinde bir diziye eklenir ve ancak sonra çağrılır. İki `store` çağrısından sonra, kapanışlar çağrılmadan önce `later.evaluations` okunur.

```swift
// main.swift — kacmayan hemen, kacan sonra degerlendirilir
let now = Probe("kacmayan")
let later = Probe("kacan")
do {
    var pending: [() -> Int] = []

    func run(_ work: () -> Int) -> Int {
        work()
    }

    func store(_ work: @escaping () -> Int) {
        pending.append(work)
    }

    for round in 1...2 {
        _ = now.use { run { now.eval(round) } }
        store { later.eval(round) }
        print("tur \(round): kacmayan \(now.evaluations), kacan \(later.evaluations)")
    }
    for work in pending {
        _ = later.use { work() }
    }
    print("cagrildiktan sonra kacan \(later.evaluations)")
}
now.report()
later.report()
```

```
tur 1: kacmayan 1, kacan 0
tur 2: kacmayan 2, kacan 0
cagrildiktan sonra kacan 2
kacmayan: 2 degerlendirme, 2 kullanim -> her-kullanimda
kacan: 2 degerlendirme, 2 kullanim -> her-kullanimda
```

Her turda `kacmayan` sayacı `run` dönmeden önce artar: ilk turda 1, ikinci turda 2. `kacan` sayacı ise iki `store` çağrısından sonra da 0'dır, çünkü `store` kapanışı yalnız diziye ekler ve çağırmaz. Kapanışlar ancak sonraki döngüde çağrılır ve sayaç `cagrildiktan sonra kacan 2` olur. İki `Probe` de `her-kullanimda` der, ama değerlendirmenin yeri farklıdır: kaçmayan kapanışın değerlendirmesi, onu alan işlevin çağrısı içindedir. Kaçan kapanışın değerlendirmesi, kapanışın kurulduğu satırdan çok sonra gelebilir. Bu fark bir kapanışın neden `self`'i açıkça yazmaya zorladığını da açıklar: kaçan kapanış, yakaladığı örnekten uzun yaşayabilir. Kaçmayan kapanış ise örneği hiçbir zaman tutamaz, çünkü işlev dönünce biter.

## Güçlü Geçici Kopya

`guard let self` satırı yalnız `self`'in `nil` olup olmadığına bakmaz; gövde boyunca yaşayan güçlü bir geçici referans da kurar. Bu referans, kapanış bitene kadar örneği tutar. Ölçüm bunu şöyle sınar: `Job` örneğinin kapanışı, gövdenin ortasında dışarıdaki adı bırakan bir işlevi çağırır. `deinit` bırakılma satırında mı, yoksa gövde bittikten sonra mı koşar?

```swift
// main.swift — guard let self govde boyunca ornegi tutar
final class Job {
    let name: String
    let owner: Probe
    var run: (() -> Void)?

    init(_ name: String, owner: Probe) {
        self.name = name
        self.owner = owner
    }

    func install(release: @escaping () -> Void) {
        run = { [weak self] in
            guard let self else {
                print("is yok")
                return
            }
            print("govde basladi \(self.name)")
            release()
            print("govde bitiyor \(self.name)")
        }
    }

    deinit {
        print("deinit is \(name)")
        _ = owner.eval(name)
    }
}

let held = Probe("gecici-guclu")
for round in 1...2 {
    var job: Job? = Job("is-\(round)", owner: held)
    job?.install { job = nil }
    let saved = job?.run
    _ = held.use { saved?() }
    print("kapanis dondu: \(held.evaluations) deinit")
}
held.report()
```

```
govde basladi is-1
govde bitiyor is-1
deinit is is-1
kapanis dondu: 1 deinit
govde basladi is-2
govde bitiyor is-2
deinit is is-2
kapanis dondu: 2 deinit
gecici-guclu: 2 degerlendirme, 2 kullanim -> her-kullanimda
```

Her turda `govde basladi` ve `govde bitiyor` satırları arasında `release()` çağrılır ve dışarıdaki `job` adı `nil` olur. Yine de `deinit is is-1` satırı `govde bitiyor is-1` satırından sonra gelir. `guard let self` ile kurulan geçici güçlü referans, gövde bitene kadar örneği ayakta tutar; son referans ancak kapanış dönerken bırakılır. `kapanis dondu: 1 deinit` satırı bunu doğrular: kapanış döndüğü anda sayaç 1'dir. `gecici-guclu` iki turda iki değerlendirme sayar ve `her-kullanimda` der. Bunun pratik sonucu iki yönlüdür. `[weak self]` kapanışın örneği kalıcı olarak tutmasını önler, ama gövde çalışırken örnek ortadan kalkmaz. Gövdede yapılan işlem örneğin kaybolacağı korkusuyla bölünmek zorunda değildir.

## Sınırlayıcı Ölçüm

Her kapanış döngü kurmaz. Döngü için kapanışın örnekte **saklanması** gerekir. Bir yöntemin içinde kurulup yöntem dönmeden biten bir kapanış, örneğin bir dizi dönüştürmesine verilen kapanış, hiçbir yerde saklanmaz ve `self`'i yakalaması örneği tutmaz. Ölçüm bir `Report` örneğinde bunu gösterir: `joined` yöntemi `parts.map { ... }` içinde `name` özelliğini okur, sonuç döner ve örnek bırakılır.

```swift
// main.swift — saklanmayan kapanis dongu kurmaz
final class Report {
    let name: String
    let owner: Probe
    let parts = ["a", "b", "c"]

    init(_ name: String, owner: Probe) {
        self.name = name
        self.owner = owner
    }

    func joined() -> String {
        parts.map { "\($0)-\(name)" }.joined(separator: ",")
    }

    deinit {
        print("deinit rapor \(name)")
        _ = owner.eval(name)
    }
}

let passing = Probe("gecici-kapanis")

for round in 1...2 {
    var report: Report? = Report("rapor-\(round)", owner: passing)
    print(report?.joined() ?? "yok")
    _ = passing.use { report = nil }
    print("birakildi \(round): \(passing.evaluations) deinit")
}

passing.report()
```

```
a-rapor-1,b-rapor-1,c-rapor-1
deinit rapor rapor-1
birakildi 1: 1 deinit
a-rapor-2,b-rapor-2,c-rapor-2
deinit rapor rapor-2
birakildi 2: 2 deinit
gecici-kapanis: 2 degerlendirme, 2 kullanim -> her-kullanimda
```

Her turda `deinit` bırakma anında koşar. `map` kapanışı `self`'i okudu ama yöntem döndüğü anda kapanış da bitti; ona bir referans kalmadı. Sızıntı yakalamanın kendisinden değil, yakalayan kapanışın örnekte yaşamaya devam etmesinden doğar. Yakalama listesi gerektiren yer yalnızca saklanan kapanıştır.

**BL3.** Ölçülen her sınıf kapanışı tek bir özellikte saklıyor. Birden çok saklanan kapanışın ya da kapanışı dışarıdaki bir listede tutan nesnenin aynı kuralları izlediği bu derste ölçülmedi. Kapanış sayısının bir olması tasarım tercihidir.

## Özet

- Özellikte saklanan bir kapanış `self`'i güçlü yakalarsa örnek ile kapanış birbirini tutar ve `deinit` hiç koşmaz.
- `[weak self]` yakalama listesi örneği tutmaz; örnek gidince `self` `nil` olur ve `guard let self` kapanışı boş çalıştırır.
- Yakalama listesindeki `[snapshot = ifade]` kapanış kurulurken bir kez değerlendirilir; listesiz yakalanan değişken her çağrıda güncel değeriyle okunur.
- Saklanmayan bir kapanış, örneğin `map` içindekiler, `self`'i kullansa da döngü kurmaz.
- Bu dersin anı: **hemen**. Yakalama listesindeki ifade kapanışın kurulduğu noktada bir kez değerlendirilir.

## Sonraki Adım

Bu derste kapanış, bir değeri ya da örneği kendi içine alıp saklayan bir kutuydu. Özellik sarmalayıcıları da benzer bir şeyi bir özelliğin kendisi için yapar: değerin nasıl saklanacağını ve okunacağını ayrı bir tipe taşır. Sonraki ders `wrappedValue` okuyucusunun ne zaman çalıştığını ve sarmalayıcının başlangıç değerinin ne zaman kurulduğunu ölçer.
