---
title: 'Swift Dili'
source: 'https://academia.sh/tr/kurslar/swift'
language: tr
updated: '2026-10-21T05:03:11+00:00'
license: 'CC BY-SA 4.0'
lessons: 21
outcomes:
    - "Swift'in tip sistemini ve isteğe bağlı değer modelini doğru kullanır"
    - 'Değer ve referans semantiği farkını açıklar'
    - 'Protokol ve uzantılarla genişletilebilir tasarım kurar'
    - 'Bir ifadenin hemen mi, ilk kullanımda mı, her kullanımda mı değerlendirildiğini ya da hiç değerlendirilmediğini ayırt eder'
    - 'Otomatik referans sayımının ve katı eşzamanlılık denetiminin koda koyduğu sınırları tanır'
---

# Swift Dili

Swift'te bir ifadenin yazıldığı satır ile değerlendirildiği an her zaman aynı değildir: let ile kurulan sabit satır geçildiğinde hazırdır, lazy ile yazılan özellik ilk okunana kadar bekler, hesaplanan özellik her okunuşta yeniden koşar, && işlecinin sağındaki ifade hiç koşmayabilir. Kurs her düzeneği tek bir soruyla ölçer — bu ifade ne zaman değerlendiriliyor? Sabitler, temel tipler, işleçler, fonksiyonlar ve kapanışlar, isteğe bağlı değerler, yapılar ve sınıflar, protokoller, genellikler, hata yönetimi, otomatik referans sayımı, özellik sarmalayıcıları, sonuç kurucular ve eşzamanlılık güvenliği aynı çerçeveden geçirilir.

## Dil Temelleri

Swift'in tasarım hedefleri, sabitler ve değişkenler, tip çıkarımı, temel tipler, dizgi ve karakter modeli, işleçler, desen eşlemeli switch ile denetim akışı, argüman etiketli fonksiyonlar ve yakalama listeli kapanışlar.

### Swift'in Tasarım Hedefleri

Source: https://academia.sh/tr/kurslar/swift/swiftin-tasarim-hedefleri

Swift'in üç hedefini — güvenlik, ifade gücü, başarım — bir ölçü eksenine bağlar: bu ifade ne zaman değerlendiriliyor? Sekiz ifadeyi dört ana ayırır.

Önceki kurslarda bir programın ne yaptığını sordun. Bu kurs başka bir şey soruyor: bir ifade yazdığında, o ifade programın hangi anında çalışıyor? Derleme ile koşum arasındaki ayrımı önceki bir kursta (M11/K01) gördün; burada koşum içindeki sırayı ölçeceğiz. Swift iOS müfredatının dilidir, ve onu üç hedefle tanıtmak gelenektir.

#### Üç Hedef

Güvenlik: dil, hatayı koşumdan önce yakalamaya çalışır. Bir sabit atanmadan okunamaz; bir değer, tipi isteğe bağlı değer (optional) olmadıkça `nil` olamaz. İki durumda da derleyici programı reddeder. Bu parçalar Swift 6.4'te derlenmez; derleyici ikisini de reddeder, bu yüzden çıktısız kalırlar.

```swift
let total: Int
print(total)
```

```swift
var name: String = nil
```

İfade gücü: tipi yazmak zorunda kalmazsın, derleyici sağ taraftan çıkarır (tip çıkarımı, type inference). Başarım hedefi ise bu derste ölçülmez; süre ölçmüyoruz, yalnız sıra ölçüyoruz.

#### Ölçü Ekseni: Dört An

Kurs boyunca tek bir soru soracağız: bu ifade ne zaman değerlendiriliyor? Dört cevap var: **hemen**, **ilk kullanımda**, **her kullanımda** ve **hiç**. Ölçüm düzeneği iki parçadan oluşur. Önce yardımcı dosya; ders boyunca aynı kalır ve ilk Swift bloğu olarak hep burada durur.

```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)")
    }
}
```

`eval` ifadeyi sarar ve değerlendirildiğini sayar; `use` ifadeye erişimi sarar. Kural basit: hiç değerlendirilmediyse `hic`; birden çok değerlendirildiyse `her-kullanimda`; bir kez değerlendirildi ve bu ilk kullanımdan önceyse `hemen`; değilse `ilk-kullanimda`.

**DT1.** Her gözlem, `hic` dışında, en az iki kullanım taşır; tek kullanım, tek değerlendirmeyi `hemen` ile `ilk-kullanimda` arasında ayırt edemez.

Sekiz ifade, sekiz `Probe`. Sabit, tembel özellik, hesaplanan özellik, fonksiyon, kısa devre ve kapanış aynı dosyada yan yana durur; kurs bunları sırayla ayrı derslere bölecek.

```swift
// main.swift — sekiz ifade, dort an
final class Basket {
    let lazyProbe: Probe
    let computedProbe: Probe
    var items = [3, 4, 5]

    init(lazyProbe: Probe, computedProbe: Probe) {
        self.lazyProbe = lazyProbe
        self.computedProbe = computedProbe
    }

    lazy var total: Int = lazyProbe.eval(items.reduce(0, +))
    var count: Int { computedProbe.eval(items.count) }
}

func square(_ x: Int, probe: Probe) -> Int { probe.eval(x * x) }

let constant = Probe("let-sabiti")
let lazyTotal = Probe("lazy-ozellik")
let computed = Probe("hesaplanan-ozellik")
let fallback = Probe("bos-birlestirme-sagi")
let call = Probe("fonksiyon-govdesi")
let shortCircuit = Probe("ve-isleci-sagi")
let captured = Probe("yakalama-listesi")
let body = Probe("kapanis-govdesi")

let limit = constant.eval(10)
_ = constant.use { limit }
_ = constant.use { limit }

let basket = Basket(lazyProbe: lazyTotal, computedProbe: computed)
_ = lazyTotal.use { basket.total }
_ = lazyTotal.use { basket.total }
_ = computed.use { basket.count }
_ = computed.use { basket.count }

let stored: Int? = 7
_ = fallback.use { stored ?? fallback.eval(0) }
_ = fallback.use { stored ?? fallback.eval(0) }

_ = call.use { square(3, probe: call) }
_ = call.use { square(4, probe: call) }

let ready = false
_ = shortCircuit.use { ready && shortCircuit.eval(true) }
_ = shortCircuit.use { ready && shortCircuit.eval(true) }

let step = 2
let addStep = { [step = captured.eval(step)] (x: Int) -> Int in x + step }
_ = captured.use { addStep(1) }
_ = captured.use { addStep(2) }

let double = { (x: Int) -> Int in body.eval(x * 2) }
_ = body.use { double(3) }
_ = body.use { double(4) }

let probes = [constant, lazyTotal, computed, fallback, call, shortCircuit, captured, body]
for probe in probes { probe.report() }
tally(probes)
```

```
let-sabiti: 1 degerlendirme, 2 kullanim -> hemen
lazy-ozellik: 1 degerlendirme, 2 kullanim -> ilk-kullanimda
hesaplanan-ozellik: 2 degerlendirme, 2 kullanim -> her-kullanimda
bos-birlestirme-sagi: 0 degerlendirme, 2 kullanim -> hic
fonksiyon-govdesi: 2 degerlendirme, 2 kullanim -> her-kullanimda
ve-isleci-sagi: 0 degerlendirme, 2 kullanim -> hic
yakalama-listesi: 1 degerlendirme, 2 kullanim -> hemen
kapanis-govdesi: 2 degerlendirme, 2 kullanim -> her-kullanimda
hemen: 2
ilk-kullanimda: 1
her-kullanimda: 3
hic: 2
```

Sekiz ifade dört ana bölündü: iki `hemen`, bir `ilk-kullanimda`, üç `her-kullanimda`, iki `hic`. Bu sayı kursun bağlayıcı dökümüdür; sonraki dersler her ifadeyi kendi dersinde açar. `let` sabiti tanımlandığı satırda değerlendirilir. `lazy` özellik ilk okunduğunda kurulur ve saklanır; hesaplanan özellik ise her okumada yeniden hesaplanır. Fonksiyon gövdesi her çağrıda koşar; `??` işlecinin sağı sol dolu olduğu için, `&&` işlecinin sağı sol yanlış olduğu için hiç çalışmaz. Yakalama listesi kapanış oluşturulurken bir kez değerlendirilir; kapanışın gövdesi ise her çağrıda.

**DT2.** Sabit ve yakalama listesi aynı ana düşer, ama yollar farklıdır; bu derste yalnız anı ölçüyoruz, mekanizmayı değil.

#### Değer Kopyası Yeniden Hesaplamaz

Swift'in güvenlik hedefinin bir ayağı değer semantiğidir (value semantics): dizi, dizgi ve yapı gibi değer tipleri atamada kopyalanır ve kopya bağımsız bir değerdir. Bir değişkeni diğerine atadığında, ikisinden birini değiştirmek öbürünü etkilemez. Bu özellik tasarımın "yanlışlıkla paylaşılan durum olmasın" kaygısından gelir. Ölçüm sorusu başka: kopya, kaynağı üreten ifadeyi yeniden değerlendirir mi, yoksa yalnız var olan değeri mi çoğaltır?

Aşağıdaki blok bir diziyi bir `Probe` ile üretir, `duplicate` adlı ikinci bir değişkene kopyalar ve iki diziyi birbirinden bağımsız biçimde değiştirir. Ardından her iki değişkeni okur; okumalar iki kullanım sayılır.

```swift
// Copy.swift — kopya ifadeyi tekrar calistirmaz
let sourceProbe = Probe("kaynak-dizi")
var original = sourceProbe.eval([1, 2, 3])
var duplicate = original
duplicate.append(4)
original.append(9)
_ = sourceProbe.use { original.count }
_ = sourceProbe.use { duplicate.count }
print(original, duplicate)
sourceProbe.report()
```

```
[1, 2, 3, 9] [1, 2, 3, 4]
kaynak-dizi: 1 degerlendirme, 2 kullanim -> hemen
```

`original` dizisine eklenen 9, `duplicate` dizisinde görünmedi; `duplicate` dizisine eklenen 4 de `original` dizisinde yok. İki değişken atama anında ayrıldı ve her biri kendi yoluna gitti. Ölçüm satırı ise ifadenin yalnız bir kez değerlendirildiğini söylüyor: atama bir değeri çoğaltır, onu üreten ifadeyi yeniden çalıştırmaz. Değerlendirme `var` tanımında, iki okumadan önce olduğu için an `hemen` çıkar.

Bu ayrım güvenlik hedefini somutlaştırır. Eğer atama kaynağı yeniden hesaplasaydı, her kopya sonucunu başka bir anda üretebilirdi; ölçülen davranışta kopya, atama anındaki değerin bir görüntüsüdür. Sonradan yapılan değişiklikler iki tarafa ayrı ayrı uygulanır.

**DT3.** Kopyanın bağımsızlığı bir `Int` dizisiyle ölçüldü; içinde sınıf örneği taşıyan bir dizinin kopyasında elemanların paylaşılması bu dersin konusu değildir.

#### Sabit Bağlama, Nesneyi Dondurmaz

Güvenlik hedefinin bir başka ucu `let` ile ilgilidir ve sık yanlış okunur. Bir sınıf örneğini `let` ile bağladığında, bağlanan şey örneğe giden referanstır. Değişken yerine sabit yazmak, `holder` adının başka bir nesneyi göstermesini engeller, ama nesnenin kendi `var` özelliklerini kilitlemez. Aynı kuralı değer tipleri için ters yönde okuyabilirsin: bir yapıyı `let` ile bağlarsan özelliklerini de değiştiremezsin; sınıfta ise bu yasak yoktur.

Aşağıdaki blok küçük bir sınıf tanımlar, onu `let` ile bağlar ve yine de içindeki sayacı iki kez atayarak değiştirir. Sayaç değeri bir `Probe` üzerinden gelir; iki ayrı atama iki ayrı değerlendirme getirir.

```swift
// Holder.swift — let nesneyi dondurmaz
final class Holder {
    var count = 0
}
let holderProbe = Probe("sinif-sayaci")
let anchor = Holder()
_ = holderProbe.use { anchor.count = holderProbe.eval(5) }
_ = holderProbe.use { anchor.count = holderProbe.eval(8) }
print(anchor.count)
holderProbe.report()
```

```
8
sinif-sayaci: 2 degerlendirme, 2 kullanim -> her-kullanimda
```

`anchor` bir `let` sabiti olduğu halde `anchor.count` iki kez atandı ve son değer olan 8 okundu. Sabit olan, `anchor` adının bağlandığı referanstır; nesnenin içi değişkendir. Ölçüm satırı iki değerlendirme gösteriyor, çünkü iki atama iki ayrı `eval` çağrısı yaptı: `let` kuralı burada atamanın sayısını kısıtlamıyor, çünkü atanan şey `anchor` değil, `anchor.count` alanıdır. Bu yüzden an `her-kullanimda` olur.

Tasarım açısından çıkarım şudur: `let`, bir bağlamanın yeniden yapılamayacağı güvencesidir; nesnenin değişmezliği güvencesi değildir. Değişmezlik istiyorsan alanları da `let` yap ya da değer tipi kullan.

**DT4.** Sınıf örneği tek bir alanla ölçüldü; `let` ile bağlı değer tipinde aynı atamanın derlenmediği iddiası bu dersin dışında, KAYNAK düzeyinde bir bilgidir.

#### Tip Çıkarımı Anı Değiştirmez

İfade gücü hedefinin en görünür parçası tip çıkarımıdır: `let size = 12` yazarsan derleyici `Int` tipini kendisi bulur, sen yazmak zorunda kalmazsın. İstersen tipi açıkça da yazarsın: `let size: Int = 12`. Burada akla gelen soru, çıkarımın bir çalışma zamanı maliyeti ya da zamanlaması olup olmadığıdır. Tip çıkarımı derleme sırasında biter; çalışan program tipin nasıl bulunduğunu bilmez.

Aşağıdaki blok aynı değeri iki yolla tanımlar: biri çıkarımla, biri açık tip bildirimiyle. Her biri kendi `Probe`'una bağlıdır ve iki kez okunur; `type(of:)` ile iki değişkenin tipi de yazdırılır.

```swift
// Inference.swift — cikarim ve acik tip ayni ani verir
let inferredProbe = Probe("cikarilan-tip")
let declaredProbe = Probe("acik-tip")
let inferred = inferredProbe.eval(42)
let declared: Int = declaredProbe.eval(42)
_ = inferredProbe.use { inferred + 1 }
_ = inferredProbe.use { inferred + 2 }
_ = declaredProbe.use { declared + 1 }
_ = declaredProbe.use { declared + 2 }
print(type(of: inferred), type(of: declared))
inferredProbe.report()
declaredProbe.report()
```

```
Int Int
cikarilan-tip: 1 degerlendirme, 2 kullanim -> hemen
acik-tip: 1 degerlendirme, 2 kullanim -> hemen
```

İki değişkenin tipi aynı çıktı, ve iki `Probe` aynı anı verdi: ikisi de tek değerlendirme, iki kullanım, `hemen`. Açık tip bildirimi ifadeyi başka bir zamana taşımadı; yalnız derleyiciye ne bulması gerektiğini söyledi. Bu yüzden kursun ölçü ekseni tip çıkarımına duyarsızdır: ifadenin ne zaman değerlendirildiğini belirleyen şey tipin yazılı olup olmaması değil, ifadenin hangi bağlamda durduğudur.

**DT5.** Tip çıkarımı bu derste yalnız `Int` literaliyle ölçüldü; çıkarımın başka literal ve ifade çeşitlerinde zamanlamaya dokunmadığı iddiası bu dersin dışındadır.

#### Tembel Dönüşüm Çağrılana Kadar Bekler

Tasarımın ifade gücü hedefi, kısa yazılan şeyin ne zaman çalıştığı sorusunu da doğurur. Bir dizi üzerinde `map` çağırdığında dönüşüm hemen ve her eleman için yapılır. Aynı dizinin `lazy` görünümü üzerinde `map` çağırırsan, dönüşüm tanımlandığı anda hiçbir eleman için çalışmaz; yalnız bir eleman istendiğinde, o eleman için çalışır. Bu, daha önce gördüğümüz `lazy var` özelliğinin dizi karşılığı değildir ama aynı sezgiyi taşır: iş, sonucu isteyene kadar ertelenebilir.

Aşağıdaki blok iki `Probe` kurar. İstekli dönüşümün gövdesi `eagerProbe`'a, tembel dönüşümün gövdesi `lazyProbe`'a değerlendirme yazar. Dönüşümlerin hemen ardından ve tembel görünümden iki eleman çekildikten sonra sayaçlar yazdırılır. `evaluations` özelliği `Probe` içinde okunabilir bir sayaçtır.

```swift
// LazyMap.swift — istekli ve tembel donusum
let eagerProbe = Probe("istekli-donusum")
let lazyMapProbe = Probe("tembel-donusum")
let values = [1, 2, 3]
let eagerDoubled = values.map { eagerProbe.eval($0 * 2) }
let lazyDoubled = values.lazy.map { lazyMapProbe.eval($0 * 2) }
print(eagerProbe.evaluations, lazyMapProbe.evaluations)
_ = lazyMapProbe.use { Array(lazyDoubled.prefix(2)) }
print(eagerProbe.evaluations, lazyMapProbe.evaluations)
_ = lazyMapProbe.use { Array(lazyDoubled.prefix(2)) }
print(eagerDoubled, Array(lazyDoubled.prefix(1)))
lazyMapProbe.report()
```

```
3 0
3 2
[2, 4, 6] [2]
tembel-donusum: 5 degerlendirme, 2 kullanim -> her-kullanimda
```

İlk satır `3 0` çıktı: istekli `map` üç elemanın üçünü hemen dönüştürdü, tembel `map` ise henüz hiçbirine dokunmadı. İkinci satırda tembel sayaç 2 oldu, çünkü `prefix(2)` yalnız iki elemanın dönüşümünü istedi; üçüncü eleman hiç hesaplanmadı. Aynı `prefix(2)` ikinci kez çağrılınca dönüşüm yeniden çalıştı, çünkü tembel görünüm sonuçları saklamaz; son `report` satırındaki beş değerlendirme bunu gösteriyor: iki, iki ve son `prefix(1)` çağrısından bir.

İfade gücü hedefi bu iki davranışı birbirinden ayırt etmeyi sana bırakır. İstekli `map` sonucu bir kez üretir ve saklar. Tembel görünüm yazmayı kısa tutar, ama her çekişte işi yeniden yapabilir. Hangisinin doğru olduğu, sonucun kaç kez okunacağına ve gövdenin yan etkisi olup olmadığına bağlıdır.

**DT6.** Tembel dönüşüm yalnız `prefix` ile çekilen elemanlar için ölçüldü; `lazy` görünümün `filter` ya da zincirlenmiş dönüşümlerde sayıyı nasıl değiştirdiği bu dersin dışındadır.

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

Kural sayar, niyeti okumaz. Aynı `let` ifadesini iki ayrı sabit olarak, iki ayrı `Probe` ile yazarsan ikisi de `hemen` çıkar. Aynı ifadeyi tek `Probe`'a bağlarsan, ikinci `eval` sayıyı ikiye çıkarır ve kural `her-kullanimda` der; kodun niyeti değişmemiştir, ölçüm düzeneği ise değişmiştir.

```swift
// Twin.swift — ayni ifade, iki probe ve tek probe
let first = Probe("birinci-sabit")
let second = Probe("ikinci-sabit")
let shared = Probe("ortak-sabit")

let a = first.eval(5)
let b = second.eval(5)
let c = shared.eval(5)
let d = shared.eval(5)

for probe in [first, second, shared] {
    _ = probe.use { a + b + c + d }
    _ = probe.use { a + b + c + d }
    probe.report()
}
```

```
birinci-sabit: 1 degerlendirme, 2 kullanim -> hemen
ikinci-sabit: 1 degerlendirme, 2 kullanim -> hemen
ortak-sabit: 2 degerlendirme, 2 kullanim -> her-kullanimda
```

**DT7.** Bu ölçüm, ifadenin yazılışını değil, `eval` çağrısının sayısını okur; `Probe`'u dil özelliği saymıyoruz.

#### Özet

- Swift güvenliği derleyiciyle sağlar: atanmamış sabit okunamaz, isteğe bağlı olmayan değer `nil` olamaz.
- İfade gücü tip çıkarımıdır; başarım hedefi bu derste ölçülmez.
- Kursun ölçü ekseni: bu ifade ne zaman değerlendiriliyor? Dört an: hemen, ilk kullanımda, her kullanımda, hiç.
- `Probe` kuralı sayıyı okur; niyeti okumaz.
- Bu dersin anı: **hemen** (`let` sabiti, tanımlandığı satırda).

#### Sonraki Adım

Sabit tanımlandığı anda değerlendirilir; peki `var` yeniden atanınca ne olur, ve bir sabit tanımlandıktan sonra atanabilir mi? Sıradaki ders, sabitlerle değişkenlerin atama anlarını ölçüyor.

### Sabitler ve Değişkenler

Source: https://academia.sh/tr/kurslar/swift/sabitler-ve-degiskenler

`let` ve `var` ile tip çıkarımını, açık tip bildirimini ve atama anını ölçer: sabit hemen mi değerlendirilir, atama her seferinde mi?

Önceki derste bir `let` sabitinin tanımlandığı satırda `hemen` değerlendirildiğini gördün. Bu ders o gözlemi üç yönden zorluyor: sabite tip yazmak bir şeyi değiştirir mi, `var` her atamada ne yapar, ve değer tanımlandıktan sonra atanırsa an kayar mı? Değişken ve tip kavramlarını Programlama Temelleri kursundan (M01/K02) biliyorsun; burada yalnız Swift'e özgü kısmı ölçüyoruz.

#### Sabit, Değişken ve Tip

Swift'te `let` bir kez bağlanan sabiti, `var` sonradan değişebilen değişkeni tanımlar. Tipi çoğu zaman yazmazsın: derleyici sağ taraftan çıkarır (tip çıkarımı, type inference). İstersen tipi iki noktadan sonra yazarsın (açık tip bildirimi, explicit type annotation). Sabite ikinci kez atama yapmak derleyicide reddedilir: aşağıdaki parça Swift 6.4'te derlenmez, bu yüzden çıktısızdır.

```swift
let rate = 4
rate = 5
```

`var` için aynı atama serbesttir. Fark yalnız yazım değil: `var` satırı her atamada sağ tarafı yeniden değerlendirir.

#### Üç Atama Biçimi

Önce `Probe.swift` yardımcı dosyası; her derste aynı kalır.

```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)")
    }
}
```

Şimdi üç `Probe`: biri tipi çıkarılan sabiti, biri tanımı ile ataması ayrılmış sabiti, biri de üç kez atanan değişkeni izler.

```swift
// main.swift — sabit ve degisken atama anlari
let inferredProbe = Probe("cikarilan-sabit")
let deferredProbe = Probe("ertelenen-sabit")
let counterProbe = Probe("degisken-atamasi")

let rate = inferredProbe.eval(4)
let explicitRate: Double = 4
_ = inferredProbe.use { rate }
_ = inferredProbe.use { rate }
print("\(type(of: rate)) \(type(of: explicitRate))")

let isWeekend = false
let level: Int
if isWeekend {
    level = deferredProbe.eval(1)
} else {
    level = deferredProbe.eval(2)
}
_ = deferredProbe.use { level }
_ = deferredProbe.use { level }

var total = counterProbe.eval(0)
total = counterProbe.eval(total + 5)
total = counterProbe.eval(total + 7)
_ = counterProbe.use { total }
_ = counterProbe.use { total }

let allProbes = [inferredProbe, deferredProbe, counterProbe]
for probe in allProbes { probe.report() }
tally(allProbes)
```

```
Int Double
cikarilan-sabit: 1 degerlendirme, 2 kullanim -> hemen
ertelenen-sabit: 1 degerlendirme, 2 kullanim -> hemen
degisken-atamasi: 3 degerlendirme, 2 kullanim -> her-kullanimda
hemen: 2
ilk-kullanimda: 0
her-kullanimda: 1
hic: 0
```

İlk satır tipleri gösterir: tipi yazılmayan `4` bir `Int`, `Double` olarak bildirilen aynı sayı bir `Double` oldu. Tip çıkarımı değerlendirme anını değiştirmedi; çıkarılan sabit tanımlandığı satırda, ilk kullanımdan önce değerlendirildi, yani `hemen`.

`level` ilginç bir durumdur: tanım (`let level: Int`) ile değer arasına bir `if` girdi, ama atama kullanımdan önce yapıldı. Dalların ikisi de `eval` çağırır, çalışan ise biri; sayı bir, an yine `hemen`. Derleyici atamadan önce okumayı reddeder (önceki dersin ilk parçası), bu yüzden tanım ile atama arasında okuma olamaz; ama atama bir kullanım sarmalının içinde yapılırsa ölçüm `ilk-kullanimda` verir (aşağıdaki Ertelenmiş İlk Değer bölümü).

`var` ise başka. `total` üç kez atandı ve her atama sağ tarafı yeniden değerlendirdi; kural bunu `her-kullanimda` okur. Değişken tek kez bağlanan bir sabit değildir; her atama yeni bir değerlendirmedir.

**DT8.** `counterProbe` atamaları kullanımdan önce yapılır, ama üç değerlendirme olduğu için kural `her-kullanimda` der; sayı birden çoksa "önce mi sonra mı" bakılmaz.

**DT9.** `type(of:)` çıktısı yalnız `Int` ve `Double` adlarını basar; tip adı ölçüm eksenine değil, çıkarımın sonucuna işaret eder.

#### Tip Çıkarımı ve Açık Tip Bildirimi

Bir sabit ya da değişken tanımlarken tipi yazmak zorunda değilsin. `let attempts = 3` satırında derleyici sağdaki değişmezden `Int` çıkarır; buna tip çıkarımı (type inference) denir. İstersen tipi iki noktadan sonra açıkça yazarsın: `let ratio: Double = 3`. Buna açık tip bildirimi (explicit type annotation) denir. İkisinin farkı yalnız tipi kimin belirlediğidir: çıkarımda değerden tip, açık bildirimde tipten değer biçimi türer.

Fark en çok değişmez bir sayı yazdığında görünür. `3` değişmezi çıkarımda `Int` olur, ama sol tarafa `Double` yazarsan aynı değişmez `3.0` olarak okunur. Aşağıdaki blok aynı `3` değişmezini iki yolla bağlar, her ikisini bir `Probe`'dan geçirir ve iki kez okur.

```swift
// Literal.swift — ayni degismez, iki tip
let inferredProbe = Probe("cikarilan-int")
let declaredProbe = Probe("acik-double")
let attempts = inferredProbe.eval(3)
let ratio: Double = declaredProbe.eval(3)
_ = inferredProbe.use { attempts }
_ = inferredProbe.use { attempts }
_ = declaredProbe.use { ratio }
_ = declaredProbe.use { ratio }
print(attempts, ratio)
print(type(of: attempts), type(of: ratio))
inferredProbe.report()
declaredProbe.report()
```

```
3 3.0
Int Double
cikarilan-int: 1 degerlendirme, 2 kullanim -> hemen
acik-double: 1 degerlendirme, 2 kullanim -> hemen
```

`attempts` `3` olarak, `ratio` ise `3.0` olarak yazıldı; `type(of:)` satırı tiplerin `Int` ve `Double` olduğunu doğruluyor. Değişmezin yazılışı aynıydı, ama bağlamdan gelen tip onu iki farklı değere çevirdi. İki `Probe` ise aynı anı verdi: ikisi de `hemen`. Tip yalnız değerin ne olduğunu belirler, ne zaman değerlendirileceğini belirlemez. Derleyici tipi kodu çalıştırmadan bulur; çalışma sırasında geriye kalan yalnız tek bir `eval` çağrısıdır.

Bu yüzden açık tip bildirimi iki iş görür. Birincisi, çıkarımın seçeceğinden farklı bir tip istediğinde onu söylemektir. İkincisi, kodu okuyana değerin niyetini göstermektir. Ölçüm ekseni açısından ise ikisi eşittir.

**DT10.** Çıkarım ve bildirim aynı `3` değişmeziyle ölçüldü; daha karmaşık bir ifadede tipin ne kadar erken bulunduğu bu dersin dışındadır.

#### Demet Çözümleme ve Tek Satırda Çok Tanım

Swift bir sabit ya da değişken satırında birden çok ad tanımlamana izin verir. Virgülle ayırarak `var left = 0, right = 0` yazabilirsin; her ad kendi ifadesiyle ayrı ayrı bağlanır. Başka bir yol, bir demeti açmaktır: `let (low, high) = (1, 9)` iki sabit tanımlar ve demetin elemanlarını sırayla onlara dağıtır. Bu söz dizimi demet çözümleme (tuple decomposition) olarak anılır.

Aklına gelen soru şudur: iki ad aynı satırda tanımlandığında ifadeleri aynı anda mı değerlendirilir, yoksa biri öbüründen önce mi? Aşağıdaki blok iki ayrı `Probe` ile iki adı bir demetle tanımlar, ardından iki `var` adı tek satırda tanımlayıp sırayla atar.

```swift
// Decompose.swift — demet cozumleme ve tek satirda cok tanim
let lowProbe = Probe("alt-sinir")
let highProbe = Probe("ust-sinir")
let (low, high) = (lowProbe.eval(1), highProbe.eval(9))
_ = lowProbe.use { low }
_ = lowProbe.use { low + 1 }
_ = highProbe.use { high }
_ = highProbe.use { high - 1 }
var left = 0, right = 0
left = lowProbe.eval(low)
right = highProbe.eval(high)
_ = lowProbe.use { left }
_ = highProbe.use { right }
print(low, high, left, right)
lowProbe.report()
highProbe.report()
```

```
1 9 1 9
alt-sinir: 2 degerlendirme, 3 kullanim -> her-kullanimda
ust-sinir: 2 degerlendirme, 3 kullanim -> her-kullanimda
```

Demetin iki elemanı da tanım satırında, hiçbir okumadan önce değerlendirildi. Ama bu bir tek değerlendirme değildi: `lowProbe` ve `highProbe` birer değerlendirme gösterdi, sonra `left` ve `right` atamaları ikinci birer değerlendirme ekledi. Bu yüzden iki ölçüm satırı da iki değerlendirme, üç kullanım ve `her-kullanimda` verdi. Tek satırda tanım ya da demet çözümleme, ifadeleri tek bir adımda birleştirmez; her eleman kendi ifadesi kadar değerlendirilir.

Burada ilke şudur: bir ifadenin kaç kez değerlendirildiğini, onu kaç satırda yazdığın değil, `eval` benzeri kaç değerlendirme noktası olduğu belirler. Demet çözümlemesi yazımı kısaltır, çalışma sırasını değiştirmez.

**DT11.** Demet çözümleme iki elemanlı ve sabit değişmezle ölçüldü; iç içe demetlerin çözülme sırası bu dersin dışındadır.

#### Gölgeleme

Bir iç kapsamda dış kapsamla aynı adı yeniden tanımlayabilirsin. Buna gölgeleme (shadowing) denir: iç kapsamdaki ad, dış kapsamdakini kapsam bitene kadar görünmez kılar. Dış değişken ya da sabit hiçbir şekilde değişmez; yalnız iç kapsamdaki okumalar yeni ada gider. Bu davranış, bir değeri geçici olarak dönüştürüp aynı adı kullanmanın güvenli yoludur.

Gölgeleme yeni bir atama değildir. `amount` ikinci kez `let` ile tanımlandığında dış `amount` olduğu gibi kalır; yeni tanım ikinci bir değerlendirmedir ve yalnız kendi kapsamında yaşar. Aşağıdaki blok bir `do` kapsamı içinde dış `amount` sabitini gölgeler, iç değeri yazdırır, kapsamdan çıkar ve dış değeri yeniden yazdırır.

```swift
// Shadow.swift — golgeleme dis degeri degistirmez
let outerProbe = Probe("dis-kapsam")
let innerProbe = Probe("ic-kapsam")
let amount = outerProbe.eval(1)
do {
    let amount = innerProbe.eval(amount + 10)
    _ = innerProbe.use { amount }
    _ = innerProbe.use { amount * 2 }
    print(amount)
}
_ = outerProbe.use { amount }
_ = outerProbe.use { amount + 5 }
print(amount)
outerProbe.report()
innerProbe.report()
```

```
11
1
dis-kapsam: 1 degerlendirme, 2 kullanim -> hemen
ic-kapsam: 1 degerlendirme, 2 kullanim -> hemen
```

İç kapsamda `amount` 11 çıktı: iç tanımın sağ tarafındaki `amount` hâlâ dış sabitti, çünkü iç ad henüz bağlanmamıştı. Kapsamdan çıkınca `amount` yeniden 1 oldu; gölgeleme dış sabiti hiç değiştirmedi. İki `Probe` ayrı ayrı bir değerlendirme ve iki kullanım gösterdi: ikisi de `hemen`. Gölgeleme yeni bir değerlendirme ekler, ama bir yeniden atama olmadığı için dış tarafın sayısını artırmaz.

Pratikte gölgeleme, değişken adlarını çoğaltmadan bir değeri dönüştürmenin sade yoludur. Karşılığında okuyanın hangi `amount` ile karşılaştığını bilmesi gerekir; kapsamı kısa tutmak bu yükü azaltır.

**DT12.** Gölgeleme `do` kapsamıyla ölçüldü; fonksiyon parametresini ya da döngü değişkenini gölgelemenin davranışı bu dersin dışındadır.

#### Ertelenmiş İlk Değer

Bir `let` sabitine tanımlandığı satırda değer vermek zorunda değilsin. Tipini yazıp değeri sonra atayabilirsin, yeter ki derleyici her yolda sabite tam olarak bir kez değer verildiğini görebilsin. Bu kural, `let` bildiriminin "değişmez" anlamını korur: sabit ilk değerini alana kadar okunamaz, aldıktan sonra yeniden atanamaz. Dallanan bir akışta her dal sabiti bir kez doldurur ve sabit, dalın sonunda hazırdır.

Bunun ölçülecek bir yan etkisi vardır: yalnız seçilen dalın ifadesi çalışır. Aşağıdaki blok bir fonksiyon yazar. Fonksiyonda `label` sabiti önce bildirilir, sonra `if` ile bir dalda `Probe`'dan gelen değerle, öbür dalda sabit bir dizgiyle doldurulur. Fonksiyon önce `true`, sonra `false` ile çağrılır.

```swift
// Deferred.swift — ertelenmis ilk deger
func pick(open: Bool, probe: Probe) -> String {
    let label: String
    if open {
        label = probe.eval("acik")
    } else {
        label = "kapali"
    }
    return label
}
let pickProbe = Probe("ertelenmis-sabit")
print(pickProbe.use { pick(open: true, probe: pickProbe) })
print(pickProbe.use { pick(open: false, probe: pickProbe) })
pickProbe.report()
```

```
acik
kapali
ertelenmis-sabit: 1 degerlendirme, 2 kullanim -> ilk-kullanimda
```

`true` çağrısı `acik`, `false` çağrısı `kapali` verdi. Her çağrıda yeni bir `label` sabiti tanımlandığı ve her biri bir kez doldurulduğu için derleyici bunu kabul etti. `Probe` ise iki kullanıma karşılık tek değerlendirme gösterdi: `probe.eval` yalnız `true` dalında çalıştı, `false` dalının sabit dizgisi `Probe`'a hiç uğramadı. İlk kullanımda değerlendirilen ve sonra bir daha çalışmayan bir ifade, `ilk-kullanimda` anının işaretidir; burada ikinci çağrı o ifadeye hiç ulaşmadı.

Bu davranış, ertelenmiş ilk değerin bir kural olduğunu gösterir: her yol sabiti tam olarak bir kez doldurur, doldurmayan yol hiçbir şey hesaplamaz. Sabitin değeri, akış hangi dala girerse onun ifadesinden gelir. Derleyici bir dalda atamayı unutursan, sabiti o noktadan sonra kullanmana izin vermez.

Bu kural bir fonksiyonun dönüş değerini hazırlarken de işe yarar: sabit bildirilir, dallar onu doldurur ve fonksiyon sonunda tek bir `return` kalır. Yerine `var` kullanıp sonradan değiştirmek de çalışırdı, ama `let` derleyiciye ve okuyana değerin bir kez atandığını söyler.

**DT13.** Ertelenmiş ilk değer iki dalla ve bir fonksiyon içinde ölçüldü; üst düzey kodda ya da özelliklerde aynı yapının kuralları bu dersin dışındadır.

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

Bir sabitin `hemen` değerlendirilmesi her yerde geçerli değildir. Başka dosyada tanımlı bir küresel sabit, ilk erişimde kurulur. Aşağıdaki yardımcı dosya başlatma kapanışına bir `print` koyar; kapanış `let` tanımının içindedir.

```swift
// Config.swift — baska dosyadaki kuresel sabit
let appName: String = {
    print("appName kuruluyor")
    return "academia"
}()
```

`main.swift` ise aynı kalıpta bir yerel sabit tanımlar ve iki sabitin ne zaman kurulduğunu satır sırasıyla gösterir.

```swift
// main.swift — kuresel ve yerel sabitin kurulus ani
print("baslangic")
let localName: String = {
    print("localName kuruluyor")
    return "academia"
}()
print("ikinci satir")
print("okunan: \(appName)")
print("yerel: \(localName)")
```

```
baslangic
localName kuruluyor
ikinci satir
appName kuruluyor
okunan: academia
yerel: academia
```

`localName` `main.swift` üst düzeyinde, yazıldığı satırda kuruldu (`hemen`); `appName` ise ikinci satırdan sonra, ilk okunduğu `print` içinde kuruldu (`ilk-kullanimda`). Aynı yazılışın iki farklı anı vardır.

**DT14.** `Probe` bu gözleme giremez: `Probe` `Sendable` değildir ve Swift 6 kurallarında küresel sabit olarak tutulamaz; bu yüzden an burada `print` sırasından okunur, sayıdan değil.

**DT15.** Bu ölçüm yalnız bir küresel sabit ve yalnız bir yardımcı dosya içindir; sıralama kuralının başka durumlarını (örneğin birden çok küresel sabitin birbirine bağlı kurulması) ölçmüyoruz.

#### Özet

- `let` bir kez bağlanır, ikinci atama derleyicide reddedilir; `var` yeniden atanabilir.
- Tip çıkarımı ile açık tip bildirimi değerlendirme anını değiştirmez.
- Ertelenmiş başlatma yalnız atamadan sonra okunabilir; atama kullanımdan önceyse `hemen`, bir kullanım sarmalının içindeyse `ilk-kullanimda` verir.
- Her `var` ataması sağ tarafı yeniden değerlendirir; kural bunu `her-kullanimda` okur.
- Bu dersin anı: **hemen** (tanımlandığı ya da atandığı satırda değerlendirilen sabit).

#### Sonraki Adım

Sabitlerin ve değişkenlerin atama anını ölçtük. Bir sonraki ders, üzerlerine konan değerlerin tiplerine bakıyor: `Int`, `Double`, `Bool` ve `String` arasındaki dönüşüm ne zaman yapılıyor?

### Temel Tipler

Source: https://academia.sh/tr/kurslar/swift/temel-tipler

`Int`, `Double`, `Bool` ve `String` tiplerini tanıtır; dönüşümün açık olduğunu ve bir dönüşüm ifadesinin hemen değerlendirildiğini ölçer.

Sabit ve değişkenin atama anını gördük: tanımlandığı satırda, `hemen`. Şimdi atanan değerin kendisine bakıyoruz. Swift'in dört temel tipi var: tam sayı için `Int`, kayan noktalı sayı için `Double`, mantıksal değer için `Bool` ve dizgi (string) için `String`. Bu dersin sorusu şu: bir tipi başka bir tipe çeviren ifade ne zaman değerlendiriliyor?

#### Dönüşüm Açıktır

Swift iki tipi kendiliğinden karıştırmaz. `Int` ile `Double` toplamak istersen derleyici programı reddeder: aşağıdaki parça Swift 6.4'te derlenmez, bu yüzden çıktısızdır.

```swift
let count = 1
let size = 2.5
print(count + size)
```

Çözüm dönüşümü yazmaktır: `Double(count)`. Dönüşüm kendi başına bir ifadedir ve bir `let` içine yazılırsa o satırda bir kez değerlendirilir. Bu dersin ölçümü bunu doğrular. Önce her derste aynı kalan yardımcı dosya.

```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)")
    }
}
```

Ölçüm iki `Probe` ile başlar: biri dönüşümü, ikincisi bir demet elemanını izler. Dönüşümün hemen yanında iki kayan noktalı gözlem ve bir `Int.max` basımı durur; çünkü `Double` karşılaştırmasının neden sürpriz verdiği bu dersin konusudur. `marked` yardımcı fonksiyonu demet elemanlarının sırasını `print` ile gösterir. `unusedProbe` ise hiç okunmayan bir demet elemanını izler.

```swift
// main.swift — donusum ve demet ani
let conversionProbe = Probe("tip-donusumu")
let tupleProbe = Probe("demet-elemani")
let unusedProbe = Probe("okunmayan-eleman")

let wholeCount = 7
let ratio = conversionProbe.eval(Double(wholeCount) / 2)
_ = conversionProbe.use { ratio }
_ = conversionProbe.use { ratio }
print(ratio)
print(Int.max)
print(0.1 + 0.2 == 0.3)
print(0.1 + 0.2)

func marked(_ n: Int) -> Int { print("deger \(n)"); return n }
let triple = (marked(1), marked(2), marked(3))
print(triple.1)

let record = (size: tupleProbe.eval(3), spare: unusedProbe.eval(9))
_ = tupleProbe.use { record.size }
_ = tupleProbe.use { record.size }

let allProbes = [conversionProbe, tupleProbe, unusedProbe]
for probe in allProbes { probe.report() }
tally(allProbes)
```

```
3.5
9223372036854775807
false
0.30000000000000004
deger 1
deger 2
deger 3
2
tip-donusumu: 1 degerlendirme, 2 kullanim -> hemen
demet-elemani: 1 degerlendirme, 2 kullanim -> hemen
okunmayan-eleman: 1 degerlendirme, 0 kullanim -> hemen
hemen: 3
ilk-kullanimda: 0
her-kullanimda: 0
hic: 0
```

`ratio` yedinin ikiye bölümünü 3.5 olarak verir, çünkü bölme `Double` üzerinde yapıldı. `Int.max` en büyük `Int` değerini basar; bu değerin bir fazlası bir program hatasıdır ve dersin ölçümünden çıkarıldı. `0.1 + 0.2 == 0.3` beklentinin tersine `false` verir: toplama tam 0.3'ü değil, ona çok yakın bir sayıyı üretir. Kayan noktalı sayı ikilik tabanda tam gösterilemediği için eşitlik yerine fark ölçülür; bu bir Swift özelliği değil, `Double` temsilinin sonucudur.

Dönüşüm satırı bir kez, tanımlandığı satırda, ilk kullanımdan önce değerlendirildi: `tip-donusumu` iki kullanımla `hemen` çıktı. Demet elemanları ise soldan sağa değerlendirildi: `deger 1`, `deger 2`, `deger 3`. Demet (tuple), birden çok değeri tek bir değerde toplayan yapıdır; elemanlar tanım satırında hazır olur.

**DT16.** `Int.max` yazdırması yalnız 64 bitlik `Int` için geçerlidir; ölçümde kullanılan derleyici 64 bitlik bir hedef için derledi.

**DT17.** `0.1 + 0.2 == 0.3` iki sabit ifadeyle ölçüldü; hesabı yapan değerlendirmenin derleyicide mi koşumda mı yapıldığı bu derste ayırt edilmiyor.

#### Sayısal Tip Başlatıcıları

Swift farklı sayısal tipler arasında örtük dönüşüm yapmaz; dönüşümü sen yazarsın. Bunun aracı tipin başlatıcısıdır: `Double(7)`, `Int(3.9)`, `UInt8(200)`. Ondalıklı bir değeri `Int` yapan başlatıcı kesirli kısmı atar, yani sıfıra doğru keser; `Int(3.9)` 3, `Int(-3.9)` ise -3 verir. Yuvarlama gerekiyorsa ayrı bir işlem yazarsın.

Bu davranışı ölçmek için aşağıdaki blok ondalıklı bir sayıyı bir `Probe` üzerinden üretir ve iki farklı başlatıcıdan geçirir. Başlatıcı ifadenin kendisidir; bu yüzden sayıyı üreten `eval` çağrısı başlatıcının argümanı içinde durur.

```swift
// Convert.swift — baslaticilarla donusum
let truncateProbe = Probe("kesme-donusumu")
let widenProbe = Probe("genisletme-donusumu")
let cut = Int(truncateProbe.eval(3.9))
let negativeCut = Int(truncateProbe.eval(-3.9))
let wide = Double(widenProbe.eval(7))
let small = UInt8(widenProbe.eval(200))
_ = truncateProbe.use { cut + negativeCut }
_ = truncateProbe.use { cut - negativeCut }
_ = widenProbe.use { wide }
_ = widenProbe.use { small }
print(cut, negativeCut, wide, small)
truncateProbe.report()
widenProbe.report()
```

```
3 -3 7.0 200
kesme-donusumu: 2 degerlendirme, 2 kullanim -> her-kullanimda
genisletme-donusumu: 2 degerlendirme, 2 kullanim -> her-kullanimda
```

`Int(3.9)` 3, `Int(-3.9)` -3 verdi; yani dönüşüm sıfıra doğru keser. `Double(7)` `7.0` yazıldı ve `UInt8(200)` 200 olarak kaldı. İki ölçüm satırı da iki değerlendirme gösterdi: her başlatıcı çağrısı argümanını bir kez değerlendirdi ve her `Probe` iki başlatıcıya bağlı olduğu için sayı ikiye çıktı. Bu yüzden iki satır da `her-kullanimda` okunur; sayı dönüşümün değil, ölçüm düzeneğinin eseridir.

Burada önemli olan başlatıcının hata verebildiği yerdir: `UInt8(300)` gibi bir değer bu tipe sığmaz ve program durur. Bu yüzden dönüşüm yazarken değerin hedef tipe sığdığını önceden bilmek gerekir. Ders bu durumu çalıştırmaz.

**DT18.** Dönüşümler yalnız tipe sığan değerlerle ölçüldü; sığmayan değerin durumu bu derste çalıştırılmaz, yalnız anlatılır.

#### Taşma İşleçleri

Tam sayı tiplerinin sınırı vardır. `UInt8` değeri 0 ile 255 arasında kalır. Normal `+` işleci sonucu bu sınırın dışına taşırsa program durur. Swift bunun yanında taşmayı kabul eden işleçler de sunar: `&+`, `&-` ve `&*`. Bu işleçler sonucu tipin kapasitesine göre başa sarar. `&` öneki, taşmayı bilerek istediğini söylemenin yoludur; sessizce olmaz, işleci seçerek olur.

Aşağıdaki blok `UInt8` değerine bir fonksiyon içinde `&+` ile sayı ekler. Eklenen sayı bir `Probe` üzerinden geldiği için her çağrı bir değerlendirme sayılır. İlk çağrı 250 değerine 10 ekler ve sınırı aşar; ikinci çağrı güvenli bir değerle çalışır.

```swift
// Wrap.swift — tasma isleci basa sarar
func bump(_ value: UInt8, probe: Probe) -> UInt8 {
    value &+ probe.eval(10)
}
let wrapProbe = Probe("tasma-isleci")
print(wrapProbe.use { bump(250, probe: wrapProbe) })
print(wrapProbe.use { bump(1, probe: wrapProbe) })
let floor: UInt8 = 0
print(floor &- 1, UInt8.max)
wrapProbe.report()
```

```
4
11
255 255
tasma-isleci: 2 degerlendirme, 2 kullanim -> her-kullanimda
```

`250 &+ 10` 260 olmadı; sonuç 4 çıktı, çünkü 255'ten sonra sayım 0'a döndü. `1 &+ 10` ise normal 11 verdi. Üçüncü satırdaki `0 &- 1` değeri `UInt8.max` ile aynı olan 255 çıktı: sınırın altına inen sayı üst uca sarıldı. Ölçüm satırı iki değerlendirme gösteriyor: her `bump` çağrısı operandı bir kez değerlendirdi, bu yüzden an `her-kullanimda` olur.

Taşma işleçleri ölçülebilir, çünkü program durmaz. Normal `+` ile aynı taşma ise durdurur ve bu yüzden bu derste çalıştırılmaz. Hangisini seçeceğin niyetine bağlıdır: sarmayı istiyorsan `&+`, taşmanın hata olmasını istiyorsan `+` yaz.

**DT19.** Taşma işleci `UInt8` ile ölçüldü; işaretli tiplerin `&+` ile sarma davranışı bu derste ölçülmedi.

#### Tip Takma Adı ve Sınırlar

Her sayısal tipin bir alt ve üst sınırı vardır ve tip bunları `min` ile `max` statik özellikleri olarak sunar: `Int8.min` -128, `Int8.max` 127, `UInt8.max` 255. Bir tipin sınırını bilmek taşma sorusunu yerinde yanıtlar. Kodda bir tipe başka bir ad vermek istediğinde `typealias` kullanırsın: `typealias Pixel = UInt8`. Takma ad yeni bir tip değildir; aynı tipin ikinci adıdır, sınırlarını ve davranışını aynen taşır.

Aşağıdaki blok `Pixel` takma adını tanımlar, onunla bir sabit bağlar ve sabitin tipini ve sınırlarını yazdırır. Sabit bir `Probe`'dan gelir ve iki kez okunur.

```swift
// Alias.swift — tip takma adi ayni tiptir
typealias Pixel = UInt8
let aliasProbe = Probe("takma-ad-sabiti")
let brightness: Pixel = aliasProbe.eval(200)
_ = aliasProbe.use { brightness }
_ = aliasProbe.use { brightness &+ 100 }
print(type(of: brightness), Pixel.max, Int8.min)
aliasProbe.report()
```

```
UInt8 255 -128
takma-ad-sabiti: 1 degerlendirme, 2 kullanim -> hemen
```

`type(of: brightness)` `UInt8` yazdı, yani takma ad yeni bir tip yaratmadı. `Pixel.max` 255, `Int8.min` ise -128 çıktı. `Probe` tek bir değerlendirme gösterdi ve sabit kullanımdan önce kurulduğu için an `hemen` oldu. Takma ad yalnız ad düzeyinde bir kolaylıktır; çalışma zamanında hiçbir şey eklemez.

Sınır özelliklerini bilmek, dönüşüm ve taşma kararlarının temelidir. Bir değerin sığıp sığmayacağını `max` ile karşılaştırarak önceden yoklayabilirsin; böylece sığmayan bir dönüşümü hiç çalıştırmazsın.

**DT20.** Sınır değerleri `UInt8` ve `Int8` ile gösterildi; diğer tam sayı tiplerinin sınırları bu dersin dışındadır.

#### Ondalığı Tam Sayıya Çevirmek

Bir `Double` değerini `Int` yapmanın tek bir yolu yoktur ve üç yaygın yol farklı sonuç verir. `Int(value)` ondalık kısmı atar, yani sıfıra doğru keser. `value.rounded()` en yakın tam değere yuvarlar; tam ortadaki değerde sıfırdan uzağa gider. `value.rounded(.down)` ise daima küçük olana iner. Üçü de önce bir `Double` üretir ya da doğrudan `Int` verir; hangi yolu seçtiğin, negatif sayılarda sonucu değiştirir.

Aşağıdaki blok bir sayıyı üç yolla çevirir. Çevrilecek değer bir `Probe`'dan gelir ve fonksiyon pozitif ve negatif bir değerle çağrılır.

```swift
// Rounding.swift — ondaligi tam sayiya cevirmek
func convert(_ value: Double, probe: Probe) -> (Int, Int, Int) {
    let source = probe.eval(value)
    return (Int(source), Int(source.rounded()), Int(source.rounded(.down)))
}
let roundProbe = Probe("yuvarlama")
print(roundProbe.use { convert(2.5, probe: roundProbe) })
print(roundProbe.use { convert(-2.5, probe: roundProbe) })
roundProbe.report()
```

```
(2, 3, 2)
(-2, -3, -3)
yuvarlama: 2 degerlendirme, 2 kullanim -> her-kullanimda
```

Pozitif `2.5` için üç yol `(2, 3, 2)` verdi: kesme 2, en yakına yuvarlama 3, aşağı yuvarlama 2. Negatif `-2.5` için sonuç `(-2, -3, -3)` oldu. Kesme sıfıra doğru gittiği için `-2` verdi; en yakına yuvarlama tam ortada sıfırdan uzağa gittiği için `-3`; aşağı yuvarlama ise daima küçük olana indiği için yine `-3` verdi. Pozitif sayıda kesme ile aşağı yuvarlama aynı sonucu verir, negatif sayıda ayrılırlar. `Probe` iki çağrıya karşılık iki değerlendirme gösterdi, bu yüzden an `her-kullanimda` okunur.

Dönüşüm yolunu seçerken sorman gereken şey, negatif değerlerin de gelip gelmediğidir. Gelmiyorsa üç yolun ikisi aynı işi görür; geliyorsa her birinin kuralı ayrı bir karar demektir.

**DT21.** Yuvarlama `2.5` ve `-2.5` ile ölçüldü; diğer yuvarlama kuralları (`.up`, `.toNearestOrEven`) bu dersin dışındadır.

#### Ondalık Eşitliği

İkili kayan nokta sayıları, onluk tabandaki bazı basit kesirleri tam olarak tutamaz. `0.1`, `0.2` ve `0.3` bunların en bilinenidir: her biri en yakın temsil edilebilir değere yuvarlanır. İki sayıyı toplayıp üçüncüyle `==` ile karşılaştırmak bu yüzden beklenmedik bir sonuç verebilir. Ondalık sayıları karşılaştırırken tam eşitlik yerine, kabul edilebilir bir fark sınırı seçersin.

Aşağıdaki blok bu durumu ölçer. Toplamın ilk terimi bir `Probe`'dan gelir. Blok toplamı, tam eşitlik sonucunu ve farkın küçük bir sınırın altında kalıp kalmadığını yazdırır.

```swift
// Equality.swift — ondalik esitligi
func sum(probe: Probe) -> Double {
    probe.eval(0.1) + 0.2
}
let equalProbe = Probe("ondalik-toplam")
let total = equalProbe.use { sum(probe: equalProbe) }
print(total)
print(total == 0.3)
print(abs(total - 0.3) < 0.000001)
equalProbe.report()
```

```
0.30000000000000004
false
true
ondalik-toplam: 1 degerlendirme, 1 kullanim -> ilk-kullanimda
```

Toplam `0.30000000000000004` yazdırdı, tam eşitlik `false` verdi, küçük sınırla yapılan karşılaştırma ise `true` çıktı. `0.1` ve `0.2` ikili tabanda tam yazılamadığı için toplamları `0.3` ile aynı değerde buluşmadı; fark son basamaktadır. Bu yüzden ondalık sayılarda `==`, hesaplanmış değerler için güvenilir bir sınama değildir. Ölçüm tek değerlendirme gösterdi: ilk terim `Probe`'dan bir kez alındı ve sonraki karşılaştırmalar yeniden hesaplamadı, an `ilk-kullanimda` olur.

Sınırın büyüklüğü işe göre seçilir; burada `0.000001` yeterli bir ölçüydü. Para gibi kuruşu önemli olan değerlerde ondalık tip yerine tam sayı birimleriyle çalışmak daha güvenlidir.

Yuvarlama kuralını sonuca bakarak değil, niyete göre seç. Parayı ya da sayacı aşağı kesmek istiyorsan `.down`, en yakın değeri istiyorsan `rounded()`, yalnız tam kısmı almak istiyorsan `Int(value)` yazarsın. Üçünü aynı sanmak, negatif değer ilk geldiği gün hatalı bir sonuca yol açar. Çevrilen `Double` tam sayı aralığının dışındaysa `Int(value)` programı durdurur; bu ders bu durumu çalıştırmaz.

Ondalık eşitlikte de aynı ilke geçerlidir: sonucu ne olması gerektiğine göre bir sınırla karşılaştır, tam eşitliğe güvenme. Sınırı bir isimli sabite almak, aynı ölçünün birden çok yerde tutarlı kalmasını sağlar.

**DT22.** Eşitlik `Double` ile ölçüldü; `Float` ve `Decimal` gibi tiplerde aynı toplamın sonucu bu dersin dışındadır.

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

`okunmayan-eleman` satırına bak: demet içinde bir eleman tanımlandı, bir kez değerlendirildi, hiç okunmadı. Kural `hemen` der, çünkü değerlendirme kullanımdan önce yapıldı ve hiç kullanım yoktu. Yani demet elemanı okunmasa da değerlendirilir; `hic` yalnızca ifade hiç değerlendirilmediğinde ortaya çıkar. Bu satır, ölçüm koşulunun neden "en az iki kullanım" istediğini de gösterir: sıfır kullanımda kural, boşa değerlendirilen bir ifadeyi gerçek bir `hemen`'den ayıramaz.

**DT23.** Sıfır kullanım bu satırda bilerek bırakıldı; derste başka hiçbir gözlem sıfır kullanım taşımaz.

#### Özet

- `Int`, `Double`, `Bool` ve `String` dört temel tiptir; tipler kendiliğinden karıştırılmaz.
- Dönüşüm açıktır: `Double(count)` bir ifadedir ve tanımlandığı satırda değerlendirilir.
- `0.1 + 0.2 == 0.3` kayan noktalı gösterim yüzünden `false` verir.
- Demet elemanları soldan sağa ve okunmasa da değerlendirilir.
- Bu dersin anı: **hemen** (dönüşüm ifadesi `let` satırında bir kez).

#### Sonraki Adım

`String` bu derste yalnız adıyla geçti. Bir sonraki ders dizgiye iniyor: bir karakter nedir, `count` ile `utf8.count` neden ayrışır ve dizgi içine yerleştirilen ifade kaç kez kuruluyor?

### Dizgi İşlemleri

Source: https://academia.sh/tr/kurslar/swift/dizgi-islemleri

`String` değerinin karakter ve bayt sayısını, dizinlemeyi ve ifade yerleştirmeyi ölçer: yerleştirilen ifade her kullanımda yeniden kurulur.

`String` bir önceki derste yalnız adıyla geçti. Bu derste üç soru var: bir dizgide kaç karakter vardır, dizginin belirli bir karakterine nasıl ulaşılır, ve dizgiye yerleştirilen bir ifade kaç kez kuruluyor? Üçüncüsü kursun ekseninde durur; ilk ikisi onu anlamanın zeminidir.

#### Karakter Modeli

Swift'te dizgi `Character` değerlerinden oluşur ve `count` karakter sayar. Dizginin bellekteki bayt sayısı ise `utf8.count` ile ayrıca sorulur. İkisi aynı olmak zorunda değildir: Türkçe harflerin bir kısmı birden çok baytla saklanır.

Dizgiye tam sayıyla da erişemezsin. `word[1]` yazmak Swift 6.4'te derleyicide reddedilir: aşağıdaki parça derlenmez, bu yüzden çıktısızdır. Neden: karakterler bellekte farklı sayıda bayt tuttuğu için n. karakterin yeri baştan sayılmadan bulunamaz.

```swift
let word = "ağaç"
print(word[1])
```

Doğrusu `String.Index` kullanmaktır: `word.startIndex` ilk karakteri gösterir, `word.index(after:)` bir sonrakini. Ölçüm bunu da kapsar.

#### İfade Yerleştirme

Dizgi içine `"\(değer)"` yazmak ifade yerleştirmedir (string interpolation). `CustomStringConvertible` uyan bir tip, yerleştirildiği her yerde kendi `description` özelliğini sorar. İfade yerleştirme dizgiyi kuran bir ifadedir; kurulduğu her seferde `description` yeniden çağrılır.

```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)")
    }
}
```

Ölçüm, `description` özelliğinin içinde bir `Probe` taşıyan küçük bir tip kurar. İki dizgi yerleştirmesi aynı değeri iki kez kullanır; ayrıca `Counter` tipiyle saklanan dizginin sonradan değişmediği gösterilir.

```swift
// main.swift — karakter modeli ve ifade yerlestirme
struct Tag: CustomStringConvertible {
    let probe: Probe
    var description: String { probe.eval("etiket") }
}

struct Counter: CustomStringConvertible {
    var n: Int
    var description: String { "n=\(n)" }
}

let word = "ağaç"
print("ağaç: \(word.count) karakter, \(word.utf8.count) bayt")
let second = word.index(after: word.startIndex)
print(word[second])

let descriptionProbe = Probe("description-cagrisi")
let savedProbe = Probe("saklanan-dizgi")

let tag = Tag(probe: descriptionProbe)
_ = descriptionProbe.use { "ilk: \(tag)" }
_ = descriptionProbe.use { "ikinci: \(tag)" }

let saved = Tag(probe: savedProbe)
let text = "\(saved)"
_ = savedProbe.use { text }
_ = savedProbe.use { text }

var counter = Counter(n: 1)
let snapshot = "\(counter)"
counter.n = 2
print("\(snapshot) \(counter)")

let allProbes = [descriptionProbe, savedProbe]
for probe in allProbes { probe.report() }
tally(allProbes)
```

```
ağaç: 4 karakter, 6 bayt
ğ
n=1 n=2
description-cagrisi: 2 degerlendirme, 2 kullanim -> her-kullanimda
saklanan-dizgi: 1 degerlendirme, 2 kullanim -> hemen
hemen: 1
ilk-kullanimda: 0
her-kullanimda: 1
hic: 0
```

Dört harfli `ağaç` dört karakterdir ama altı bayttır: `ğ` ve `ç` ikişer bayt tutar. `word[second]` ikinci karakteri, `ğ`'yi verir; dizin bir sayı değil, dizgiyi bilen bir değerdir.

`description-cagrisi` iki kullanımda iki değerlendirme gösterdi: her ifade yerleştirme, değeri istendiği anda yeniden kurdu. Kural bunu `her-kullanimda` okur. Dizgi kuran ifade tembel bir ifade değil, her yazıldığı yerde çalışan bir ifadedir.

**DT24.** `ağaç` hizalı sütuna konmadı ve dizede olduğu gibi durur: ölçümün konusu bu sözcüğün karakter ve bayt sayısıdır.

**DT25.** `count` ve `utf8.count` değerleri bu sözcük için ölçüldü; başka bir sözcüğün ya da başka bir yazı sisteminin sayıları bu dersin iddiası değildir.

#### Birleştirme ve Çok Satırlı Dizgi

Dizgileri `+` işleciyle birleştirebilirsin: `"a" + "b"` yeni bir dizgi üretir. Birden çok satıra yayılan metin için üç tırnaklı çok satırlı dizgi değişmezi vardır: açılış `"""` kendi satırında durur, metin sonraki satırlarda başlar ve kapanış `"""` yine kendi satırında biter. Kapanış üç tırnağın girintisi, bütün satırlardan çıkarılır; böylece metni kodun girintisine uydurarak yazarsın ve çıktıda fazladan boşluk kalmaz.

Bir dizginin bir ifadeye göre biçimlenmesi, ifadenin her çağrıda yeniden değerlendirilmesi demektir. Aşağıdaki blok bir fonksiyon yazar; fonksiyon gelen metni köşeli parantezle sarar ve sarma işlemini bir `Probe` üzerinden geçirir. Fonksiyon iki kez çağrılır. Son satırda çok satırlı bir değişmez yazdırılır.

```swift
// Join.swift — birlestirme ve cok satirli dizgi
func decorate(_ text: String, probe: Probe) -> String {
    probe.eval("[" + text + "]")
}
let joinProbe = Probe("birlestirme")
print(joinProbe.use { decorate("a", probe: joinProbe) })
print(joinProbe.use { decorate("b", probe: joinProbe) })
let note = """
    ilk satir
      ikinci satir
    """
print(note)
joinProbe.report()
```

```
[a]
[b]
ilk satir
  ikinci satir
birlestirme: 2 degerlendirme, 2 kullanim -> her-kullanimda
```

İlk iki satır `[a]` ve `[b]` oldu. Sonraki iki satır çok satırlı dizginin çıktısıdır: `ilk satir` satırı girintisiz, `ikinci satir` satırı iki boşluk girintili basıldı. Kapanış `"""` dört boşluk girintiliydi, bu girinti bütün satırlardan çıkarıldı; ikinci satırın kendi fazladan iki boşluğu ise korundu. Ölçüm satırı iki değerlendirme gösteriyor: `decorate` çağrısı gövdesinde birleştirmeyi her seferinde yeniden hesaplar, yani an `her-kullanimda` olur.

Çok satırlı dizgi değişmezi ise bir ifade olarak yalnız bir kez okunur; yazılan metin bir sabittir ve çalışma sırasında birleştirilmez. Birleştirmeyi `+` ile yaptığında ise birleştirme çalışma sırasında yapılabilir. Hangisini seçeceğin metnin sabit mi, hesaplanan mı olduğuna bağlıdır.

**DT26.** Birleştirme ve çok satırlı değişmez ASCII metinle ölçüldü; satır sonu karakterlerinin platforma göre farkı bu dersin dışındadır.

#### Substring Dilimdir, Dizgi Değildir

Bir dizgiden bir parça aldığında, örneğin `prefix(7)` ya da bir aralıkla dilimleme yaptığında, sonuç `String` değil `Substring` tipindedir. `Substring`, kaynak dizgi üzerinde bir dilim görünümüdür ve dizginin pek çok yöntemini taşır. Bir `Substring`'i kalıcı olarak saklamak ya da bir `String` bekleyen yere vermek istediğinde onu `String(...)` ile dizgiye çevirirsin. Bu açık dönüşüm, kısa ömürlü bir dilimin kaynak dizgiyi gereksiz yere tutmasını önler.

Aşağıdaki blok bir cümleyi bir `Probe` ile üretir, ondan bir dilim alır, dilimin tipini yazdırır ve dilimi `String` yapar. Kaynak dizginin değerlendirme sayısı dilimleme ve dönüştürme boyunca izlenir.

```swift
// Slice.swift — dilim ve dizgiye donusum
let sourceProbe = Probe("kaynak-cumle")
let sentence = sourceProbe.eval("merhaba dunya")
let head = sentence.prefix(7)
let owned = String(head)
_ = sourceProbe.use { head.count }
_ = sourceProbe.use { owned.count }
print(head, type(of: head))
print(owned, type(of: owned))
sourceProbe.report()
```

```
merhaba Substring
merhaba String
kaynak-cumle: 1 degerlendirme, 2 kullanim -> hemen
```

`head` `merhaba` yazdı ve tipi `Substring` çıktı; `String(head)` ise aynı yedi karakterli metni `String` olarak taşıdı. `Probe` tek değerlendirme gösterdi: dilimleme ve dönüştürme kaynak dizgiyi yeniden üretmedi, var olan değer üzerinde çalıştı. Dilim kaynağın bir parçasına bakar; dönüştürme ise o parçanın bağımsız bir dizgi kopyasını verir. Değerlendirme kullanımdan önce tek kez yapıldığı için an `hemen` olur.

Bu ayrım API'lerde kendini gösterir: bir fonksiyon `String` bekliyorsa, dilimi doğrudan veremezsin, önce dönüştürmen gerekir. Derleyicinin bunu zorlaması, dilimin ömrünü bilinçli yönetmeni sağlar.

**DT27.** Dilim `prefix` ile ölçüldü; aralık işleciyle dilimlemenin ve dilim üzerinde başka işlemlerin aynı tipi verdiği bu dersin dışındadır.

#### Karakter, Unicode Skaları ve Bayt

Dizgi sayımlarında üç ayrı birim vardır. Karakter (`Character`), okuyanın tek bir simge olarak gördüğü birimdir. Unicode skaları, Unicode tablosundaki tek bir kod noktasıdır. Bayt ise UTF-8 kodlamasında dizginin saklandığı birimdir. Çoğu ASCII karakter için üçü de birdir; ama birleşik karakterlerde ayrışırlar. Örneğin `e` harfi ile ona eklenen vurgu işareti iki skalar olarak yazılır, buna rağmen tek karakter olarak görünür.

Aşağıdaki blok `e` harfini ve onu izleyen birleştirici vurgu işaretini `\u{301}` kaçış dizisiyle yazar. Dizginin üç sayımı, bir fonksiyonun içinde bir `Probe` üzerinden ölçülür. Fonksiyon iki farklı metinle çağrılır; ikinci metin düz ASCII bir kelimedir.

```swift
// Units.swift — karakter, skalar ve bayt sayilari
func measure(_ text: String, probe: Probe) -> (Int, Int, Int) {
    probe.eval((text.count, text.unicodeScalars.count, text.utf8.count))
}
let unitProbe = Probe("dizgi-birimleri")
let combined = "e\u{301}"
print(unitProbe.use { measure(combined, probe: unitProbe) })
print(unitProbe.use { measure("kedi", probe: unitProbe) })
unitProbe.report()
```

```
(1, 2, 3)
(4, 4, 4)
dizgi-birimleri: 2 degerlendirme, 2 kullanim -> her-kullanimda
```

Birleşik metin `(1, 2, 3)` verdi: bir karakter, iki Unicode skaları, üç bayt. `e` harfi UTF-8 kodlamasında bir bayt, birleştirici işaret iki bayt tutar. Düz `kedi` kelimesinde ise üç sayı dört, dört ve dört çıktı. İki çağrı iki değerlendirme getirdi, bu yüzden an `her-kullanimda` okunur.

Karakter sayısını okuyanın gördüğü simgelerle ilişkilendirebilirsin, ama bayt sayısı depolama boyutuna bakar. İkisini karıştırmak, sınır denetimlerinde sessiz hataya yol açar. Bir dizginin uzunluğunu sorarken hangi birimi sorduğunu seç.

**DT28.** Üç sayım iki metinle ölçüldü; diğer birleşik karakter ve emoji dizilerinin sayımları bu dersin dışındadır.

#### Karşılaştırma, Önek ve Sonek

`==` iki dizgiyi karakter karakter karşılaştırır, ama "aynı karakter" kavramı Unicode'a göredir: farklı skalar dizileriyle yazılmış ama okuyana aynı görünen iki dizgi, kurallı eşdeğerlikle eşit sayılır. Bu, tek bir `é` ile `e` ve birleştirici vurgu işaretinden oluşan dizgiyi aynı yapar. `hasPrefix` ve `hasSuffix` ise dizginin başını ve sonunu denetler; onlar da aynı karakter anlayışını kullanır.

Aşağıdaki blok iki yazımı karşılaştırır. Tek skalarlı yazım bir `Probe`'dan geçer, ikincisi iki skalarlıdır. Sonra `hasPrefix` ve `hasSuffix` sonuçları yazdırılır.

```swift
// Compare.swift — karsilastirma, onek ve sonek
func same(_ left: String, _ right: String, probe: Probe) -> Bool {
    probe.eval(left == right)
}
let compareProbe = Probe("dizgi-esitligi")
let single = "\u{E9}"
let composed = "e\u{301}"
print(compareProbe.use { same(single, composed, probe: compareProbe) })
print(single.unicodeScalars.count, composed.unicodeScalars.count)
let word = "swift-dili"
print(word.hasPrefix("swift"), word.hasSuffix("dil"))
compareProbe.report()
```

```
true
1 2
true false
dizgi-esitligi: 1 degerlendirme, 1 kullanim -> ilk-kullanimda
```

Tek skalarlı `é` ile iki skalarlı `e\u{301}` karşılaştırması `true` verdi, ama skalar sayıları `1` ve `2` olarak ayrıştı. Yani `==` skalara değil, kurallı eşdeğerliğe bakar: iki dizgi bayt bayt aynı olmasa da aynı karakteri temsil ediyorsa eşit sayılır. `hasPrefix("swift")` doğru, `hasSuffix("dil")` yanlış çıktı, çünkü kelime `dili` ile bitiyor, `dil` ile değil. `Probe` tek değerlendirme gösterdi ve an `ilk-kullanimda` olur.

Bu, kullanıcı girdisini karşılaştıran kodda önemlidir: klavyeden ya da bir dosyadan gelen metin, aynı görünen ama farklı skalarlarla yazılmış olabilir. `==` bunu senin yerine çözer; ham bayt karşılaştırması çözmezdi.

**DT29.** Karşılaştırma tek bir birleşik karakterle ölçüldü; büyük küçük harf duyarsız karşılaştırma bu dersin dışındadır.

#### Dizinle Erişim

Swift dizgisine `text[3]` gibi bir tam sayıyla erişemezsin. Karakterlerin bayt uzunluğu değiştiği için üçüncü karakterin nerede başladığı, baştan sayılmadan bilinemez. Bunun yerine `String.Index` değerleri kullanılır: `text.startIndex` ilk karakteri, `text.index(text.startIndex, offsetBy: 3)` dördüncü karakteri gösterir. Dizin hesabı dizginin içinde yürüyerek yapılır.

Aşağıdaki blok bir kelimeden üçüncü karakteri dizinle alır. Kelime bir `Probe`'dan gelir; fonksiyon iki farklı kelimeyle çağrılır.

```swift
// Index.swift — dizinle erisim
func third(_ text: String, probe: Probe) -> Character {
    let source = probe.eval(text)
    let position = source.index(source.startIndex, offsetBy: 2)
    return source[position]
}
let indexProbe = Probe("dizin-erisimi")
print(indexProbe.use { third("kalem", probe: indexProbe) })
print(indexProbe.use { third("defter", probe: indexProbe) })
indexProbe.report()
```

```
l
f
dizin-erisimi: 2 degerlendirme, 2 kullanim -> her-kullanimda
```

`kalem` kelimesinde üçüncü karakter `l`, `defter` kelimesinde `f` çıktı. Dizin her kelimede baştan yürünerek bulundu: `index(_:offsetBy:)` karakterleri tek tek geçti, çünkü bir karakterin kaç bayt tuttuğu önceden bilinmiyor. Tam sayı alt simgesi bu yüzden yoktur; olsaydı, uzun bir metinde sessizce yavaş bir arama gizlenirdi. `Probe` iki çağrıya karşılık iki değerlendirme gösterdi ve an `her-kullanimda` okunur.

Dizinle erişim, sık yapılan bir işse bir `Array(text)` dönüşümüyle karakter dizisine çevrilip tam sayıyla okunabilir; ama bu, dizginin tamamını bir kez kopyalamak demektir.

Dizin tipinin ayrı bir tip olması bir de güvenlik sağlar: bir dizgiden alınan dizini başka bir dizgide kullanmak hatalı sonuç verebilir. Dizini hesaplayan satır ile onu kullanan satırı aynı dizgiye bağlı tutmak bu yüzden iyi bir alışkanlıktır. Aynı fonksiyonda `source` adıyla tek bir yerel kopya kullanmak, dizinin ve dizginin aynı değere bakmasını güvenceye alır.

Bir dizginin sonundaki karaktere erişmek için de aynı mekanizma geçerlidir: `text.index(before: text.endIndex)` son karakterin dizinini verir. Dizin hesapları baştan ya da sondan yürüdüğü için, uzun dizgilerde bunları bir döngünün içinde tekrar tekrar yapmak yerine bir kez hesaplayıp saklamak daha ucuzdur. Ölçüm, kelimenin her çağrıda yeniden alındığını gösteriyor.

**DT30.** Dizin erişimi ASCII kelimelerle ölçüldü; sınır dışı dizinin durdurduğu durumlar bu derste çalıştırılmaz.

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

Dizgi bir kez kurulup bir `let` içinde saklanırsa tablo değişir. `saklanan-dizgi` iki kullanıma karşın bir değerlendirme gösterdi: `"\(saved)"` yalnız saklandığı satırda çalıştı, sonraki iki okuma yalnız dizgiyi okudu. Kural `hemen` der.

Bu bir bedeli de getirir. `snapshot` dizgisi `counter` değişmeden önce kuruldu, ve dizgi kurulduktan sonra değişen değer ona yansımaz: `snapshot` hâlâ `n=1`, yeniden kurulan dizgi `n=2`. Saklanan dizgi bir fotoğraftır; ifade yerleştirme ise her seferinde yeniden çekilen bir fotoğraftır.

**DT31.** `Counter` bir yapıdır ve `snapshot` onun kurulduğu andaki değeri taşır; sınıf tiplerinin davranışı bu derste ölçülmedi.

#### Özet

- `count` karakter sayar, `utf8.count` bayt sayar; `ağaç` 4 karakter ve 6 bayttır.
- Dizgi tam sayıyla dizinlenmez; `String.Index` kullanılır.
- İfade yerleştirme, `description` özelliğini her değerlendirmede yeniden çağırır.
- Saklanan dizgi tek bir değerlendirmedir ve sonradan değişen değeri yansıtmaz.
- Bu dersin anı: **her kullanımda** (iki yerleştirmede iki `description` çağrısı).

#### Sonraki Adım

Dizgiler `+` ve `==` gibi işleçlerle de birleşir ve karşılaştırılır. Sıradaki ders işleçlere bakıyor: bir işlecin sağ tarafı her zaman değerlendirilir mi?

### İşleçler

Source: https://academia.sh/tr/kurslar/swift/islecler

Aritmetik, karşılaştırma ve aralık işleçlerini tanıtır; `||`, `&&` ve üçlü işlecin sağ tarafının hiç değerlendirilmeyebileceğini ölçer.

Dizginin ifade yerleştirmesi her kullanımda yeniden kuruluyordu. Bir işleç de iki ifadeyi birleştirir; soru şu: ikisi de değerlendiriliyor mu? Önceki bir kursta (M10/K03) "kaç kez koştu" bir sayıydı; bu kurs bir an soruyor ve işleçlerde en dikkat çekici an `hic`'tir.

#### Aritmetik, Karşılaştırma ve Aralık

Aritmetik işleçler `+`, `-`, `*`, `/` ve `%`'dir. İki `Int`'i bölmek `Int` verir: `7 / 2` değeri 3'tür ve `7 % 2` bölümden kalan 1'dir; `7.0 / 2` ise 3.5 verir. Karşılaştırma işleçleri `<`, `>`, `==` gibi bir `Bool` döndürür. Aralık işleçleri (range operators) bir değer dizisi kurar: `1...4` her iki ucu içerir, `1..<4` üst ucu dışarıda bırakır. `contains` aralığın bir değeri içerip içermediğini söyler.

İşleç öncelikleri matematikteki gibidir: çarpma ve bölme toplamadan önce gelir, parantez sırayı değiştirir. Karşılaştırma işleçleri `Bool` döndürdüğü için doğrudan bir `if` koşulu olabilir; bir sonraki derste bunu kullanacağız. `==` iki değerin eşit olup olmadığını sorar, tek `=` ise atama yapar ve ikisini karıştırmak yaygın bir hatadır. Aralıklar da bir değerdir: `1...4` bir sabite atanabilir, bir döngüde dolaşılabilir ya da `Array(...)` ile diziye çevrilebilir.

Ölçü aracı yine aynı yardımcı dosya. Her `Probe` sarmaladığı ifadenin kaç kez değerlendirildiğini ve kaç kez kullanıldığını sayar; ikisinin ilişkisi anı verir.

```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)")
    }
}
```

Şimdi ölçüm bloğu. Her satır bir sonucu gösterir, ardından üç `Probe` kendi raporunu basar. Okurken şuna bak: önce işleçlerin sonuçları, sonra sağ tarafı ölçülen üç ifade. `isOpen` doğru olduğu için `||`'nin sağı hiç gerekmez; üçlü işlecin (`koşul ? a : b`) seçilmeyen dalı da öyle.

```swift
// main.swift — isleclerin sonuclari ve sag tarafi
print(7 / 2, 7 % 2, 7.0 / 2)
print((1...4).contains(4), (1..<4).contains(4))
print(Array(1..<4))

let orProbe = Probe("veya-isleci-sagi")
let andProbe = Probe("ve-isleci-sagi-dogru")
let ternaryProbe = Probe("uclu-isleci-dali")

let isOpen = true
_ = orProbe.use { isOpen || orProbe.eval(true) }
_ = orProbe.use { isOpen || orProbe.eval(true) }
_ = andProbe.use { isOpen && andProbe.eval(true) }
_ = andProbe.use { isOpen && andProbe.eval(true) }
_ = ternaryProbe.use { isOpen ? 1 : ternaryProbe.eval(2) }
_ = ternaryProbe.use { isOpen ? 1 : ternaryProbe.eval(2) }

let allProbes = [orProbe, andProbe, ternaryProbe]
for probe in allProbes { probe.report() }
tally(allProbes)
```

```
3 1 3.5
true false
[1, 2, 3]
veya-isleci-sagi: 0 degerlendirme, 2 kullanim -> hic
ve-isleci-sagi-dogru: 2 degerlendirme, 2 kullanim -> her-kullanimda
uclu-isleci-dali: 0 degerlendirme, 2 kullanim -> hic
hemen: 0
ilk-kullanimda: 0
her-kullanimda: 1
hic: 2
```

`veya-isleci-sagi` iki kullanımda sıfır değerlendirme gösterdi: `isOpen` doğruyken `||` sağ tarafa bakmaz. Üçlü işlecin seçilmeyen dalı da aynı: `uclu-isleci-dali` iki kullanımda `hic`. `&&` ise sol taraf doğru olduğu için sağa geçer ve sağ taraf her kullanımda yeniden değerlendirilir. Üç satır, iki sonuç: aynı sağ taraf ifadesi, işlecin sol tarafına göre ya hiç ya her seferinde değerlendirilir.

**DT32.** `isOpen` bir sabittir; sol tarafın başka bir değeri için sağ tarafın değerlendirilmesi bu derste ölçülmedi.

**DT33.** `7 / 2` ve `7 % 2` ifadelerinin sonuçları bu bloğun çıktısında görülür; başka işleç öncelikleri ölçülmedi.

#### Aralık İşleçleri

Swift iki aralık işleci sunar: kapalı aralık `1...3` her iki ucu da içerir, yarı açık aralık `1..<3` üst ucu dışarıda bırakır. Aralıklar bir döngüde, bir dilimlemede ya da bir kıyaslamada kullanılabilir ve `Array(...)` ile bir diziye çevrilebilir. Aralığın uç değerleri sıradan ifadelerdir; işleç yalnız bu iki ucu birleştirir.

Aşağıdaki blok iki aralık kurar. Kapalı aralığın üst ucu bir `Probe`'dan, yarı açık aralığın üst ucu başka bir `Probe`'dan gelir. Her aralık iki kez okunur: biri `count`, biri bir dizi olarak. Sonra iki aralığın içeriği yazdırılır.

```swift
// Range.swift — kapali ve yari acik aralik
let closedProbe = Probe("kapali-aralik")
let halfProbe = Probe("yari-acik-aralik")
let closed = 1...closedProbe.eval(3)
let half = 1..<halfProbe.eval(3)
_ = closedProbe.use { closed.count }
_ = closedProbe.use { Array(closed) }
_ = halfProbe.use { half.count }
_ = halfProbe.use { Array(half) }
print(Array(closed), Array(half))
print(closed.contains(3), half.contains(3))
closedProbe.report()
halfProbe.report()
```

```
[1, 2, 3] [1, 2]
true false
kapali-aralik: 1 degerlendirme, 2 kullanim -> hemen
yari-acik-aralik: 1 degerlendirme, 2 kullanim -> hemen
```

Kapalı aralık `[1, 2, 3]`, yarı açık aralık ise `[1, 2]` verdi; aynı üst uç 3 bir yerde içeride, bir yerde dışarıda kaldı. `contains(3)` satırı bunu doğruluyor. İki `Probe` da tek bir değerlendirme gösterdi: üst uç, aralık kurulurken bir kez hesaplandı ve sonraki okumalar yeniden hesaplamadı. Aralık bir değer olduğu için, ucu hesaplayan ifade tanım satırında çalışır; okumalar hazır değeri kullanır.

Aralık işleçlerinin güvenliği buradan gelir: uç ifadesi kısa devre yapmaz, hepsi hemen hesaplanır. Bu, daha önce gördüğümüz `&&` ve `||` işleçlerinden farklıdır, çünkü onların sağı koşula bağlıydı.

**DT34.** Aralıklar tam sayı uçlarıyla ve `Int` değerlerle ölçüldü; ondalıklı aralıkların ve `stride` ile adım atlamalı aralıkların davranışı bu dersin dışındadır.

#### Bileşik Atama ve İşleç Önceliği

Bileşik atama işleçleri bir değişkeni okuyup yeni değerle aynı yere yazar: `score += 3` ile `score = score + 3` aynı iş içindir. `-=`, `*=` ve `/=` aynı kalıba uyar. İşleçler ayrıca bir öncelik sırasına sahiptir: çarpma toplamadan önce işler, bu yüzden `2 + 3 * 4` 14 verir; parantez bu sırayı değiştirir ve `(2 + 3) * 4` 20 verir.

Ölçülecek şey, bileşik atamanın sağ tarafının her çalıştığında yeniden değerlendirilmesidir. Aşağıdaki blok bir `score` değişkenini iki kez artırır. Her artış, bir `Probe`'dan gelen bir çarpanı içerir. Önceliğin iki sonucu da aynı blokta yazdırılır.

```swift
// Compound.swift — bilesik atama ve oncelik
let stepProbe = Probe("artis-carpani")
var score = 2
_ = stepProbe.use { score += stepProbe.eval(3) * 2 }
_ = stepProbe.use { score += stepProbe.eval(3) * 2 }
print(score)
print(2 + 3 * 4, (2 + 3) * 4)
stepProbe.report()
```

```
14
14 20
artis-carpani: 2 degerlendirme, 2 kullanim -> her-kullanimda
```

`score` 2 ile başladı ve iki artıştan sonra 14 oldu; her artış `3 * 2` olan 6 ekledi. `2 + 3 * 4` ifadesi 14, parantezli biçim 20 çıktı. Ölçüm satırı iki değerlendirme gösterdi: bileşik atamanın sağ tarafı her çalıştığında yeniden hesaplanır. Bu yüzden an `her-kullanimda` olur; `+=` bir kez yazıldığı hâlde iki kez çalıştı.

Bileşik atamayı bir kısaltma olarak okuyabilirsin, ama kısaltmanın sağ tarafı yeniden değerlendirilen bir ifadedir. Sağ taraf yan etkili bir çağrıysa, bunu atamanın her çalışmasında beklemelisin.

**DT35.** Bileşik atama `Int` ile ölçüldü; ondalıklı sayılarda ya da dizgilerde `+=` davranışı bu dersin dışındadır.

#### Bölme ve Kalan

Tam sayı bölmesi sonucu tam sayı olarak keser: `7 / 2` 3 verir, 3.5 vermez. `%` işleci bölmeden kalanı döndürür: `7 % 2` 1 verir. Negatif sayılarda kalanın işareti bölünenin işaretini izler: `-7 % 3` -1 çıkar. Kesirli sonuç istiyorsan her iki tarafı da ondalıklı yazarsın: `7.0 / 2` 3.5 verir. Sıfıra bölmek ise programı durdurur; ders bunu çalıştırmaz.

Aşağıdaki blok bir tam sayıyı hem bölen hem bölünen olarak ayıran bir fonksiyon yazar. Bölen bir `Probe` üzerinden iki kez okunur: bir kez bölüm, bir kez kalan için. Fonksiyon bir pozitif ve bir negatif sayıyla çağrılır.

```swift
// Division.swift — bolme ve kalan
func divide(_ number: Int, probe: Probe) -> (Int, Int) {
    (number / probe.eval(2), number % probe.eval(2))
}
let divisorProbe = Probe("bolen")
print(divisorProbe.use { divide(7, probe: divisorProbe) })
print(divisorProbe.use { divide(-7, probe: divisorProbe) })
print(7.0 / 2, -7 % 3)
divisorProbe.report()
```

```
(3, 1)
(-3, -1)
3.5 -1
bolen: 4 degerlendirme, 2 kullanim -> her-kullanimda
```

`divide(7)` `(3, 1)`, `divide(-7)` ise `(-3, -1)` verdi: bölüm sıfıra doğru kesildi ve kalan bölünenin işaretini aldı. Ondalıklı bölme 3.5, `-7 % 3` ise -1 çıktı. Ölçüm satırında dört değerlendirme var, çünkü her çağrı bölen ifadesini iki kez yazıyor. İki çağrı bu yüzden dört değerlendirme getirdi ve an `her-kullanimda` okunur.

Bu ölçümün gösterdiği küçük bir tuzak da var: aynı ifadeyi iki yerde yazmak iki değerlendirme demektir. Değeri bir kez hesaplayıp bir sabite almak hem sayıyı azaltır hem okunuşu sadeleştirir.

**DT36.** Bölme ve kalan `Int` ile ölçüldü; sıfıra bölme ve `Int.min / -1` gibi durduran durumlar bu derste çalıştırılmaz.

#### Üçlü Koşul İşleci

`koşul ? a : b` biçimindeki üçlü işleç, koşula göre iki değerden birini seçer. Koşul `true` ise `a`, değilse `b` sonuç olur. İşlecin üç operandı vardır ve sıralaması hep aynıdır: önce koşul, sonra "doğruysa" ifadesi, sonra "yanlışsa" ifadesi. Küçük bir `if`-`else` ifadesinin değer üreten kısa biçimidir.

Burada ölçülecek kural, seçilmeyen kolun hiç çalışmamasıdır. Aşağıdaki blok iki kolu da ayrı bir `Probe`'a bağlar ve işleci iki kez `true` koşuluyla çalıştırır.

```swift
// Ternary.swift — uclu kosul isleci
let yesProbe = Probe("dogru-kol")
let noProbe = Probe("yanlis-kol")
func choose(_ flag: Bool, yes: Probe, no: Probe) -> Int {
    flag ? yes.eval(10) : no.eval(20)
}
_ = yesProbe.use { choose(true, yes: yesProbe, no: noProbe) }
_ = noProbe.use { choose(true, yes: yesProbe, no: noProbe) }
print(choose(true, yes: yesProbe, no: noProbe))
yesProbe.report()
noProbe.report()
```

```
10
dogru-kol: 3 degerlendirme, 1 kullanim -> her-kullanimda
yanlis-kol: 0 degerlendirme, 1 kullanim -> hic
```

`choose(true)` her çağrıda `10` verdi. `dogru-kol` üç değerlendirme gösterdi: iki `use` çağrısı ve son `print` içindeki çağrı, üçü de doğru kola gitti. `yanlis-kol` ise sıfır değerlendirmeyle `hic` çıktı, çünkü hiçbir çağrıda seçilmedi. Koşul doğru olduğunda yanlış kolun ifadesi derlenir ama hiç çalıştırılmaz.

Bu, `&&` ve `||` ile aynı kurala dayanır: sonuç belli olduğunda kalan ifade değerlendirilmez. Üçlü işleçte yan etkili bir çağrıyı bir kola koyduğunda, o çağrının yalnız o kol seçildiğinde çalışacağını bilirsin.

Üçlü işleç ifade olduğu için bir değer üretir; bu yüzden bir sabite doğrudan atanabilir ya da bir fonksiyona argüman olarak verilebilir. Koşul uzadığında ya da kollar karmaşıklaştığında `if`-`else` ve ayrı bir sabit, aynı kuralı daha okunur yazmanın yoludur.

Bir kolun yan etkisi yoksa seçilmemesi fark edilmez; yan etkisi varsa, örneğin bir sayacı artırıyorsa ya da bir kaydı değiştiriyorsa, seçilmeyen kolda bu etkinin hiç gerçekleşmediğini bilmek hatayı önler. Ölçüm tam olarak bu farkı sayıyla gösterdi.

**DT37.** Üçlü işleç iki `Int` koluyla ölçüldü; iç içe üçlü işleçlerin okunurluğu bu dersin dışındadır.

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

Kısa devre değerlendirme (short-circuit evaluation), `||` ve `&&` işleçlerinin ikinci parametresini `@autoclosure` ile almasından gelir. Bir işleç, ikinci parametresini `@autoclosure` ile alan bir fonksiyondur; sağ taraf gerekmezse hiç çağrılmaz. Aynı işi yapan ama ikinci parametresini sıradan `Bool` alan bir fonksiyon sağ tarafı çağrıdan önce değerlendirir. Aşağıda bu iki imza yan yana durur. Her ikisini de iki kez, sol taraf yanlışken çağırıyoruz; böylece sağ tarafın değerlendirilmesi gerekmez ve fark yalnız imzadan doğar.

```swift
// main.swift — sag tarafin tipi: Bool ve autoclosure
func both(_ left: Bool, _ right: Bool) -> Bool { left && right }
func lazyBoth(_ left: Bool, _ right: @autoclosure () -> Bool) -> Bool { left && right() }

let eagerProbe = Probe("both-sagi")
let autoProbe = Probe("lazyboth-sagi")

_ = eagerProbe.use { both(false, eagerProbe.eval(true)) }
_ = eagerProbe.use { both(false, eagerProbe.eval(true)) }
_ = autoProbe.use { lazyBoth(false, autoProbe.eval(true)) }
_ = autoProbe.use { lazyBoth(false, autoProbe.eval(true)) }

let allProbes = [eagerProbe, autoProbe]
for probe in allProbes { probe.report() }
tally(allProbes)
```

```
both-sagi: 2 degerlendirme, 2 kullanim -> her-kullanimda
lazyboth-sagi: 0 degerlendirme, 2 kullanim -> hic
hemen: 0
ilk-kullanimda: 0
her-kullanimda: 1
hic: 1
```

İki fonksiyonun gövdesi aynı: `left && right`. `both` sağ tarafı çağrıdan önce değerlendirir, gövde `left` yanlış olduğu için kullanmasa da; kural `her-kullanimda` der. `lazyBoth` ise sağ tarafı bir kapanışa sarar ve gövde ona hiç dokunmaz: `hic`. Farkı yaratan işleç değil, parametre tipidir.

**DT38.** `@autoclosure` bu derste yalnız sağ tarafı erteleyen bir imza olarak ölçüldü; kapanış ayrıntıları kapanışlar dersine kalıyor.

Burada öğrenilen pratik sonuç şu: bir ifadenin sağ tarafında yan etkisi olan bir çağrı varsa, o çağrının koşulsuz çalışacağını varsaymak yanlıştır. `&&` ve `||` ile kurulan koşullar sırayla okunur ve soldaki ifade sonucu belirlediyse sağdaki hiç çalışmaz. Bir hesabın her durumda yapılmasını istiyorsan, onu işlecin dışında bir sabite al ve sonucu işleçte kullan.

#### Özet

- `/` iki `Int` için `Int` verir, `%` kalanı verir; `...` ve `..<` aralık kurar.
- `||`, `&&` ve üçlü işlecin sağ tarafı gerekmezse hiç değerlendirilmez.
- Kısa devre işleçte değil, ikinci parametrenin `@autoclosure` olmasında yaşar.
- Aynı gövdeyi `@autoclosure` olmadan yazarsan sağ taraf her çağrıda değerlendirilir.
- Bu dersin anı: **hiç** (sol taraf doğruyken `||` işlecinin sağı).

#### Sonraki Adım

Bir ifadenin hiç değerlendirilmediği başka bir yer var: `switch`'in ilk eşleşmeden sonraki dalları. Sıradaki ders denetim akışına, koşullara ve döngülere bakıyor.

### Denetim Akışı

Source: https://academia.sh/tr/kurslar/swift/denetim-akisi

Koşulları, döngüleri ve desen eşlemeli `switch` ifadesini tanıtır; ilk eşleşmeden sonraki `where` koşullarının hiç değerlendirilmediğini ölçer.

İşleçler iki ifadeyi birleştirdi ve sağ taraf gerekmediğinde hiç değerlendirilmedi. Denetim akışı aynı fikri bir adım yukarı taşır: hangi dalın çalışacağına koşullar karar verir ve ilk uygun dalı bulan yapı geri kalanına bakmaz. Bu dersin sorusu şu: bir zincirde, eşleşmeden sonra gelen koşullar ne zaman değerlendiriliyor?

#### Koşullar, Döngüler ve switch

`if` bir `Bool` koşulu sorar; `else if` zinciri koşulları yukarıdan aşağı dener. `for n in 1...3` bir aralıktaki her değer için gövdeyi çalıştırır, `while` koşul doğru olduğu sürece sürer. `switch` ise bir değeri desenlerle (pattern) karşılaştırır. Bir `case` bir değer, bir aralık, bir demet ya da `where` ile süslenmiş bir koşul olabilir. Swift'te `case` gövdeleri bir sonrakine düşmez; ilk eşleşen çalışır ve `switch` biter.

`switch` tükenmeli olmak zorundadır: her olası değer bir `case` tarafından karşılanmalı ya da bir `default` bulunmalıdır. Aşağıdaki parça `Int` için yalnız 1 ve 2'yi ele alır. Bu parça Swift 6.4'te derlenmez; derleyici onu reddeder, bu yüzden çıktısızdır.

```swift
let n = 3
switch n {
case 1: print("bir")
case 2: print("iki")
}
```

Hata gerçek bir güvencedir: yeni bir değer eklendiğinde karşılanmayan `switch` derlenmez. Çözüm `default` eklemek ya da kalan değerleri bir desenle yakalamaktır.

```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)")
    }
}
```

Ölçüm bloğu iki zincir kurar. İlkinde `grade` fonksiyonu 75 puanı değerlendirir: ilk koşul yanlış, ikincisi doğrudur ve üçüncüsüne hiç gelinmez. İkincisinde `describe` fonksiyonu `-5` değerini bir `switch` ile sınar: ilk `case`'in `where` koşulu doğrudur, sonraki `case`'in `where` koşulu hiç sorulmaz.

```swift
// main.swift — zincirde ilk eslesmeden sonrasi
func grade(_ score: Int, _ second: Probe, _ third: Probe) -> String {
    if score >= 90 {
        return "A"
    } else if second.eval(score >= 70) {
        return "B"
    } else if third.eval(score >= 50) {
        return "C"
    }
    return "F"
}

func describe(_ n: Int, _ first: Probe, _ later: Probe) -> String {
    switch n {
    case let x where first.eval(x < 0):
        return "negatif"
    case let x where later.eval(x > 100):
        return "buyuk"
    default:
        return "diger"
    }
}

for n in 1...3 { print("adim", n) }

let secondProbe = Probe("else-if-ikinci-kosul")
let thirdProbe = Probe("else-if-ucuncu-kosul")
let firstWhereProbe = Probe("switch-ilk-where")
let laterWhereProbe = Probe("switch-sonraki-where")

for _ in 1...2 {
    let letter = secondProbe.use { thirdProbe.use { grade(75, secondProbe, thirdProbe) } }
    print(letter)
    let kind = firstWhereProbe.use { laterWhereProbe.use { describe(-5, firstWhereProbe, laterWhereProbe) } }
    print(kind)
}

let allProbes = [secondProbe, thirdProbe, firstWhereProbe, laterWhereProbe]
for probe in allProbes { probe.report() }
tally(allProbes)
```

```
adim 1
adim 2
adim 3
B
negatif
B
negatif
else-if-ikinci-kosul: 2 degerlendirme, 2 kullanim -> her-kullanimda
else-if-ucuncu-kosul: 0 degerlendirme, 2 kullanim -> hic
switch-ilk-where: 2 degerlendirme, 2 kullanim -> her-kullanimda
switch-sonraki-where: 0 degerlendirme, 2 kullanim -> hic
hemen: 0
ilk-kullanimda: 0
her-kullanimda: 2
hic: 2
```

`else-if-ikinci-kosul` her çağrıda değerlendirildi, çünkü birinci koşul yanlış olduğu için zincir ona geldi. `else-if-ucuncu-kosul` ise iki çağrıda da `hic` çıktı: ikinci koşul zaten doğruydu ve zincir orada bitti. Aynı şey `switch` için de geçerli: ilk `where` her seferinde sınandı, sonraki `where` hiç sınanmadı. Yani `where` koşulundaki bir yan etki, daha önceki bir `case` eşleştiyse hiç gerçekleşmez.

**DT39.** Ölçüm tek bir giriş değeriyle (75 ve -5) yapıldı; başka girişlerde hangi koşulun `hic` kalacağı değişir.

**DT40.** `switch`'in kaynak sırasıyla yukarıdan aşağı denendiği ölçümden çıkarıldı; derleyicinin sırayı değiştirip değiştirmediği ayrıca ölçülmedi.

#### guard ile Erken Çıkış

`guard` bir koşulu doğrulamaya yarar ve koşul sağlanmazsa kapsamdan çıkmayı zorunlu kılar. Söz dizimi `guard koşul else { çıkış }` biçimindedir; `else` bloğu `return`, `break`, `continue` ya da bir durduran çağrıyla bitmek zorundadır. Koşul sağlanırsa kontrol `guard` satırının ardından devam eder ve koşulda bağlanan adlar o kapsamın geri kalanında görünür. `if` ile aynı işi görür, ama niyeti tersine çevirir: önce istenmeyen durum elenir, sonra asıl iş düz bir yolda yazılır.

Aşağıdaki blok pozitif sayıyı kabul eden bir fonksiyon yazar. Geçerli yol bir `Probe`'a, geçersiz yol başka bir `Probe`'a yazar. Fonksiyon üç sayıyla çağrılır ve her çağrıdan önce iki `Probe` birer kullanım kaydeder.

```swift
// Guard.swift — guard ile erken cikis
func check(_ number: Int, valid: Probe, invalid: Probe) -> String {
    guard number > 0 else { return invalid.eval("gecersiz") }
    return valid.eval("gecerli")
}
func observe<T>(_ watched: [Probe], _ work: () -> T) -> T {
    for probe in watched { _ = probe.use { 0 } }
    return work()
}
let validProbe = Probe("gecerli-yol")
let invalidProbe = Probe("gecersiz-yol")
for number in [5, -2, 7] {
    print(observe([validProbe, invalidProbe]) {
        check(number, valid: validProbe, invalid: invalidProbe)
    })
}
validProbe.report()
invalidProbe.report()
```

```
gecerli
gecersiz
gecerli
gecerli-yol: 2 degerlendirme, 3 kullanim -> her-kullanimda
gecersiz-yol: 1 degerlendirme, 3 kullanim -> ilk-kullanimda
```

Üç sayı `gecerli`, `gecersiz`, `gecerli` sonuçlarını verdi. Geçerli yol iki kez, geçersiz yol bir kez çalıştı: `gecerli-yol` iki değerlendirme ve üç kullanımla `her-kullanimda`, `gecersiz-yol` ise bir değerlendirme ve üç kullanımla `ilk-kullanimda` çıktı. Kullanım sayısı üç çıktı, çünkü `observe` her çağrıdan önce kullanımı kaydetti. Bir yolun ürettiği ifade, yalnız o yol seçildiğinde değerlendirilir; seçilmeyen yolun ifadesi hiç çalışmaz.

`guard`, hangi yolun hangi anda çalıştığını okunur kılar: istisna yolu en başta durur ve geride kalan satırlar yalnız koşul sağlandığında çalışır. Ölçüm bu okunuşu doğruluyor.

**DT41.** `guard` yalnız bir tam sayı koşuluyla ölçüldü; isteğe bağlı değerlerle `guard let` kullanımı sonraki konunun işidir.

#### switch Desenleri

`switch` ifadesi bir değeri sırayla desenlerle karşılaştırır. Desenler yalnız değişmez olmak zorunda değildir: demet desenleri birden çok değeri aynı anda karşılaştırır, `let` ile değer bağlanır ve `where` bir koşul ekler. Örneğin `case (let x, 0)` ikinci elemanı sıfır olan her demeti yakalar ve ilk elemanı `x` adına bağlar. Eşleşen ilk durum çalışır ve diğerleri hiç değerlendirilmez.

Aşağıdaki blok bir noktanın sınıfını bulan bir fonksiyon yazar. Üç durumun her biri kendi `Probe`'una yazar. Fonksiyon üç noktayla çağrılır: orijin iki kez, yatay eksendeki bir nokta bir kez; üçüncü durum hiç eşleşmez.

```swift
// Pattern.swift — switch desenleri
func classify(_ x: Int, _ y: Int, watched: [Probe]) -> String {
    switch (x, y) {
    case (0, 0):
        return watched[0].eval("orijin")
    case (let along, 0):
        return watched[1].eval("yatay \(along)")
    case (_, let height) where height > 5:
        return watched[2].eval("yuksek \(height)")
    default:
        return "diger"
    }
}
func observe<T>(_ watched: [Probe], _ work: () -> T) -> T {
    for probe in watched { _ = probe.use { 0 } }
    return work()
}
let originProbe = Probe("orijin-durumu")
let axisProbe = Probe("eksen-durumu")
let highProbe = Probe("yuksek-durum")
let cases = [originProbe, axisProbe, highProbe]
for point in [(0, 0), (4, 0), (0, 0)] {
    print(observe(cases) { classify(point.0, point.1, watched: cases) })
}
for probe in cases { probe.report() }
```

```
orijin
yatay 4
orijin
orijin-durumu: 2 degerlendirme, 3 kullanim -> her-kullanimda
eksen-durumu: 1 degerlendirme, 3 kullanim -> ilk-kullanimda
yuksek-durum: 0 degerlendirme, 3 kullanim -> hic
```

Üç çağrı `orijin`, `yatay 4`, `orijin` sonuçlarını verdi. `orijin-durumu` iki değerlendirmeyle `her-kullanimda`, `eksen-durumu` tek değerlendirmeyle `ilk-kullanimda`, `yuksek-durum` ise sıfır değerlendirmeyle `hic` çıktı. Üçüncü durum `where` koşuluna rağmen hiç denenmedi, çünkü önceki iki durum noktaları zaten yakalamıştı. Yani `switch` sırayla bakar, ilk eşleşmeyi alır ve gerisine dokunmaz.

Bu kural desenleri sıralarken önem taşır: genel bir durumu özel bir durumdan önce yazarsan özel durum hiç çalışmaz. Derleyici kapsam eksikliğini hata olarak reddeder, ama sıra yanlışlığını bazı durumlarda bulamaz.

**DT42.** Desenler demet ve `where` ile ölçüldü; numaralandırma durumları ve aralık desenleri bu dersin dışındadır.

#### Etiketli Deyimler

İç içe döngülerde `break` yalnız en içteki döngüyü bitirir. Dış döngüyü bitirmek için döngüye bir etiket verirsin: `outer: for row in 1...3 { ... }`. İçerideki `break outer` bu etikete atlar ve dış döngü biter. `continue` de aynı etiketle dış döngünün bir sonraki yinelemesine geçer. Etiket, hangi döngüden söz ettiğini açıkça yazmanın yoludur.

Aşağıdaki blok çarpım tablosunda belirli bir çarpımı arayan bir fonksiyon yazar. Her çarpım bir `Probe` üzerinden hesaplanır; böylece dönülen yineleme sayısı ölçülür. Fonksiyon iki hedefle çağrılır.

```swift
// Label.swift — etiketli break ile arama
func search(target: Int, probe: Probe) -> String {
    var found = "yok"
    outer: for row in 1...3 {
        for col in 1...3 {
            if probe.eval(row * col) == target {
                found = "\(row)x\(col)"
                break outer
            }
        }
    }
    return found
}
let searchProbe = Probe("carpim-aramasi")
print(searchProbe.use { search(target: 4, probe: searchProbe) })
print(searchProbe.use { search(target: 6, probe: searchProbe) })
searchProbe.report()
```

```
2x2
2x3
carpim-aramasi: 11 degerlendirme, 2 kullanim -> her-kullanimda
```

İlk arama `2x2`, ikincisi `2x3` buldu. Toplam on bir değerlendirme gerekti: ilk aramada beş çarpım hesaplandı (1, 2, 3, 2, 4), ikincisinde altı (1, 2, 3, 2, 4, 6). `break outer` bulunur bulunmaz iki döngüyü birden bitirdi, bu yüzden kalan çarpımlar hiç hesaplanmadı. Etiketsiz `break` yalnız iç döngüyü bitirirdi ve dış döngü bir sonraki satırla devam ederdi. An `her-kullanimda` okunur.

Etiketli çıkış, aramayı gereksiz çarpımlardan korur. Etiket kodun kaç döngü atladığını da gösterir; okuyan hangi döngünün bittiğini adından bilir.

**DT43.** Etiketli `break` iki iç içe döngüyle ölçüldü; `continue` ile etiket kullanımı bu dersin dışındadır.

#### repeat-while ile En Az Bir Tur

`while` döngüsü koşulu önce denetler; koşul baştan yanlışsa gövde hiç çalışmaz. `repeat-while` bunu tersine çevirir: gövde önce çalışır, koşul sonra denetlenir. Böylece gövde, koşul ne olursa olsun, en az bir kez çalışır. Bir değeri en az bir kez okumak ya da bir işlemi bir kez yapıp sonra devam edip etmeyeceğine karar vermek gerektiğinde bu biçim doğru araçtır.

Aşağıdaki blok iki döngüyü aynı baştan yanlış koşulla çalıştırır. Her gövde bir sayaca değerini bir `Probe`'dan gelen bir ile artırır. Biri `while`, öbürü `repeat-while` ile yazılmıştır.

```swift
// Repeat.swift — repeat-while en az bir tur
let whileProbe = Probe("while-govdesi")
let repeatProbe = Probe("repeat-govdesi")
var whileCount = 0
var repeatCount = 0
_ = whileProbe.use {
    while whileCount < 0 { whileCount += whileProbe.eval(1) }
}
_ = repeatProbe.use {
    repeat { repeatCount += repeatProbe.eval(1) } while repeatCount < 0
}
print(whileCount, repeatCount)
whileProbe.report()
repeatProbe.report()
```

```
0 1
while-govdesi: 0 degerlendirme, 1 kullanim -> hic
repeat-govdesi: 1 degerlendirme, 1 kullanim -> ilk-kullanimda
```

Aynı baştan yanlış koşulla iki döngü `0 1` verdi: `while` sayacı hiç artırmadı, `repeat-while` bir kez artırdı. `while-govdesi` sıfır değerlendirmeyle `hic`, `repeat-govdesi` ise bir değerlendirmeyle `ilk-kullanimda` çıktı. `repeat-while` döngüsünde koşul gövdeden sonra denetlendiği için gövdenin ifadesi, koşul yanlış olsa bile bir kez çalıştı.

Seçim sorusu şudur: gövde, koşuldan önce en az bir kez çalışmak zorunda mı? Bir değeri okuyup sonra karar veriyorsan `repeat-while`, koşul baştan sağlanmıyorsa hiçbir şey yapmak istemiyorsan `while` doğru araçtır.

**DT44.** `repeat-while` baştan yanlış bir koşulla ölçüldü; koşulun doğru kaldığı döngülerde iki biçimin aynı sonucu verdiği bu dersin dışındadır.

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

Zincirdeki koşullar bir kez sorulup bırakılırdı. Döngülerde koşul her turda yeniden sorulur. `for ... where` her yinelemede koşulu değerlendirir; yani `where` içindeki ifade bu satırda `her-kullanimda` olur. `while` ise bir ince ayrıntı taşır: koşul, döngüden çıkarken de bir kez daha değerlendirilir, çünkü döngüden çıkmanın tek yolu koşulun `false` dönmesidir. Üç tur süren bir `while` bu yüzden koşulu dört kez değerlendirir.

```swift
// main.swift — dongu kosullari
let whereProbe = Probe("for-where-kosulu")
let whileProbe = Probe("while-kosulu")

for _ in 1...2 {
    var evens: [Int] = []
    whereProbe.use {
        for n in 1...4 where whereProbe.eval(n % 2 == 0) { evens.append(n) }
    }
    print(evens)
    whileProbe.use {
        var i = 0
        while whileProbe.eval(i < 3) { i += 1 }
    }
}

let allProbes = [whereProbe, whileProbe]
for probe in allProbes { probe.report() }
tally(allProbes)
```

```
[2, 4]
[2, 4]
for-where-kosulu: 8 degerlendirme, 2 kullanim -> her-kullanimda
while-kosulu: 8 degerlendirme, 2 kullanim -> her-kullanimda
hemen: 0
ilk-kullanimda: 0
her-kullanimda: 2
hic: 0
```

İki tur boyunca `for ... where` koşulu sekiz kez, her tur dört yineleme için birer kez değerlendirildi. `while` koşulu ise iki turun her birinde üç yineleme yaptığı hâlde sekiz değerlendirme aldı: yineleme sayısı artı bir. Fazladan olan değerlendirme, döngüyü bitiren `false` sorgusudur.

**DT45.** `while` için "yineleme artı bir" sonucu bu üç yinelemelik örnekte ölçüldü; `break` ya da `return` ile çıkan bir döngüde ek sorgu yoktur, ancak bu derste ölçülmedi.

#### Özet

- `if` / `else if` zinciri koşulları yukarıdan aşağı dener ve ilk doğruda durur.
- `switch` tükenmelidir; eksik `case` derleyicide reddedilir.
- İlk eşleşmeden sonraki `where` koşulları ve `else if` koşulları hiç değerlendirilmez.
- `for ... where` koşulu her yinelemede, `while` koşulu yineleme sayısından bir fazla değerlendirilir.
- Bu dersin anı: **hiç** (eşleşmeden sonraki `where`).

#### Sonraki Adım

Bir koşul yalnız bir dalın çalışıp çalışmayacağını söyler. Bir de çağrı sırasında ne zaman değerlendirildiği sorulan ifadeler var: fonksiyonun varsayılan argümanı. Sıradaki ders, argüman etiketlerini ve varsayılan değerleri ölçüyor.

### Fonksiyonlar

Source: https://academia.sh/tr/kurslar/swift/fonksiyonlar

Argüman etiketlerini, varsayılan değerleri, demetle çoklu dönüşü ve `inout` parametreyi tanıtır; varsayılan argüman ifadesinin her çağrıda değerlendirildiğini ölçer.

Denetim akışında koşullar yalnız gerektiğinde değerlendirildi. Fonksiyonlarda soru bir adım ileri gidiyor: bir çağrının argümanı ne zaman değerlendirilir, bir parametrenin varsayılan değeri ne zaman? Swift'te bir fonksiyon, çağıranın okuyabileceği bir cümle gibi görünmek için tasarlanmıştır; bu dersin ölçümü o cümlenin arkasındaki değerlendirme sırasını gösterir.

#### Etiketler, Varsayılanlar ve Dönüş

Swift'te her parametrenin bir argüman etiketi (argument label) vardır ve çağrıda yazılır. `func move(from start: Int, to end: Int)` tanımında `from` ve `to` çağıranın gördüğü etiketlerdir, `start` ve `end` ise gövdenin kullandığı adlardır. Etiket yerine alt çizgi yazarsan çağrıda etiket olmaz: `func pad(_ text: String)`. Bir parametreye `= değer` eklemek varsayılan argüman (default argument) tanımlar; çağıran o parametreyi vermezse varsayılan ifade kullanılır.

Bir fonksiyon birden çok değer döndürmek için bir demet (tuple) döndürebilir: `-> (low: Int, high: Int)`. Etiketli demet çağıran tarafta `result.low` biçiminde okunur. `inout` parametre ise fonksiyonun çağıranın değişkenini değiştirmesini sağlar; çağrıda değişkenin önüne `&` yazılır ve değişken `var` olmak zorundadır.

```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)")
    }
}
```

Ölçüm bloğu önce bu dört özelliği çalıştırır. Ardından aynı gövdeye sahip iki fonksiyon kurar: `pad` genişliği çağıran vermezse bir `Probe` üzerinden varsayılan değeri alır, `padGiven` ise aynı varsayılana sahiptir ama biz her çağrıda genişliği kendimiz veririz. Fonksiyonlar `@MainActor` işaretlidir, çünkü varsayılan ifade üst düzeydeki `Probe`'u okur ve `Probe` Swift 6 kurallarında yalnız ana aktörden okunabilir.

```swift
// main.swift — etiket, varsayilan, demet ve inout
func move(from start: Int, to end: Int) -> Int { end - start }
func bounds(of values: [Int]) -> (low: Int, high: Int) {
    (values.min() ?? 0, values.max() ?? 0)
}
func increment(_ value: inout Int) { value += 1 }

print(move(from: 2, to: 5))
let found = bounds(of: [3, 1, 2])
print(found.low, found.high)
var total = 0
increment(&total)
increment(&total)
print(total)

let defaultProbe = Probe("varsayilan-ifade")
let givenProbe = Probe("verilen-iken-varsayilan")

@MainActor func pad(_ text: String, width: Int = defaultProbe.eval(6)) -> String {
    String(repeating: "*", count: width) + text
}

@MainActor func padGiven(_ text: String, width: Int = givenProbe.eval(6)) -> String {
    String(repeating: "*", count: width) + text
}

print(defaultProbe.use { pad("a") })
print(defaultProbe.use { pad("b") })
print(givenProbe.use { padGiven("c", width: 2) })
print(givenProbe.use { padGiven("d", width: 2) })

let allProbes = [defaultProbe, givenProbe]
for probe in allProbes { probe.report() }
tally(allProbes)
```

```
3
1 3
2
******a
******b
**c
**d
varsayilan-ifade: 2 degerlendirme, 2 kullanim -> her-kullanimda
verilen-iken-varsayilan: 0 degerlendirme, 2 kullanim -> hic
hemen: 0
ilk-kullanimda: 0
her-kullanimda: 1
hic: 1
```

`move` çağrısı 3 verdi, `bounds` demeti `1 3` olarak okundu ve iki `increment` çağrısı `total` değişkenini 2 yaptı. Ölçümün asıl satırları sondadır. `varsayilan-ifade`, iki çağrıda iki kez değerlendirildi: varsayılan ifade fonksiyon tanımında bir kez hesaplanıp saklanmaz, her çağrıda yeniden çalışır ve sonucu `her-kullanimda` olur. `verilen-iken-varsayilan` ise iki çağrıda sıfır değerlendirme gösterdi, çünkü çağıran genişliği kendisi verdi ve varsayılan ifadeye hiç uğranmadı: `hic`.

**DT46.** Varsayılan argüman ifadesi `@MainActor` bir fonksiyonda ve yalnız sabit bir tam sayı için ölçüldü; başka tiplerde ya da `nonisolated` bağlamda aynı sonucun çıkması bu dersin iddiası değildir.

**DT47.** Varsayılan ifadenin çağıranın bağlamında mı çağrılanın bağlamında mı çalıştığı bu derste ayırt edilmiyor; ölçülen şey kaç kez ve ne zaman değerlendirildiğidir.

#### Değişken Sayıda Parametre

Bir parametrenin tipinden sonra üç nokta yazarsan fonksiyon o noktada istediğin kadar argüman alır: `func sumAll(of numbers: Int...) -> Int`. Fonksiyon gövdesinde `numbers` sıradan bir `[Int]` dizisidir. Çağrıda argümanları virgülle yazarsın; hiç argüman vermeye de izin verilir ve bu durumda dizi boş gelir. Bir fonksiyonda birden çok değişken sayıda parametre olabilir; ilkinden sonra gelen parametrenin argüman etiketi taşıması gerekir.

Argümanlar çağrıdan önce değerlendirilir; üç argüman üç ifade demektir. Aşağıdaki blok her argümanını bir `Probe`'dan geçiren iki çağrı yapar ve bir de argümansız çağrı ekler.

```swift
// Variadic.swift — degisken sayida parametre
func sumAll(of numbers: Int...) -> Int {
    numbers.reduce(0, +)
}
let variadicProbe = Probe("degisken-sayi")
print(variadicProbe.use {
    sumAll(of: variadicProbe.eval(1), variadicProbe.eval(2), variadicProbe.eval(3))
})
print(variadicProbe.use {
    sumAll(of: variadicProbe.eval(4), variadicProbe.eval(5))
})
print(sumAll())
variadicProbe.report()
```

```
6
9
0
degisken-sayi: 5 degerlendirme, 2 kullanim -> her-kullanimda
```

Üç sayının toplamı 6, iki sayının toplamı 9, argümansız çağrı 0 verdi. `Probe` beş değerlendirme gösterdi: ilk çağrıda üç, ikincisinde iki. Her argüman kendi ifadesidir ve çağrıdan önce hesaplanır; fonksiyon gövdesi dizi olarak hazır alır. Bu yüzden an `her-kullanimda` okunur.

Değişken sayıda parametre çağıran tarafı sadeleştirir, çünkü bir dizi kurmak gerekmez. Argümanların sayısı çağrıya göre değişir; ölçüm bu değişimin değerlendirme sayısını da değiştirdiğini gösteriyor.

**DT48.** Değişken sayıda parametre `Int` ile ölçüldü; Swift'te bir diziyi değişken parametreye yaymanın yolu yoktur; dizi almak gerekiyorsa parametre `[Int]` olarak tanımlanır.

#### İç İçe Fonksiyon Çevresini Yakalar

Bir fonksiyonun içinde başka bir fonksiyon tanımlayabilirsin. İç fonksiyon, dış fonksiyonun yerel değişkenlerini ve parametrelerini görür ve onları yakalar. Dış fonksiyon iç fonksiyonu döndürürse, yakalanan değişkenler dış fonksiyon bittikten sonra da yaşamaya devam eder. Bu, her çağrıda kendi durumunu taşıyan bir sayaç yapmanın yoludur.

Aşağıdaki blok `makeCounter` adlı bir fonksiyon yazar. Fonksiyon bir başlangıç değeri alır, içindeki `advance` adlı iç fonksiyon her çağrıda sayıyı bir artırıp bir `Probe`'dan geçirir ve iç fonksiyonu döndürür. İki bağımsız sayaç kurulur: ilki iki kez, ikincisi bir kez çağrılır.

```swift
// Counter.swift — ic fonksiyon durum tasir
func makeCounter(start: Int, probe: Probe) -> () -> Int {
    var current = start
    func advance() -> Int {
        current += 1
        return probe.eval(current)
    }
    return advance
}
let nestedProbe = Probe("ic-fonksiyon")
let first = makeCounter(start: 10, probe: nestedProbe)
let second = makeCounter(start: 100, probe: nestedProbe)
print(nestedProbe.use { first() })
print(nestedProbe.use { first() })
print(nestedProbe.use { second() })
nestedProbe.report()
```

```
11
12
101
ic-fonksiyon: 3 degerlendirme, 3 kullanim -> her-kullanimda
```

İlk sayaç 11 ve 12, ikinci sayaç 101 verdi. İki sayaç aynı fonksiyondan çıktığı hâlde ayrı bir `current` yakaladı; biri ilerlerken öbürü etkilenmedi. Üç çağrı üç değerlendirme getirdi ve an `her-kullanimda` olur. `makeCounter` bittikten sonra bile `current` yaşıyor, çünkü iç fonksiyon onu tutuyor.

Bu, bir sonraki dersin konusunu hazırlar: iç fonksiyon, kapanışın adı konmuş hâlidir. Yakalama kuralları aynıdır.

**DT49.** İç fonksiyon bir `Int` durumu yakaladı; yakalanan değerin sınıf örneği olduğu durumlar bu dersin dışındadır.

#### inout Geri Yazar

`inout` parametre, çağıranın değişkenini fonksiyonun değiştirmesine izin verir. Mekanizma "kopyala, değiştir, geri yaz" biçimindedir: fonksiyon değişkenin bir kopyasını alır, gövdede onu istediği kadar değiştirir ve fonksiyon bitince sonucu değişkene tek seferde geri yazar. Değişkenin gözlemcisi varsa (`didSet`), gözlemci gövdedeki değişikliklerin her birinde değil, geri yazma anında bir kez çalışır. Doğrudan atama ise her satırda gözlemciyi çalıştırır.

Aşağıdaki blok aynı işi iki yolla yapar: biri `inout` parametreli bir fonksiyonla iki kez artırır, öbürü iki artırmayı doğrudan yazar. İki değişkenin de bir `didSet` gözlemcisi var ve her gözlemci kendi `Probe`'una yazar. İki yol da iki tur çalıştırılır.

```swift
// Inout.swift — inout geri yazma
func addTwice(_ value: inout Int) {
    value += 1
    value += 1
}
let viaCallProbe = Probe("inout-gozlemci")
let directProbe = Probe("dogrudan-gozlemci")
var viaCall = 0 { didSet { _ = viaCallProbe.eval(viaCall) } }
var direct = 0 { didSet { _ = directProbe.eval(direct) } }
_ = viaCallProbe.use { addTwice(&viaCall) }
_ = viaCallProbe.use { addTwice(&viaCall) }
_ = directProbe.use { direct += 1; direct += 1 }
_ = directProbe.use { direct += 1; direct += 1 }
print(viaCall, direct)
viaCallProbe.report()
directProbe.report()
```

```
4 4
inout-gozlemci: 2 degerlendirme, 2 kullanim -> her-kullanimda
dogrudan-gozlemci: 4 degerlendirme, 2 kullanim -> her-kullanimda
```

İki değişken de 4 oldu, ama gözlemci sayıları ayrıştı: `inout` yolu iki değerlendirme, doğrudan yol dört değerlendirme gösterdi. `addTwice` gövdede iki kez artırdı; gözlemci geri yazma anında, çağrı başına bir kez çalıştı. Doğrudan yolda her `+=` kendi gözlemcisini tetikledi. İki satır da `her-kullanimda` okunur, ama sayıları fark eder.

Bu fark `inout` ile gözlemci bir arada olduğunda dikkat gerektirir: gözlemcinin görmediği ara değerler olabilir. Ara durumları görmek istiyorsan doğrudan yaz, yalnız sonucu istiyorsan `inout` yeterlidir.

**DT50.** Gözlemci sayısı üst düzey bir değişkenle ölçüldü; sınıf özelliklerinde ve yapı özelliklerinde `inout` geri yazmasının aynı sayıyı verdiği bu dersin dışındadır.

#### Fonksiyon Tipi ve Değer Olarak Fonksiyon

Her fonksiyonun bir tipi vardır: parametre tiplerinin ve dönüş tipinin birleşimi. `func triple(_ n: Int) -> Int` fonksiyonunun tipi `(Int) -> Int` yazılır. Bu tip, sıradan bir tip gibi kullanılır: bir sabite atayabilir, bir parametre tipi olarak yazabilir ya da başka bir fonksiyondan döndürebilirsin. Fonksiyonu bir sabite atamak onu çağırmaz; yalnız onu bir değer olarak taşır. Çağrı, ancak parantezle yapılır.

Aşağıdaki blok `triple` fonksiyonunu bir sabite atar, tipini yazdırır ve onu `apply` adlı başka bir fonksiyona argüman olarak geçirir. `apply` verilen fonksiyonu bir değere uygular. Uygulanacak değer bir `Probe`'dan gelir ve `apply` iki kez çağrılır.

```swift
// FunctionType.swift — fonksiyon tipi
func triple(_ n: Int) -> Int { n * 3 }
func apply(_ transform: (Int) -> Int, to value: Int) -> Int {
    transform(value)
}
let typeProbe = Probe("fonksiyon-tipi")
let transform = triple
print(type(of: transform))
print(typeProbe.use { apply(transform, to: typeProbe.eval(4)) })
print(typeProbe.use { apply(transform, to: typeProbe.eval(5)) })
typeProbe.report()
```

```
@Sendable (Int) -> Int
12
15
fonksiyon-tipi: 2 degerlendirme, 2 kullanim -> her-kullanimda
```

`type(of: transform)` `@Sendable (Int) -> Int` yazdırdı: tip, parametre ve dönüş tipine ek olarak bir `@Sendable` niteliği taşıyor, çünkü üst düzey fonksiyonlar eşzamanlı ortamlarda güvenle paylaşılabilir. Okunuşta `(Int) -> Int` kısmı belirleyicidir. `apply` iki çağrıda `12` ve `15` verdi: `4 * 3` ve `5 * 3`. `Probe` iki değerlendirme gösterdi; her çağrıda argüman, `apply` çalışmadan önce bir kez hesaplandı. `transform` sabitinin kendisi bir değerlendirme sayılmadı, çünkü fonksiyonu bir sabite atamak onu çalıştırmaz.

Fonksiyonu değer olarak taşıdığında çağırma anı sende kalır: onu ne zaman, kaç kez ve hangi argümanla çağıracağın, çağrı yerinde belli olur.

Fonksiyon tipini bir kez okumayı öğrendiğinde, standart kütüphanedeki imzalar da okunur hâle gelir. Bir dizinin `map` yöntemi bir `(Element) -> T` fonksiyonu bekler; ona adlı bir fonksiyon da verebilirsin, bir kapanış da. Sonraki derste göreceğin kapanış, adı olmayan bir fonksiyon değeridir ve aynı tip kurallarına uyar.

Tipin başındaki `@Sendable` niteliği şimdilik dikkate alınmayabilir; onu sonraki konularda eşzamanlılıkla birlikte göreceksin. Önemli olan, fonksiyonun bir değer olarak taşındığını ve çağrı parantezi gelmedikçe çalışmadığını bilmektir. Aynı fonksiyonu iki farklı değişkene atamak iki ayrı kopya üretmez; ikisi de aynı fonksiyona işaret eder.

Çağrıdan önce hesaplanan şey argümandır, fonksiyon değeri değil; ölçümün iki değerlendirmesi bunu doğruluyor.

**DT51.** Fonksiyon tipi tek parametreli bir fonksiyonla ölçüldü; birden çok parametreli ve fonksiyon döndüren fonksiyonlar bu dersin dışındadır.

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

Varsayılan değer çağıran vermediğinde devreye girer. Çağıran bir argüman verdiğinde ise iş tersine döner: o ifade çağrıdan önce değerlendirilir, fonksiyon gövdesi onu okusun ya da okumasın. Bu, dil açısından sıradan bir kuraldır, ama ölçüm bunu göstermeden önce bir sezgi bırakır: okunmayan bir parametrenin argümanı hiç değerlendirilmez gibi görünür. Aşağıdaki gövde parametresini hiç okumaz, ve `marked` yardımcı fonksiyonu argümanın ne zaman çalıştığını `print` ile gösterir.

```swift
// main.swift — argüman gövdeden once degerlendirilir
func marked(_ n: Int) -> Int {
    print("arguman \(n)")
    return n
}

func ignore(_ value: Int) -> String {
    print("govde calisti")
    return "okumadi"
}

let argumentProbe = Probe("arguman-ifadesi")
_ = argumentProbe.use { ignore(argumentProbe.eval(marked(7))) }
_ = argumentProbe.use { ignore(argumentProbe.eval(marked(8))) }

let allProbes = [argumentProbe]
for probe in allProbes { probe.report() }
tally(allProbes)
```

```
arguman 7
govde calisti
arguman 8
govde calisti
arguman-ifadesi: 2 degerlendirme, 2 kullanim -> her-kullanimda
hemen: 0
ilk-kullanimda: 0
her-kullanimda: 1
hic: 0
```

`arguman 7` satırı `govde calisti` satırından önce basıldı, hem de gövde `value` parametresini hiç kullanmadığı hâlde. `arguman-ifadesi` iki çağrıda iki kez değerlendirildi: argüman, fonksiyon çağrılmadan önce her seferinde hesaplanır. Varsayılan ifade ile arasındaki fark şudur: varsayılan ifade yalnız argüman verilmediğinde çalışır, verilen argüman ise her zaman çalışır.

**DT52.** `ignore` gövdesinin parametreyi okumaması, derleyicinin argüman ifadesini eleyip elemediğini ölçmez; çıktıda `arguman 7` satırı bu ölçümün tek kanıtıdır ve yalnız bu yapı için geçerlidir.

#### Özet

- Argüman etiketi çağrıyı okunur kılar; `_` etiketi kaldırır, `= değer` varsayılan argüman tanımlar.
- Demet birden çok değer döndürür; `inout` parametre çağıranın `var` değişkenini `&` ile değiştirir.
- Varsayılan argüman ifadesi her çağrıda, argüman verilmediğinde değerlendirilir.
- Argüman verilirse varsayılan ifade hiç değerlendirilmez; verilen argüman gövde okumasa da çağrıdan önce değerlendirilir.
- Bu dersin anı: **her kullanımda** (varsayılan argüman ifadesi).

#### Sonraki Adım

Bir fonksiyon, parametresi olarak başka bir fonksiyon alabilir ve bunu isimsiz bir ifade olarak yazabilirsin. Bu ifadenin gövdesi ne zaman çalışır ve çevresindeki değişkenleri hangi anda görür? Sıradaki ders kapanışlara bakıyor.

### Kapanışlar

Source: https://academia.sh/tr/kurslar/swift/kapanislar

Kapanış sözdizimini, `$0` kısaltmasını, sondaki kapanışı ve `sorted(by:)` çağrısını tanıtır; yakalama listesinin kapanış oluşturulurken bir kez değerlendirildiğini ölçer.

Fonksiyonlar adlandırılmış davranıştır. Kapanış (closure) aynı davranışı adsız yazar ve bir değişkene koyabilir ya da başka bir fonksiyona verebilirsin. Bu dersin sorusu iki parçalı: kapanışın gövdesi ne zaman çalışır, ve kapanış çevresindeki değişkenleri hangi anda görür?

#### Kapanış Yazmak

En uzun biçim, parametre ve dönüş tipini süslü parantezin içine yazar: `{ (a: Int, b: Int) -> Bool in a < b }`. `in` anahtar sözcüğü imzayı gövdeden ayırır. Tip bağlamdan çıkarılabildiğinde imza düşer ve parametrelere sıra numarasıyla erişilir: `$0` ilk, `$1` ikinci argümandır. Gövde tek bir ifadeyse `return` yazılmaz, ifadenin değeri örtük olarak döner.

Bir fonksiyonun son parametresi kapanışsa, kapanış parantezin dışına yazılabilir. Buna sondaki kapanış (trailing closure) denir. Parantez içinde başka argüman kalmadıysa parantezin kendisi de düşer: `numbers.sorted { $0 > $1 }`. `sorted(by:)` bir diziyi verdiğin karşılaştırma kapanışına göre sıralar.

```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)")
    }
}
```

Ölçüm bloğu önce bu biçimleri çalıştırır: uzun kapanış, `$0` ile kısa kapanış, `map` ve kendi yazdığımız `apply`. Ardından iki kapanış kurar. `scale` gövdesinde bir `Probe` taşır ve iki kez çağrılır; `unused` aynı biçimde kurulur ama hiç çağrılmaz.

```swift
// main.swift — kapanis bicimleri ve govde ani
let numbers = [3, 1, 2]
print(numbers.sorted(by: { (a: Int, b: Int) -> Bool in a < b }))
print(numbers.sorted { $0 > $1 })
print(numbers.map { $0 * 2 })

func apply(_ n: Int, using transform: (Int) -> Int) -> Int { transform(n) }
print(apply(4) { $0 + 1 })

let bodyProbe = Probe("kapanis-govdesi")
let unusedProbe = Probe("cagrilmayan-govde")

let scale = { (n: Int) -> Int in bodyProbe.eval(n * 2) }
let unused = { (n: Int) -> Int in unusedProbe.eval(n) }

print(bodyProbe.use { scale(1) })
print(bodyProbe.use { scale(2) })
_ = unusedProbe.use { unused }
_ = unusedProbe.use { unused }

let allProbes = [bodyProbe, unusedProbe]
for probe in allProbes { probe.report() }
tally(allProbes)
```

```
[1, 2, 3]
[3, 2, 1]
[6, 2, 4]
5
2
4
kapanis-govdesi: 2 degerlendirme, 2 kullanim -> her-kullanimda
cagrilmayan-govde: 0 degerlendirme, 2 kullanim -> hic
hemen: 0
ilk-kullanimda: 0
her-kullanimda: 1
hic: 1
```

`sorted(by:)` sonucu küçükten büyüğe, `$0 > $1` ile büyükten küçüğe verdi. `map` her elemanı ikiye katladı ve `apply(4)` sondaki kapanışla 5 döndü. Ölçüm satırları kapanışın gövdesine bakar: `kapanis-govdesi` iki çağrıda iki kez değerlendirildi, yani gövde her çağrıda yeniden çalışır ve an `her-kullanimda` olur. `cagrilmayan-govde` ise iki kullanımda sıfır değerlendirme gösterdi: kapanışı kurmak gövdeyi çalıştırmaz, gövde yalnız çağrı olduğunda çalışır.

**DT53.** `sorted(by:)` karşılaştırma kapanışının kaç kez çağrıldığı bu derste sayılmadı; sayı sıralama algoritmasına bağlıdır ve ders yalnız sonucu gösterir.

**DT54.** Kapanış gövdesi yalnız `Int` döndüren, yan etkisi olmayan basit bir ifadeyle ölçüldü; kaçan (escaping) kapanışlar bu dersin dışındadır.

#### Kaçan Kapanış: Saklanan İş

Bir kapanışı fonksiyona verirsen ve fonksiyon kapanışı kendi bitişinden sonra da saklarsa, kapanışın tipi `@escaping` ile işaretlenmelidir. Bu işaret, kapanışın çağrıdan kaçabileceğini söyler. Kaçmayan kapanış (varsayılan) çağrı bitince ortadan kalkar. Kaçan kapanış ise bir diziye eklenebilir, bir özelliğe atanabilir ve sonra çağrılabilir. Kapanışı kuran satır işi yapmaz; işi, kapanış çağrıldığında yapılır.

Aşağıdaki blok bir kuyruk sınıfı yazar. `add` kapanışı saklar, `runAll` saklananların hepsini çağırır. Kapanışın gövdesi bir `Probe`'a yazar. Kapanışlar kuyruğa eklendikten sonra ve çalıştırıldıktan sonra değerlendirme sayısı yazdırılır.

```swift
// Queue.swift — saklanan kapanis
final class Queue {
    var jobs: [() -> Int] = []
    func add(_ job: @escaping () -> Int) {
        jobs.append(job)
    }
    func runAll() {
        for job in jobs { _ = job() }
    }
}
let queueProbe = Probe("saklanan-is")
let queue = Queue()
queue.add { queueProbe.eval(1) }
queue.add { queueProbe.eval(2) }
print(queueProbe.evaluations)
_ = queueProbe.use { queue.runAll() }
_ = queueProbe.use { queue.runAll() }
print(queueProbe.evaluations)
queueProbe.report()
```

```
0
4
saklanan-is: 4 degerlendirme, 2 kullanim -> her-kullanimda
```

İki kapanış kuyruğa eklendiğinde sayaç 0'dı: ekleme gövdeyi çalıştırmadı. İki `runAll` çağrısından sonra sayaç 4 oldu, çünkü her çağrıda iki kapanış çalıştı. Kapanışlar saklandı ve her çağrıldığında gövdeyi yeniden değerlendirdi; bu yüzden an `her-kullanimda` okunur.

Kaçan kapanış, işi ertelemenin yoludur. Gövdenin ne zaman çalışacağını kapanışı kuran satırdan değil, onu çağıran satırdan okursun.

**DT55.** Saklanan kapanışlar bir sınıf içinde ölçüldü; kaçan kapanışta `self` yakalamanın sonuçları bu dersin dışındadır.

#### Kısaltılmış Argüman Adları

Kapanış parametrelerine `$0`, `$1` gibi adlar verebilirsin. Bu, tip bağlamdan çıkarıldığında parametre listesini yazmaktan kurtarır. Gövde tek bir ifadeyse örtük dönüş de çalışır. Standart kütüphane işleçleri de bir fonksiyon gibi geçilebilir: `numbers.sorted(by: >)` yazmak `numbers.sorted { $0 > $1 }` ile aynı işi yapar. `reduce` iki parametreli bir kapanış alır ve bu parametreler `$0` ve `$1` olur.

Aşağıdaki blok bir dizi üzerinde üç kısaltılmış biçimi çalıştırır. `map` her elemanı bir `Probe`'dan geçirir ve ikinci bir `map` çağrısı bu kez dört elemana uygulanır.

```swift
// Shorthand.swift — kisaltilmis argumanlar
let mapProbe = Probe("map-govdesi")
let numbers = [3, 1, 2]
print(numbers.sorted(by: >))
print(numbers.reduce(0) { $0 + $1 })
print(mapProbe.use { numbers.map { mapProbe.eval($0 * 10) } })
print(mapProbe.use { (numbers + [4]).map { mapProbe.eval($0 * 10) } })
mapProbe.report()
```

```
[3, 2, 1]
6
[30, 10, 20]
[30, 10, 20, 40]
map-govdesi: 7 degerlendirme, 2 kullanim -> her-kullanimda
```

`sorted(by: >)` `[3, 2, 1]` verdi. `reduce` toplamı 6 olarak hesapladı. İki `map` çağrısı `[30, 10, 20]` ve `[30, 10, 20, 40]` dizilerini üretti. `Probe` yedi değerlendirme gösterdi: ilk çağrıda üç, ikincisinde dört. Kapanış gövdesi her eleman için bir kez çalıştı. Bu yüzden an `her-kullanimda` okunur.

Kısaltılmış adlar gövde kısa olduğunda okunur; gövde uzadığında adlandırılmış parametre anlamı korur. `$0` ve `$1` yanında işleci doğrudan geçmek en kısa yazımdır ama yalnız işlecin imzası kapanışın imzasıyla eşleştiğinde çalışır.

**DT56.** Kısaltılmış adlar `Int` dizisiyle ölçüldü; `$2` ve sonrası ile iç içe kapanışlarda `$0` anlamı bu dersin dışındadır.

#### Kapanış Yakaladığı Değişkeni Değiştirebilir

Yakalanan değişken bir `var` ise kapanış onu yalnız okumakla kalmaz, değiştirebilir de. Kapanış değişkenin bir kopyasını değil, kendisini tutar. Aynı değişkeni birden çok kapanış yakalarsa, bir kapanışın yaptığı değişiklik diğerinin gördüğü değeri değiştirir. Bu yüzden yakalanan `var`'lar paylaşılan durum gibi davranır ve değişkenin ömrü, onu yakalayan kapanışlarla birlikte uzar.

Aşağıdaki blok bir `hits` sayacını iki kapanışla yakalar. `record` sayacı bir artırır ve değeri bir `Probe`'dan geçirir; `skip` sayacı doğrudan iki artırır. Kapanışlar sırayla `record`, `skip`, `record` biçiminde çağrılır.

```swift
// Shared.swift — ortak yakalanan degisken
let sharedProbe = Probe("ortak-sayac")
var hits = 0
let record = {
    hits += 1
    return sharedProbe.eval(hits)
}
let skip = { hits += 2 }
print(sharedProbe.use { record() })
skip()
print(sharedProbe.use { record() })
print(hits)
sharedProbe.report()
```

```
1
4
4
ortak-sayac: 2 degerlendirme, 2 kullanim -> her-kullanimda
```

`record` ilk çağrıda 1, `skip` sayacı 3 yaptıktan sonra ikinci çağrıda 4 döndürdü; son okuma `hits` değerinin 4 olduğunu doğruladı. İki kapanış aynı değişkeni gördü: `record`, `skip`'in yaptığı değişikliği ikinci çağrıda gördü. İki çağrı iki değerlendirme getirdi ve an `her-kullanimda` olur.

Paylaşılan durum esneklik sağlar, ama hangi kapanışın değişkeni ne zaman değiştirdiğini izlemeyi zorlaştırır. Durum küçük ve yakın olduğunda sorun olmaz; büyüdükçe durumu bir tipin içine almak daha okunur olur.

**DT57.** Paylaşılan durum bir `Int` üzerinde ölçüldü; eşzamanlı çalışan kapanışların aynı değişkene erişmesi bu dersin dışındadır.

#### Sondaki Kapanış Söz Dizimi

Bir fonksiyonun son parametresi kapanışsa, kapanışı parantezlerin dışına yazabilirsin. `perform(times: 2) { ... }` ile `perform(times: 2, action: { ... })` aynı çağrıdır. Fonksiyonun tek argümanı kapanışsa, parantezler tümüyle kalkabilir. Bu söz dizimi yalnız yazımı kısaltır; kapanışın ne zaman çalıştığı değişmez. Kapanış, fonksiyon onu çağırdığında çalışır ve kaç kez çağırırsa o kadar kez çalışır.

Aşağıdaki blok bir fonksiyon yazar: `perform` verilen kapanışı istenen sayıda çağırır. Kapanış gövdesi bir `Probe`'a yazar. Fonksiyon önce iki, sonra üç tekrarla çağrılır.

```swift
// Trailing.swift — sondaki kapanis
func perform(times: Int, action: () -> Void) {
    for _ in 0..<times { action() }
}
let trailingProbe = Probe("sondaki-kapanis")
trailingProbe.use { perform(times: 2) { _ = trailingProbe.eval(1) } }
trailingProbe.use { perform(times: 3) { _ = trailingProbe.eval(1) } }
print(trailingProbe.evaluations)
trailingProbe.report()
```

```
5
sondaki-kapanis: 5 degerlendirme, 2 kullanim -> her-kullanimda
```

`perform(times: 2)` iki, `perform(times: 3)` üç kez çalıştırdı; toplam beş değerlendirme çıktı. Kapanışı parantezin dışına yazmak çalışma sayısını değiştirmedi: `times` değeri, kapanışın kaç kez çağrılacağını belirledi. Her çağrıda gövde yeniden değerlendirildi ve an `her-kullanimda` okunur.

Bu söz dizimi yalnız okunuşa yardım eder. Kapanış parametreler arasında son sıradaysa, çağrı bir kontrol akışı ifadesine benzer: `perform(times: 3) { ... }` bir döngü gibi okunur. Ama arkasındaki kural aynıdır: kapanış bir değerdir ve fonksiyon onu istediği kadar çağırır.

**DT58.** Sondaki kapanış dönüş değeri olmayan bir kapanışla ölçüldü; birden çok sondaki kapanışın söz dizimi bu dersin dışındadır.

#### Yakalama Listesi Değeri Dondurur

Bir kapanış, çevresindeki bir `var` değişkeni yakaladığında değişkenin kendisini tutar; sonradan değişkende yapılan değişiklikler kapanışa yansır. Bunu değiştirmek istersen kapanışın başına bir yakalama listesi yazarsın: `{ [snapshot = count] in snapshot }`. Liste, kapanış oluşturulduğu anda `count` değerinin bir kopyasını alır ve kapanış artık onu kullanır. Sonradan `count` değişse bile kapanışın gördüğü sayı aynı kalır.

Aşağıdaki blok aynı değişkeni iki kapanışla yakalar: biri liste kullanmaz, biri kullanır. Kapanışlar oluşturulduktan sonra değişken değiştirilir ve ikisi de çağrılır. Okunan değer bir `Probe` üzerinden geçer.

```swift
// CaptureList.swift — yakalama listesi
let listProbe = Probe("yakalama-listesi")
var count = 1
let live = { listProbe.eval(count) }
let frozen = { [snapshot = count] in listProbe.eval(snapshot) }
count = 5
print(listProbe.use { live() }, listProbe.use { frozen() })
listProbe.report()
```

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

`live` kapanışı `5`, `frozen` kapanışı `1` verdi. `live` değişkenin kendisini tuttuğu için `count = 5` atamasından sonra yeni değeri gördü. `frozen` ise oluşturulduğu anda `count` değerinin bir kopyasını `snapshot` adıyla aldı; sonradan değişkeni değiştirmek ona ulaşmadı. İki çağrı iki değerlendirme getirdi ve an `her-kullanimda` olur.

Yakalama listesi, kapanışın hangi değeri göreceğini açıkça yazmanın yoludur. Bir değerin kapanış oluştuğu andaki hâliyle kalmasını istiyorsan listeye yazarsın; değişkenin sonradan alacağı değerleri görmesini istiyorsan listeyi hiç kullanmazsın.

Yakalama listesine birden çok kalem de yazabilirsin; her kalem, kapanış oluştuğu anda kendi kopyasını alır.

**DT59.** Yakalama listesi bir `Int` değişkenle ölçüldü; sınıf örneklerini zayıf yakalama bu dersin dışındadır ve sonraki konuda ele alınır.

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

Kapanış çevresindeki bir değişkeni kullanıyorsa onu yakalar (capture). Listesiz yakalamada kapanış değişkenin kendisine bağlanır: değişken sonradan değişirse kapanış her çağrıda yeni değeri görür. Yakalama listesi (capture list) ise köşeli parantez içinde yazılır ve kapanış oluşturulurken bir değer alır: `{ [snapshot = level] in snapshot }`. Listedeki ifade kapanış kurulurken bir kez değerlendirilir, çağrılarda yeniden değerlendirilmez. Aşağıdaki ölçüm iki kapanışı aynı değişkenle kurar, değişkeni değiştirir ve ikisini de iki kez çağırır.

```swift
// main.swift — yakalama listesi
let captureProbe = Probe("yakalama-listesi")

var level = 1
let plain = { level }
let listed = { [snapshot = captureProbe.eval(level)] in snapshot }
level = 5

print(captureProbe.use { plain() }, captureProbe.use { listed() })
print(captureProbe.use { plain() }, captureProbe.use { listed() })

let allProbes = [captureProbe]
for probe in allProbes { probe.report() }
tally(allProbes)
```

```
5 1
5 1
yakalama-listesi: 1 degerlendirme, 4 kullanim -> hemen
hemen: 1
ilk-kullanimda: 0
her-kullanimda: 0
hic: 0
```

`plain` kapanışı `level` değişkenine bağlı kaldığı için 5 gördü; `listed` kapanışı kurulduğu andaki 1 değerini taşıdı. Yakalama listesindeki ifade, kapanış kurulurken ve dört kullanımdan önce bir kez değerlendirildi: `yakalama-listesi` satırı `hemen` verir. Gövdenin her çağrıda çalışması ile yakalama listesinin bir kez çalışması aynı kapanışın iki ayrı anıdır.

**DT60.** `level` bir `var`'dır ve ölçüm yalnız değer tipi bir değişkenin yakalanmasını gösterir; sınıf örneklerinin yakalanması bu derste ölçülmedi.

**DT61.** Yakalama listesinin kapanış kurulurken bir kez çalışması tek bir kapanışla ölçüldü; aynı kapanışı birden çok yerde kuran kodlarda her kuruluşun ayrı bir değerlendirme olduğu bu dersin dışındadır.

#### Özet

- Kapanış, adsız bir davranıştır; `$0` ve `$1` argümanlara sıra numarasıyla erişir, tek ifadelik gövde `return` gerektirmez.
- Son parametre kapanışsa, kapanış parantezin dışına yazılabilir (sondaki kapanış).
- Kapanış gövdesi her çağrıda çalışır; hiç çağrılmayan kapanışın gövdesi hiç çalışmaz.
- Yakalama listesi kapanış kurulurken bir kez değerlendirilir; listesiz yakalanan değişkenin sonradan değişmesi çağrıda görünür.
- Bu dersin anı: **her kullanımda** (kapanış gövdesi).

#### Sonraki Adım

Bu konudaki örnekler `values.min() ?? 0` gibi bir ifade kullandı: `min()` boş bir dizi için değer döndüremez. Swift bu durumu bir değerin yokluğu olarak tipte taşır. Bir sonraki konunun ilk dersi, İsteğe Bağlı Değerler, bu fikri tanıtıyor.

## Tip Sistemi

Yokluğun tipte ifadesi olarak isteğe bağlı değerler ve güvenli açma, yapılar ve sınıflarla değer ve referans semantiği, numaralandırmalar, saklanan, hesaplanan ve gözlemleyen özellikler, protokoller, uzantılar, genellikler, hata yönetimi ve erişim denetimi.

### İsteğe Bağlı Değerler

Source: https://academia.sh/tr/kurslar/swift/istege-bagli-degerler

Yokluğun tipte ifade edilmesinin, nil üzerinde dönüştürücü kapanışın hiç çalışmamasının ve iç içe katmanlarda yokluğun nerede durduğunun ölçümü.

Önceki ders kapanışları ve yakalama listesini ölçtü. Bir kapanışın gövdesi yazıldığı yerde değil, çağrıldığı yerde koşar; hiç çağrılmayan bir kapanışın gövdesi hiç değerlendirilmez. Bu ders aynı soruyu, bir kapanışı parametre olarak alan ve bazen o kapanışı hiç çağırmayan bir yöntem üzerinden sorar. O yöntem, tip sisteminin en çok kullanılan parçasına aittir: yokluğu bir değer gibi taşıyan tipe.

Çoğu dil yokluğu özel bir değerle, örneğin bir boş gösterici ya da sihirli bir sayıyla ifade eder ve o değerin her yerde kontrol edilmesini programcıya bırakır. Swift yokluğu tipin kendisine yazar. **İsteğe bağlı değer (optional)**, ya bir değer taşıyan ya da hiçbir şey taşımayan bir kutudur ve `Int?` biçiminde yazılır. `Int` her zaman bir tamsayıdır; `Int?` ise tamsayı olabilir, olmayabilir. Derleyici bu iki tipi birbirinin yerine kullandırmaz, dolayısıyla yokluğun kontrolü kendiliğinden unutulamaz; onu atlamak için zorla açmayı bilerek yazmak gerekir.

Kursun ölçü ekseni bu derste de değişmez: **bu ifade ne zaman değerlendiriliyor?** Dört an vardır: hemen, ilk kullanımda, her kullanımda, hiç. Kuralı sayan `Probe` sınıfı aşağıda olduğu gibi verilir. Bu ders eksenin dördüncü anına, yani hiç değerlendirilmeyen ifadeye odaklanır.

#### Çekirdek

Her ders aynı çekirdeği kullanır. `Probe` bir ifadenin kaç kez değerlendirildiğini (`eval`) ve kaç kez kullanıldığını (`use`) sayar; `moment` özelliği bu iki sayıdan anı çıkarır. Açılış dersindeki bağlayıcı ölçümde sekiz ifadenin dağılımı 2/1/3/2 idi: ikisi hemen, biri ilk kullanımda, üçü her kullanımda, ikisi hiç. Bu derste yalnız kendi kesitini ölçeriz.

```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)")
    }
}
```

#### Yokluk Bir Tip Olarak

Bir isteğe bağlı değerin içinde ne olduğuna bakmadan önce kendisine bakalım. `Int("42")` bir metni tamsayıya çevirir ve çevirme başarısız olabileceği için dönüş tipi `Int?` olur. Başarılı olursa kutu doludur, `Int("x")` gibi bir çağrıda kutu boştur ve değeri `nil`'dir.

```swift
// main.swift — istege bagli deger bir numaralandirmadir
func describe(_ value: Int?) -> String {
    switch value {
    case .some(let number):
        return "dolu \(number)"
    case .none:
        return "bos"
    }
}

let parsed = Int("42")
let broken = Int("x")
print(type(of: parsed))
print(describe(parsed))
print(describe(broken))
print(parsed == .some(42), broken == .none)
print(broken == nil)
```

```
Optional<Int>
dolu 42
bos
true true
true
```

İlk satır tipin adını verir: `Int?` yalnızca bir kısaltmadır, gerçek tip `Optional<Int>`'tir. `Optional` standart kitaplıkta tanımlı bir **numaralandırmadır (enumeration)** ve iki durumu vardır: bir değer taşıyan `.some` ve hiçbir şey taşımayan `.none`. Bu yüzden `describe` işlevi kutuyu bir `switch` ile açabilir; her durum için ayrı bir kol yazılır ve `.some` kolunda taşınan değer `number` adına bağlanır. Dördüncü satır `42` değerinin `.some(42)` ile, boş kutunun `.none` ile eşit olduğunu gösterir; beşinci satır `nil` yazımının `.none` için kısaltma olduğunu gösterir.

Aynı çıktıdan ikinci bir sonuç çıkar: `Int?` ile `Int` iki ayrı tiptir. Aşağıdaki parçalar derlenmez; her ikisi de önce geçici bir ölçülen blokta koşturuldu ve derleyici ikisini de reddetti. Yazımları burada yalnızca anlatım için durur.

```swift
let a: Int? = 5
let b = a + 1   // derlenmez: Int? ile Int toplanamaz
let c: Int = nil   // derlenmez: nil yalnizca istege bagli bir tipe atanabilir
```

Birinci satırdaki toplama, kutunun içindeki sayıyı değil kutunun kendisini görür ve derleyici kutunun açılmasını ister. İkinci satırdaki atama ise `Int` tipine yokluk yazmaya çalışır. Yokluğu taşıyabilecek tek yer, tipi `?` ile işaretlenmiş olan yerdir. Kutunun nasıl açılacağı bir sonraki dersin konusudur; bu derste kutuyu açmadan onunla çalışan iki yöntemi ölçeceğiz.

#### Yokluğun Kaynakları

İsteğe bağlı değerler programa yalnızca metin çevirmeyle girmez. Standart kitaplık, yokluğun olağan olduğu birçok yerde bir isteğe bağlı değer döndürür; ama her yerde değil: sınır dışındaki bir dizi indeksi `nil` vermez, programı durdurur. Bir sözlükte olmayan anahtarın değeri yoktur; boş bir dizinin ilk elemanı yoktur. Bu durumlarda özel bir hata değeri ya da bir istisna beklemek gerekmez: dönüş tipi zaten "belki" der.

```swift
// main.swift — standart kitaplik yoklugu istege bagli degerle bildirir
let ages = ["ada": 36, "alan": 41]
let names: [String] = []
print(ages["ada"], ages["grace"])
print(names.first, [3, 1, 2].first)
print(ages["ada"] == 36, ages["grace"] == nil)
```

```
Optional(36) nil
nil Optional(3)
true true
```

Birinci satırda var olan anahtarın değeri `Optional(36)` olarak gelir; yani `36` bile doğrudan bir `Int` değil, içinde `36` bulunan bir kutudur. Olmayan anahtar `nil` verir. İkinci satır aynı örüntüyü diziler için gösterir: boş dizinin `first` özelliği `nil`, dolu dizinin `first` özelliği kutulanmış bir `3` döndürür. Üçüncü satırda kutu, kutulanmamış bir sayıyla karşılaştırılır: `==` işleci, iki tarafın da `Int?` olmasını sağlayacak biçimde sağdaki `36`'yı otomatik olarak kutular. Bu yüzden `ages["ada"] == 36` derlenir; yukarıdaki `+` örneği ise kutunun içindeki değeri gerektirdiği için derlenmez.

Bu kaynaklar bir ayrımı görünür kılar: yokluğun nedeni isteğe bağlı değerin tipinde yazılı değildir. `ages["grace"]` boştur çünkü anahtar yoktur, `Int("x")` boştur çünkü metin sayı değildir, ama ikisi de aynı `nil`'dir. Neden bilgisine ihtiyaç duyulduğunda hata yönetimi devreye girer; o konu bu kursta daha sonra gelir. Yokluğun nedeni önemli olmadığında isteğe bağlı değer, en az yük getiren araçtır.

#### nil Üzerinde map

`Optional` iki durumlu bir kutu olduğuna göre, kutunun içindeki değeri dönüştüren bir yöntem de iki durumda farklı davranır. `map` bir **dönüştürücü kapanış (closure)** alır. Kutu doluysa kapanış içindeki değerle çağrılır ve sonuç ayrı bir kutuya konur; kutu boşsa kapanış çağrılmaz ve sonuç boş bir kutudur. Bunu ölçelim: iki kutu, biri dolu biri boş, her biri iki kez kullanılır ve dönüştürücü kapanışın gövdesi `eval` ile işaretlenir.

```swift
// main.swift — nil uzerinde map donusturucuyu cagirmaz
let filled = Probe("map-dolu")
let vacant = Probe("map-bos")
let present: Int? = Int("21")
let missing: Int? = Int("yirmi bir")

for _ in 1...2 {
    _ = filled.use { present.map { filled.eval($0 * 2) } }
    _ = vacant.use { missing.map { vacant.eval($0 * 2) } }
}
filled.report()
vacant.report()
```

```
map-dolu: 2 degerlendirme, 2 kullanim -> her-kullanimda
map-bos: 0 degerlendirme, 2 kullanim -> hic
```

Dolu kutuda `map` her çağrıldığında dönüştürücüyü yeniden koşturur: iki kullanım, iki değerlendirme, an **her kullanımda**. Sonucun bir yerde saklanmadığına dikkat edin; `use` içindeki ifade her seferinde baştan çalışır. Boş kutuda iki kullanım vardır ama değerlendirme yoktur, çünkü `map` kutunun boş olduğunu görür görmez dönüştürücüye uğramadan döner. Gövdede yazılı `filled.eval($0 * 2)` ya da `vacant.eval($0 * 2)` ifadesi kaynakta durur, fakat boş kutuda hiçbir zaman değerlendirilmez. Kural bu durumu **hiç** diye adlandırır.

Bu ölçüm, kapanışın gövdesinin ne zaman koştuğunu bir kez daha gösterir: kapanış yazıldığı yerde koşmaz, ve onu çağırması beklenen yöntem de onu her zaman çağırmaz. Hiç çağrılmayan bir dönüştürücünün içindeki bir hata, bir yan etki ya da pahalı bir hesap hiçbir zaman gerçekleşmez. Dönüştürücünün yan etkisine güvenen bir kod, boş kutuda o yan etkiyi kaçırır.

**TS1.** Bu ölçüm yalnız `map` için; `filter`, `compactMap` ya da `forEach` gibi başka yöntemlerin boş kutudaki davranışı denenmedi. Dönüştürücünün yan etkiyle değil saf bir hesapla yazıldığı durumlar da ayrıca ölçülmedi.

##### Zincirde İki map

Boş kutu bir `map` çağrısından boş çıkıyorsa, ardına eklenen ikinci `map` de aynı kutuyla karşılaşır. Zincirin ilk halkası hiçbir şey üretmediğinde sonraki halkalar üretecek bir değer bulamaz. Bunu iki dönüştürücüyle ölçelim; her dönüştürücünün gövdesi kendi `Probe`'una bağlıdır ve zincir her kullanımda baştan kurulur.

```swift
// main.swift — bos kutuda zincirdeki her map atlanir
let stageOne = Probe("zincir-birinci")
let stageTwo = Probe("zincir-ikinci")
let seed: Int? = Int("on")

for _ in 1...2 {
    _ = stageOne.use { seed.map { stageOne.eval($0 + 1) }.map { stageTwo.eval($0 * 2) } }
    _ = stageTwo.use { seed.map { stageOne.eval($0 + 1) }.map { stageTwo.eval($0 * 2) } }
}
stageOne.report()
stageTwo.report()
```

```
zincir-birinci: 0 degerlendirme, 2 kullanim -> hic
zincir-ikinci: 0 degerlendirme, 2 kullanim -> hic
```

İki dönüştürücü de hiç değerlendirilmedi. İkincinin hiç çalışmaması beklenebilirdi, çünkü kendi girdisi birincinin sonucudur; birincinin hiç çalışmaması ise kutunun en başta boş olmasından gelir. `seed` değeri `Int("on")` ile kurulduğu için `nil`'dir; ilk `map` boş kutuyu dönüştürmeden geçirir, ikinci `map` de aynı boş kutuyu alır. Uzun bir dönüştürücü zincirinde en baştaki bir yokluk bu yüzden sonraki halkaların hiçbirini koşturmaz.

**TS2.** Bu zincirde yalnız başlangıç değeri boştur; zincirin ortasındaki bir dönüştürücünün `nil` döndürdüğü durum bu ölçümde yok ve orada sonraki `map`in davranışı ayrıca ölçülmedi.

#### map ile flatMap

Dönüştürücünün kendisi de isteğe bağlı bir değer döndürebilir. Bir metni tamsayıya çeviren `Int($0)` tam böyle bir ifadedir: sonucu `Int?`'tir. `map` dönüştürücünün sonucunu olduğu gibi ayrı bir kutuya koyar, bu yüzden kutunun içine bir kutu girer. `flatMap` ise dönüştürücünün döndürdüğü kutuyu dışarıya düzleştirir. Üç girdiyle farkı görelim: bir geçerli metin, bir geçersiz metin ve bir boş kutu.

```swift
// main.swift — map ic ice katman uretir, flatMap duzlestirir
let inputs: [String?] = ["7", "x", nil]
for input in inputs {
    let nested = input.map { Int($0) }
    let flat = input.flatMap { Int($0) }
    print(nested, flat)
}
```

```
Optional(Optional(7)) Optional(7)
Optional(nil) nil
nil nil
```

Birinci satırda `map` iki katmanlı bir sonuç verir, `flatMap` tek katmanlı. İkinci satır asıl farkı gösterir: `"x"` geçersiz bir metindir ve `map` sonucu `Optional(nil)` olur, yani dış kutu dolu, iç kutu boştur; `flatMap` ise doğrudan `nil` verir. Üçüncü satırda girdinin kendisi boş olduğu için iki yöntem de dönüştürücüye hiç uğramaz ve `nil` döner.

İkinci satır sonraki ölçümün zeminidir. `Optional(nil)` ile `nil` birbirinden ayrı değerlerdir ve ikisi de yokluk anlatır; fakat biri yokluğu bir katman içeride, öbürü en dışta taşır. Hangi katmanda durduğu, bir sonraki yöntemin dönüştürücüyü çağırıp çağırmayacağını belirler.

**TS3.** Bu çıktı yalnız `Int` dönüştürmesi için; dönüştürücünün başka bir tipe çevirdiği ya da hata fırlattığı durumlar denenmedi. Çıktıdaki `Optional(...)` yazımı bu sürümün `print` biçimidir; ölçülen şey katman sayısıdır, biçimin kendisi değil.

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

Ders başında `map` için bir kural kurduk: boş kutuda dönüştürücü çalışmaz. Kural doğru, ama hangi kutunun boş sayıldığı bir katman sorusudur. `Int??` tipi iki katmanlı bir kutudur. Dış kutu doluyken içindeki kutu boş olabilir; bu durum `.some(nil)` diye yazılır. Aşağıda iki değer var: dış katmanı dolu olan ama içi boş bir değer ve en dışta boş olan bir değer. Her ikisine de `map` uygulanır ve dönüştürücü, aldığı değerin `nil` olup olmadığını döndürür.

```swift
// main.swift — dis katman doluysa yokluk iceride olsa da map kosar
let outer = Probe("dis-katman-dolu")
let hollow = Probe("dis-katman-bos")
let inner: Int?? = .some(nil)
let empty: Int?? = nil

for _ in 1...2 {
    _ = outer.use { inner.map { value in outer.eval(value == nil) } }
    _ = hollow.use { empty.map { value in hollow.eval(value == nil) } }
}
outer.report()
hollow.report()
print(inner.map { $0 == nil })
print(empty.map { $0 == nil })
```

```
dis-katman-dolu: 2 degerlendirme, 2 kullanim -> her-kullanimda
dis-katman-bos: 0 degerlendirme, 2 kullanim -> hic
Optional(true)
nil
```

Dış katmanı dolu olan değerde dönüştürücü çalıştı ve `nil` aldı: sonuç `Optional(true)`'dur, çünkü dönüştürücü içerideki yokluğu `true` olarak döndürdü. En dışta boş olan değerde dönüştürücü hiç çalışmadı. Yani `map` için yokluk, yalnızca en dış kutunun boş olmasıdır; içeride yokluk taşıyan ama dışı dolu bir değer, `map` açısından doludur.

Bu sonuç, dersin kuralının geçerli olduğu alanı daraltır. Dönüştürücü kapanışın hiç değerlendirilmemesi, kutunun **dış katmanının** boş olmasına bağlıdır. İç içe isteğe bağlı değerlerle çalışırken `map` yokluğu "bir katman yukarı" taşımaz: iç katmandaki yokluğu dönüştürücüye değer olarak verir. Bu yüzden önceki bölümdeki `Optional(nil)` sonucu, sonraki bir `map` çağrısı için dolu bir kutudur. Katman sayısını azaltmak istendiğinde `flatMap` kullanılır.

**TS4.** Bu ölçüm iki katmanlı bir kutu için; üç ya da daha çok katmanlı kutular denenmedi. Dönüştürücünün `nil` aldığını biz `value == nil` karşılaştırmasıyla gözlemledik; başka bir gözlem biçimi denenmedi.

#### Özet

- `Int?` ile `Int` ayrı tiplerdir; yokluk tipin içinde taşınır ve derleyici kontrolü zorunlu kılar.
- Optional bir numaralandırmadır: `.some` bir değer taşır, `.none` (yani `nil`) hiçbir değer taşımaz.
- Boş kutuda `map` dönüştürücü kapanışı hiç çağırmaz; dolu kutuda her çağrıda yeniden koşturur.
- Dönüştürücü isteğe bağlı bir değer döndürdüğünde `map` katman ekler, `flatMap` düzleştirir; `Optional(nil)` ile `nil` ayrı değerlerdir.
- Dış katmanı dolu olan `Int??` içinde yokluk bulunsa bile `map` dönüştürücüyü çalıştırır.
- Bu dersin anı: **hiç**. Boş bir kutuda `map` dönüştürücüsü hiç değerlendirilmez.

#### Sonraki Adım

Bu derste kutuyu açmadan çalıştık: `map` ve `flatMap` değeri dönüştürdü ama sonucu yine bir kutuda bıraktı. Gerçek bir değere ihtiyaç duyulduğunda kutunun açılması gerekir. Bir sonraki ders açmanın yollarını ölçer: `if let` ile `guard let` sağ tarafı ne zaman değerlendirir, `??` zinciri kaçıncı adayda durur ve isteğe bağlı zincirleme `nil` gördüğünde zincirin geri kalanına ne yapar.

### İsteğe Bağlı Açma

Source: https://academia.sh/tr/kurslar/swift/istege-bagli-acma

if let ve guard let sağ tarafının, ?? zincirinin ve isteğe bağlı zincirlemenin ne zaman değerlendirildiğinin ölçümü.

Önceki ders kutuyu açmadan çalıştı: `map` ve `flatMap` boş kutuda dönüştürücüyü hiç koşturmadı, dolu kutuda her çağrıda yeniden koşturdu. Sonuç yine bir kutuydu. Programın bir yerinde gerçek bir değere ihtiyaç duyulur: ekrana yazılacak bir sayı, bir dosyanın adı, bir toplamın terimi. Bu ders kutuyu açmanın yollarını sıralar ve her yol için aynı soruyu sorar: açılan ifade ne zaman değerlendiriliyor, açıldıktan sonraki kod ne zaman koşuyor?

Swift kutuyu açmak için birbirinden farklı araçlar sunar; bu ders beşini ölçer. **Koşullu bağlama (optional binding)**, yani `if let` ve `guard let`, kutuyu bir kez açar ve içindekini bir ada bağlar. `??` işleci boş kutu yerine bir yedek değer koyar. **İsteğe bağlı zincirleme (optional chaining)**, yani `?.`, bir çağrıyı yalnızca kutu doluysa yapar. Zorla açma, yani ünlem işaretiyle yazılan sonek işleç, yokluğu çalışma zamanında bir hataya çevirir. Beşincisi, `map` ve `flatMap`, önceki derste ölçüldü. Her aracın değerlendirme anı farklıdır ve fark hata yazılacak yeri belirler.

Kursun ölçü ekseni bu derste de aynıdır: **bu ifade ne zaman değerlendiriliyor?** Dört an vardır: hemen, ilk kullanımda, her kullanımda, hiç. Kuralı sayan `Probe` sınıfı aşağıda olduğu gibi verilir.

#### Çekirdek

Her ders aynı çekirdeği kullanır. `Probe` bir ifadenin kaç kez değerlendirildiğini (`eval`) ve kaç kez kullanıldığını (`use`) sayar; `moment` özelliği bu iki sayıdan anı çıkarır. Açılış dersindeki bağlayıcı ölçümde sekiz ifadenin dağılımı 2/1/3/2 idi: ikisi hemen, biri ilk kullanımda, üçü her kullanımda, ikisi hiç. Bu derste yalnız kendi kesitini ölçeriz.

```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)")
    }
}
```

Ölçümlerde adı değiştirilebilen küçük bir sınıfa ihtiyaç var. Sınıfın kendisi bir ölçüm değildir, yalnızca zincirleme örneğine malzeme sağlar; bu yüzden çıktısı yoktur.

```swift
// User.swift — adi degistirilebilen kucuk bir sinif
final class User {
    var name: String
    var renames = 0

    init(name: String) {
        self.name = name
    }

    func rename(_ newName: String) {
        name = newName
        renames += 1
    }
}
```

#### Koşullu Bağlama

`if let number = lookup("12", probe: source)` ifadesinde eşitliğin sağ tarafı bir isteğe bağlı değerdir. Swift bu ifadeyi bir kez değerlendirir, sonucu kontrol eder, kutu doluysa içindekini `number` adına bağlar ve blok gövdesini koşturur. Kutu boşsa gövde hiç çalışmaz. Aşağıdaki ölçümde sağ taraf `source.eval` ile sarılıdır ve bağlanan ad gövdede iki kez kullanılır.

```swift
// main.swift — if let sag tarafi bir kez degerlendirir, acilan ad iki kez kullanilir
let source = Probe("if-let-sag-taraf")

func lookup(_ text: String, probe: Probe) -> Int? {
    probe.eval(Int(text))
}

if let number = lookup("12", probe: source) {
    _ = source.use { number + 1 }
    _ = source.use { number * 2 }
}
source.report()
```

```
if-let-sag-taraf: 1 degerlendirme, 2 kullanim -> hemen
```

Bir değerlendirme, iki kullanım, an **hemen**. Sağ taraf bloğa girilmeden önce koştu; ad bağlandıktan sonra `number` sıradan bir tamsayıdır ve onu kullanmak sağ tarafı yeniden çalıştırmaz. Bu, önceki dersteki `map` ile bir tezat oluşturur: `map` dönüştürücüyü her çağrıda koşturuyordu, koşullu bağlama ise ifadeyi bir kez koşturup sonucu adda saklar. Aynı sağ tarafı sonradan iki kez okumak istediğimizde bağlanan ad ek bir değerlendirme gerektirmez.

Bağlanan ad yalnızca blok içinde geçerlidir; blok bittiğinde `number` yoktur. Aynı işlev çağrısını ikinci bir `if let` içinde yeniden yazmak ise ikinci bir değerlendirme demektir, çünkü `if let` sağ tarafını bağlama anında koşturur ve sonucu başka bir `if let` ile paylaşmaz. Değerin birden çok yerde gerekeceği durumda çağrıyı bir kez yazıp sonucu bir adda tutmak gerekir.

**TS5.** Bu ölçüm sağ tarafı tek bir işlev çağrısı olan `if let` için. Birden çok koşulun virgülle bağlandığı biçim (`if let a, let b`) ve `where` koşulu ölçülmedi.

#### guard let ve Kısa Biçim

`guard let` aynı bağlamayı ters yönden yapar. Kutu boşsa `else` bloğu koşar ve bloğun kapsamı terk etmesi (`return`, `throw`, `break`, `continue` ya da `fatalError` gibi dönmeyen bir çağrı) zorunludur; kutu doluysa ad bağlanır ve bağlanan ad `guard` satırından sonra, işlevin kalan kısmında geçerlidir. Bu yüzden erken çıkış gerektiren kontroller `guard` ile yazılır: asıl iş girintisiz kalır, hata durumu en üstte toplanır. Kısa biçim olan `if let maybe` ise adı yeniden yazmadan aynı adla bağlar.

```swift
// main.swift — guard let ve kisa if let
func label(for text: String) -> String {
    guard let number = Int(text) else {
        return "gecersiz"
    }
    return "sayi \(number)"
}

let maybe: Int? = Int("5")
if let maybe {
    print("kisa bicim", maybe)
}
print(label(for: "30"))
print(label(for: "otuz"))
```

```
kisa bicim 5
sayi 30
gecersiz
```

Birinci satır kısa biçimi gösterir: `maybe` adlı isteğe bağlı değer, aynı adla ve isteğe bağlı olmayan bir tamsayı olarak blok içinde kullanılır. İkinci ve üçüncü satır `guard let` içeren `label(for:)` işlevinin iki yolunu gösterir: `"30"` bağlamayı geçer ve işlevin sonundaki dize oluşur, `"otuz"` ise `else` kolundan döner. Bağlanan `number` adı `guard` satırından sonra `Int` tipindedir; kutuyu bir daha açmaya gerek kalmaz.

İki bağlama biçimi arasındaki fark ağırlık dağılımındadır, yoksa ölçülebilen bir değerlendirme farkında değil. `if let` başarılı yolu bir blokta tutar, `guard let` hata yolunu bir blokta tutar. Hangi yol uzunsa onu girintisiz bırakan seçim daha okunur sonuç verir.

#### Yedek Değer: ??

`??` işleci iki yandan oluşur. Soldaki isteğe bağlı değer doluysa içindeki değer sonuç olur; boşsa sağdaki yedek değer sonuç olur. Sağdaki ifade, **otomatik kapanış (autoclosure)** olarak yazılmış bir parametredir: Swift onu bir kapanışa sarar ve işleç yalnızca gerektiğinde çağırır. Bu, önceki dersteki dönüştürücünün bir başka örneğidir: kaynakta yazılı bir ifade, bir yöntem onu çağırana kadar hiç koşmayabilir.

Zincirleme yazıldığında bu davranış bir adım ileri gider. Aşağıda üç aday var: boş bir kutu, `second` probu ile işaretlenmiş dolu bir kutu ve `third` probu ile işaretlenmiş bir yedek sayı. Zincir ilk dolu adayda durur. Döngü iki tur döner ve her turda sonuç iki kez kullanılır.

```swift
// main.swift — ?? zinciri ilk dolu degerde durur
let second = Probe("ikinci-aday")
let third = Probe("ucuncu-aday")
let nothing: Int? = nil
let found: Int? = 8

for _ in 1...2 {
    let result = nothing ?? second.eval(found) ?? third.eval(0)
    _ = second.use { result }
    _ = third.use { result }
}
second.report()
third.report()
```

```
ikinci-aday: 2 degerlendirme, 2 kullanim -> her-kullanimda
ucuncu-aday: 0 degerlendirme, 2 kullanim -> hic
```

İkinci aday her turda değerlendirildi: iki tur, iki değerlendirme, an **her kullanımda**. Üçüncü aday hiç değerlendirilmedi, çünkü ikinci aday doluydu ve zincir orada kesildi. Yani `a ?? b ?? c` yazıldığında `c` yalnızca hem `a` hem `b` boşsa hesaplanır. Pahalı bir yedek değeri sağa koymak bu yüzden boşa iş üretmez: ilk aday dolu olduğu sürece o ifade hiç değerlendirilmez.

Bu davranışın bir yan sonucu vardır. Sağ taraftaki ifadenin yan etkisi, örneğin bir sayaç artırımı ya da bir günlük satırı, yalnızca soldaki kutu boşken gerçekleşir. Yan etkinin her durumda olmasını isteyen kod, yan etkiyi `??` ifadesinin içine değil, öncesine yazmalıdır.

**TS6.** Bu ölçüm üç adaylı bir zincir ve sabit dolu ikinci aday için. Soldaki kutunun dolu olduğu iki adaylı biçim ve yedek değerin bir işlev çağrısı olduğu durum ayrıca ölçülmedi.

#### İsteğe Bağlı Zincirleme

`presentUser?.rename("y")` ifadesindeki `?` bir soru işaretidir ve bir yokluk kontrolünü işaretler. Alıcı doluysa çağrı yapılır; alıcı boşsa çağrı yapılmaz ve ifadenin değeri `nil` olur. Çağrının dönüş tipi `Int` olsaydı zincirleme onu `Int?` yapardı; bu yüzden zincirlemenin sonucu her zaman isteğe bağlı bir değerdir; dönüş tipi olmayan bir çağrıda bile sonuç `Void?` olur.

Asıl soru çağrının argümanıdır. `absentUser?.rename(skipped.eval("x"))` yazıldığında alıcı boştur ve çağrı yapılmaz. Argüman ifadesi çağrıdan önce hesaplanan bir şeydir; peki alıcı boşken de hesaplanır mı? İki alıcıyla ölçelim: biri boş, biri dolu. Her biri için argümanı `eval` ile işaretli bir probe ve döngü iki tur.

```swift
// main.swift — zincir nil'de durunca arguman da degerlendirilmez
let skipped = Probe("nil-alici")
let reached = Probe("dolu-alici")
let absentUser: User? = nil
let presentUser: User? = User(name: "ada")

for _ in 1...2 {
    absentUser?.rename(skipped.eval("x"))
    presentUser?.rename(reached.eval("y"))
    _ = skipped.use { absentUser?.renames }
    _ = reached.use { presentUser?.renames }
}
skipped.report()
reached.report()
print(presentUser?.name ?? "yok", presentUser?.renames ?? -1)
```

```
nil-alici: 0 degerlendirme, 2 kullanim -> hic
dolu-alici: 2 degerlendirme, 2 kullanim -> her-kullanimda
y 2
```

Boş alıcıda argüman hiç değerlendirilmedi. Zincirleme, alıcının boşluğunu gördüğü anda ifadenin geri kalanını atlar; argüman da bu "geri kalan"ın içindedir. Dolu alıcıda argüman her turda bir kez değerlendirildi. Son satır sonucu doğrular: dolu kullanıcının adı `y` oldu ve iki turda iki yeniden adlandırma sayıldı.

Bu davranışın uzun zincirlerde de sürmesi beklenir: `a?.b?.c(expensive())` yazıldığında `a` ya da `b` boşsa `expensive()` koşmamalıdır; bu ders yalnız iki halkalı zinciri ölçtü (TS8). Argümana yazılan yan etkili bir çağrıyı zincirin boş gelebilecek bir halkasının sağına koymak, o yan etkiyi koşullu yapar.

#### Araçları Birleştirmek

Beş araç tek başına nadiren kullanılır. Yukarıdaki son çıktı satırı iki aracı birlikte gösterir: `presentUser?.name ?? "yok"` ifadesinde zincirleme kutuyu açıp adı okur, sonuç yine bir kutu olduğu için `??` boş kalan durumda bir yedek dize koyar. Alıcı dolu olduğundan çıktı `y` oldu; alıcı boş olsaydı `yok` yazılacaktı. Aynı şekilde `presentUser?.renames ?? -1` ifadesi sayaç değerini verir ve boş alıcıda `-1` döndürür.

Birleşimin değerlendirme anı bileşenlerinin anlarından çıkar. Zincirleme boş alıcıda geri kalanı atladığı için `??` yalnızca bir kez, zincirin döndürdüğü kutuya bakar. Yedek değer sağdadır ve soldaki dolu olduğu sürece hiç değerlendirilmez. Bu iki kural birbirini tamamlar: yokluk yalnızca bir yerde ele alınır ve ele alındığı noktadan sonra kod isteğe bağlı olmayan bir değerle çalışır.

**TS7.** Bu birleşim yalnız iki aracın yan yana olduğu durum için ve çıktıdaki son satırdan okundu. Üç ya da daha çok aracın iç içe yazıldığı biçimler ayrıca ölçülmedi.

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

Zincirleme için kurduğumuz kural şu: boş alıcıda argüman hiç değerlendirilmez. Önceki ölçümde alıcı doğrudan bir sabitti ve en baştan boştu. Peki yokluk zincirin ortasında, bir yöntem çağrısının sonucunda ortaya çıkarsa ne olur? Aşağıda `find` yöntemi her zaman `nil` döndürür. Bir ifade `find("ada")?.rename(...)` ile boş kutuya çağrı yapar; aynı satırdaki ikinci eleman ise zincirin parçası değil, ayrı bir ifadedir.

```swift
// main.swift — zincirin ortasinda bos donen halka, sonraki cagrinin argumanini atlatir
struct Directory {
    func find(_ name: String) -> User? {
        nil
    }
}

let tail = Probe("orta-bos-sonraki")
let free = Probe("orta-bos-bagimsiz")
let directory = Directory()

for _ in 1...2 {
    _ = (directory.find("ada")?.rename(tail.eval("z")), free.eval("w"))
    _ = tail.use { 0 }
    _ = free.use { 0 }
}
tail.report()
free.report()
```

```
orta-bos-sonraki: 0 degerlendirme, 2 kullanim -> hic
orta-bos-bagimsiz: 2 degerlendirme, 2 kullanim -> her-kullanimda
```

Zincirin ortasında doğan yokluk da sonraki çağrının argümanını atlattı: `find` boş döndüğünde `rename` çağrısı yapılmadı ve onun argümanı hiç değerlendirilmedi. Aynı satırda, virgülle ayrılmış ikinci elemandaki bağımsız ifade ise her turda değerlendirildi. Atlanan yer, `?` işaretinden sonra gelen zincirin kendisidir; aynı satırda yazılmış olmak bir ifadeyi zincirin parçası yapmaz.

Bu sonuç dersin kuralının geçerli olduğu alanı daraltır. "Boş alıcı argümanı değerlendirmez" cümlesi, zincirin boş kalan halkasından sonra gelen ifadeler için geçerlidir ve ölçülen iki halkada yokluğun nerede doğduğuna bağlı değildi. Zincirin dışındaki ifadeler bu kuralın kapsamına girmez.

**TS8.** Bu ölçüm iki halkalı bir zincir için. Üç ya da daha çok halkalı zincirlerde ortadaki bir `?` işaretinin davranışı ve alıcının bir özellik olduğu durum ayrıca ölçülmedi.

#### Zorla Açma

Zorla açma işleci bu derste ölçülmez. Çünkü ölçülecek bir değerlendirme anı yoktur: kutu doluysa içindeki değer alınır, boşsa program durur. Aşağıdaki parça derlenir ama çalıştırıldığında çalışma zamanı hatası verir; çıktı iddiası taşımadığı için burada yalnız kaynak olarak durur.

```swift
let text = "otuz"
let broken = Int(text)!   // calisma zamaninda coker: kutu bos
print(broken)
```

Zorla açma, bir yokluk kontrolünü derleme zamanından çalışma zamanına taşır. Derleyicinin tip sistemiyle sağladığı güvence burada kalkar: `Int?` tipindeki değer, yazarın "bu kutu dolu" sözüne bağlı kalarak `Int` olarak kullanılır. Söz tutulmazsa program durur. Yokluğun kanıtlanmış biçimde imkânsız olduğu yerlerde, örneğin kaynak kodda sabit yazılmış ve geçerli olduğu bilinen bir metnin dönüştürülmesinde, zorla açma savunulabilir; kullanıcıdan ya da dışarıdan gelen veride savunulamaz.

#### Özet

- `if let` sağ tarafı bir kez ve bloğa girmeden önce değerlendirir; bağlanan ad bir daha hesap yaptırmaz.
- `guard let` aynı bağlamayı erken çıkışla yazar; bağlanan ad `guard` satırından sonra geçerlidir.
- `??` zinciri ilk dolu adayda durur; sağdaki aday yalnızca soldaki boşsa hesaplanır.
- İsteğe bağlı zincirleme boş alıcıda `?` işaretinden sağdaki çağrıyı ve argümanını atlar.
- Zorla açma yokluk kontrolünü çalışma zamanına taşır ve ölçülecek bir an taşımaz.
- Bu dersin anı: **hemen**. `if let` sağ tarafı bloğa girmeden önce bir kez değerlendirilir.

#### Sonraki Adım

Bu derste kutular açıldı ve içlerindeki değerler kullanıldı. Değerlerin kendisi iki yoldan biriyle tanımlanır: kopyalanan yapılarla ya da paylaşılan sınıflarla. Bir sonraki ders yapılar ve sınıflar arasındaki farkı, kopyanın tam olarak hangi anda oluştuğunu ölçerek inceler.

### Yapılar ve Sınıflar

Source: https://academia.sh/tr/kurslar/swift/yapilar-ve-siniflar

Yapıların atamada kopyalanmasının, sınıfların paylaşılmasının, varsayılan değerin ne zaman kurulduğunun ve kopyanın ilk değişiklikte oluşmasının ölçümü.

Önceki ders kutuyu açmanın yollarını ölçtü ve her yolun ifadeyi ne zaman değerlendirdiğini sıraladı. Açılan değer bir `Int` ya da bir `String` olabilir, ama programın asıl verisi çoğu zaman birkaç alanı bir arada tutan bir tiptir: bir nokta, bir sipariş, bir belge. Swift'te böyle tiplerin iki temel biçimi vardır (numaralandırmalar ve aktörler ayrı derslerin konusu). **Yapı (structure)** bir **değer tipidir**: atandığında içeriği kopyalanır. **Sınıf (class)** bir **referans tipidir**: atandığında aynı nesneye ikinci bir ad verilir.

Bu fark çoğu kitapta "kim neyi görüyor" sorusuyla anlatılır: bir adın değişikliğini öbür ad görüyor mu? Bu ders başka bir soruyu sorar: kopya ne zaman oluşuyor? Atamanın kendisi bir kopya mı üretir, yoksa kopya yalnızca ilk değişiklikte mi ortaya çıkar? Kursun ölçü ekseni bu soruyu dört ana ayırır: hemen, ilk kullanımda, her kullanımda, hiç. `Probe` sınıfı aşağıda olduğu gibi verilir.

#### Çekirdek

Her ders aynı çekirdeği kullanır. `Probe` bir ifadenin kaç kez değerlendirildiğini (`eval`) ve kaç kez kullanıldığını (`use`) sayar; `moment` özelliği bu iki sayıdan anı çıkarır. Açılış dersindeki bağlayıcı ölçümde sekiz ifadenin dağılımı 2/1/3/2 idi: ikisi hemen, biri ilk kullanımda, üçü her kullanımda, ikisi hiç. Bu derste yalnız kendi kesitini ölçeriz.

```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)")
    }
}
```

Aşağıdaki ilk ölçümler üç tip kullanır. `Point` iki tamsayı tutan bir yapıdır, `Counter` tek bir sayaç tutan bir sınıftır. `Order` varsayılan değerli bir alana sahip bir yapıdır; varsayılan değer bir işlev çağrısıdır ve işlev çalıştığında bir satır yazdırır. Bu bloğun çıktısı yoktur, çünkü yalnızca tipleri tanımlar.

```swift
// Types.swift — bir yapi, bir sinif ve varsayilan degerli bir yapi
struct Point {
    var x: Int
    var y: Int
}

final class Counter {
    var value = 0
}

func makeNote() -> String {
    print("varsayilan deger kuruldu")
    return "yok"
}

struct Order {
    var id: Int
    var note = makeNote()
}
```

#### Atama: Kopya mı, Paylaşım mı

Aynı iki satırı bir yapıyla ve bir sınıfla yazalım. Yapıda `var second = first` ardından `second.x = 10` yazıldığında `first` değişmez. Sınıfta `let right = left` ardından `right.value = 5` yazıldığında iki ad aynı nesneye bağlıdır ve her ikisi de `5` görür.

```swift
// main.swift — yapi atamada kopyalanir, sinif paylasilir
let first = Point(x: 1, y: 2)
var second = first
second.x = 10
print(first.x, second.x)

let left = Counter()
let right = left
right.value = 5
print(left.value, right.value)
```

```
1 10
5 5
```

Birinci satır yapıyı gösterir: `first.x` `1` kaldı, `second.x` `10` oldu. Atama bağımsız bir kopya verdi. İkinci satır sınıfı gösterir: `left.value` ve `right.value` ikisi de `5`'tir, çünkü tek bir `Counter` nesnesi vardır ve iki ad ona bağlıdır. Dikkat edin ki `right` bir `let` sabitidir ve yine de nesnenin içindeki `value` değişebildi: `let` adın başka bir nesneye bağlanmasını yasaklar, bağlı nesnenin içeriğini yasaklamaz. Yapıda ise `let` bütün değerin değişmezliği anlamına gelir: değişmez bir yapının alanı, alan `var` olarak tanımlanmış olsa bile atanamaz.

Çıktı iki tipi birbirinden ayırır ama kopyanın ne zaman oluştuğunu söylemez. Yapı için "atamada kopyalanır" cümlesi ders kitaplarında bir model olarak vardır; ölçtüğümüz şey ise gözlenebilir davranıştır: değişiklikten sonra iki değer bağımsızdır. Kopyanın fiziksel olarak atamada mı yoksa sonradan mı oluştuğu aşağıda ölçülecek.

**TS9.** Bu ölçüm iki tamsayı alanlı bir yapı ve tek alanlı bir sınıf için. Alanların başka bir sınıfa referans tuttuğu yapılar ve yapı içindeki sınıf alanlarının kopyalanması bu ölçümde yok.

#### İşleve Geçirme

Atama değer ile referans ayrımının yalnız bir yüzüdür; aynı ayrım bir işleve argüman verirken de yaşanır. Bir işlev yapıyı parametre olarak aldığında kendi kopyasıyla çalışır: içeride yapılan değişiklik, çağıran tarafın değerine ulaşmaz. Sınıf parametresinde ise işlev ile çağıran aynı nesneye bakar. Aşağıdaki ölçümde `moved` yapının bir kopyasını değiştirip döndürür, `bump` ise sayacı yerinde değiştirir.

```swift
// main.swift — isleve gecirilen yapi kopyalanir, sinif paylasilir
func moved(_ point: Point) -> Point {
    var copy = point
    copy.x += 100
    return copy
}

func bump(_ counter: Counter) {
    counter.value += 100
}

let origin = Point(x: 1, y: 2)
let shifted = moved(origin)
print(origin.x, shifted.x)

let ticker = Counter()
bump(ticker)
print(ticker.value)
```

```
1 101
100
```

Birinci satırda `origin.x` `1` olarak kaldı, `shifted.x` ise `101` oldu: `moved` içindeki `copy` ayrı bir değerdi ve değişiklik yalnızca ona yazıldı. İkinci satırda `ticker.value` `100` oldu, çünkü `bump` aynı `Counter` nesnesini değiştirdi ve işlevin döndürdüğü bir şey yok. Bu yüzden parametresi yapı olan bir işlevin imzası, yapı yalnız değer tutuyorsa ve parametre `inout` değilse, okuyana "bu işlev çağırdığım değeri değiştirmez" der. Parametresi sınıf olan bir işlevin imzası böyle bir güvence vermez; işlevin gövdesine bakmak gerekir.

Bu ayrımın pratik sonucu, hata ayıklamada görünür. Bir yapının değeri beklenmedik biçimde değiştiyse, değişiklik o değerin kendi kapsamında yapılmıştır; bir sınıf nesnesi beklenmedik biçimde değiştiyse, nesneye ulaşabilen her yer şüphelidir. İşlev imzaları arasında değer tipi kullanmak şüpheli alanı daraltır.

**TS10.** Bu ölçüm bir yapı ve bir sınıf parametresi için. `inout` parametre ile geçirme ve kapanışın yakaladığı yapı bu ölçümde yok.

#### Varsayılan Değer: Her Örnekte Bir Kez

`Order` yapısının `note` alanı `makeNote()` işlevinin sonucuyla başlar. Bu varsayılan değer bir kez kurulup bütün örnekler arasında paylaşılmaz; her `Order` örneği kendi varsayılan değerini oluşturur. Üstelik değer, çağıran taraf alanı kendisi vermişse hiç hesaplanmaz. Üç siparişle ölçelim: ikisi varsayılan notla, biri açık bir notla.

```swift
// main.swift — varsayilan deger her ornekte, ama yalniz verilmediyse degerlendirilir
let o1 = Order(id: 1)
let o2 = Order(id: 2)
let o3 = Order(id: 3, note: "acele")
print(o1.note, o2.note, o3.note)
```

```
varsayilan deger kuruldu
varsayilan deger kuruldu
yok yok acele
```

`makeNote()` iki kez çalıştı, üç siparişin ikisi için. Notu açıkça veren üçüncü sipariş için işlev hiç çağrılmadı. Çalıştığı iki yer de yazdırma satırından önce görünür; yani varsayılan değer, örnek kurulurken, çağıran satırda hesaplandı.

Bir sonuç çıkar: varsayılan değer olarak yazılan her ifade örnek sayısı kadar değerlendirilir, verilen değer sayısı kadar eksik. Değerlendirmesi pahalı bir ifade varsayılan değer olarak yazılırsa, bu maliyet her örnekte ödenir. Özellikler dersinde `lazy` ile bu maliyetin ertelenmesi ölçülecek.

**TS11.** Bu ölçüm üç örnekle ve bir varsayılan değerli alanla yapıldı. Birden çok varsayılan değerli alanın değerlendirme sırası ve sınıflarda aynı durum ayrıca ölçülmedi.

#### Yazarken Kopyalama

Büyük bir değer tipi atandığında gerçekten tamamını kopyalamak israf olurdu. Standart kitaplığın bazı tipleri bu yüzden **yazarken kopyalama (copy-on-write)** denen bir teknik kullanır: atama yalnızca bir depoya ikinci bir referans ekler; kopya ancak ortak depoyu değiştirmek gerektiğinde, ilk değişiklikte oluşur. Aynı tekniği kendi tipimizde elle kurabiliriz. `Document` yapısı metni bir `Storage` sınıfında tutar. `append` önce depoya tek başına mı sahip olduğumuzu `isKnownUniquelyReferenced` ile sorar; değilse depoyu kopyalar ve kopyalama anını `onCopy` kapanışıyla bildirir.

```swift
// Document.swift — sinif kutulu, yazarken kopyalayan yapi
final class Storage {
    var text: String

    init(text: String) {
        self.text = text
    }
}

struct Document {
    private var storage: Storage
    private let onCopy: () -> Void

    init(text: String, onCopy: @escaping () -> Void) {
        storage = Storage(text: text)
        self.onCopy = onCopy
    }

    var text: String {
        storage.text
    }

    mutating func append(_ more: String) {
        if !isKnownUniquelyReferenced(&storage) {
            onCopy()
            storage = Storage(text: storage.text)
        }
        storage.text += more
    }
}

final class Holder {
    var document: Document

    init(_ document: Document) {
        self.document = document
    }
}
```

Koda yakından bakalım. `storage` alanı `private` olduğu için dışarıdan görünmez; dışarıya yalnızca salt okunur `text` özelliği açılır. Değişiklik yalnız `mutating` olarak işaretli `append` içinden geçer ve kopyalama kararı da yalnızca orada verilir. Bu daraltma önemlidir: kopyalama kuralı tek bir yerde durduğu için, tipin başka hiçbir yöntemi depoyu paylaşılan halde değiştiremez. `onCopy` kapanışı ise ölçüm içindir; üretim kodunda böyle bir bildirim bulunmaz, ama burada kopyanın tam olarak hangi çağrıda oluştuğunu saymamızı sağlar. `Holder` sınıfı ise son ölçümde kullanılacak: bir `Document` değerini bir sınıf nesnesinin alanı yapar.

Bu bloğun çıktısı yoktur; yalnızca `Document` ve onu bir sınıfa saran `Holder` tanımlıdır. Şimdi `duplicate = original` ataması yapılıp ikisi de değiştirilince kopyanın tam olarak ne zaman oluştuğunu ölçelim. `onCopy` kapanışı `cow.eval` ile sarılıdır; iki kez `append` çağrılır.

```swift
// main.swift — kopya atamada degil, ilk degisiklikte olusur
let cow = Probe("yazarken-kopyalama")
let original = Document(text: "ab", onCopy: { _ = cow.eval(()) })
var duplicate = original

_ = cow.use { duplicate.append("c") }
_ = cow.use { duplicate.append("d") }
cow.report()
print(original.text, duplicate.text)
```

```
yazarken-kopyalama: 1 degerlendirme, 2 kullanim -> ilk-kullanimda
ab abcd
```

Atamanın kendisi kopya üretmedi: değerlendirme sayısı bir ve an **ilk kullanımda**. İlk `append` depoyu başka bir değerle paylaşıyordu, bu yüzden kopya o anda oluştu. İkinci `append` kopyalanan depoya tek başına sahipti, kopya gerekmedi. Son satır sonucu doğrular: `original` hâlâ `ab`, `duplicate` ise `abcd`. Ortada iki değer var ama yalnızca bir kopyalama yaşandı ve o da atamada değil, ilk değişiklikte.

Sayıların anlamını da yazalım. Bir değerlendirme, kopyanın bir kez oluştuğunu söyler; iki kullanım, iki `append` çağrısı yapıldığını söyler. Değerlendirme sayısı kullanım sayısına eşit olsaydı her değişiklik kopya doğururdu ve an **her kullanımda** olurdu. Sıfır olsaydı hiçbir değişiklik kopya gerektirmezdi ve an **hiç** olurdu. Ölçülen bir, ikinin altındadır ve ilk kullanımda oluşmuştur; bu üç sayı birlikte anı belirler.

Bu ölçüm başta sorduğumuz soruyu yanıtlar. Depoyu paylaşan `Document` gibi bir değer tipinde kopya atamadan sonra, ilk yazma sırasında oluşur. Okuyan taraf için bir fark yoktur; kopyanın bedeli, ilk yazan tarafa düşer. Bu yüzden değer tipi ile referans tipi arasındaki seçim yalnız anlamsal bir tercih olarak değil, bedelin ne zaman ödeneceği olarak da okunabilir.

**TS12.** Bu ölçüm yalnızca elle kurduğumuz `Document` tipi için; standart kitaplığın dizi, sözlük ve dize tiplerinin kendi yazarken kopyalamaları bu derste ölçülmedi.

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

Yazarken kopyalama için kurduğumuz kural şu: kopya ilk değişiklikte oluşur. Kuralın bir koşulu vardır, depo başka bir sahiple paylaşılıyor olmalı. İki durumda bu koşul oluşmaz. Birincisinde `solo` belgesine kimse ortak olmamıştır; ikincisinde belge bir sınıfın içindedir ve iki ad aynı sınıf nesnesine bağlıdır, yani depoyu paylaşan ikinci bir `Document` değeri yoktur. Her ikisinde de iki kez `append` çağrılır.

```swift
// main.swift — tek sahipli ya da sinif icinde paylasilan belge kopyalanmaz
let unique = Probe("tek-sahipli")
var solo = Document(text: "ab", onCopy: { _ = unique.eval(()) })
_ = unique.use { solo.append("c") }
_ = unique.use { solo.append("d") }

let shared = Probe("sinif-paylasimi")
let holder = Holder(Document(text: "ab", onCopy: { _ = shared.eval(()) }))
let alias = holder
_ = shared.use { holder.document.append("c") }
_ = shared.use { alias.document.append("d") }

unique.report()
shared.report()
print(solo.text, holder.document.text, alias.document.text)
```

```
tek-sahipli: 0 degerlendirme, 2 kullanim -> hic
sinif-paylasimi: 0 degerlendirme, 2 kullanim -> hic
abcd abcd abcd
```

İki durumda da `onCopy` hiç çağrılmadı: an **hiç**. Tek sahipli belge ilk değişiklikte bile kopyalanmadı, çünkü paylaşılacak bir ikinci sahip yoktu. Sınıf içindeki belge de kopyalanmadı: `holder` ve `alias` aynı `Holder` nesnesine bağlı olduğundan, bir ad üzerinden yapılan ekleme ötekinde de görünür ve `Document` değerinin ikinci bir kopyası hiç oluşmaz. Son satır bu paylaşımı doğrular: üç ad da `abcd` yazar.

Bu sınır dersin kuralını daraltır. "Değer tipi atamada kopyalanır" cümlesi ve onu tamamlayan "kopya ilk değişiklikte oluşur" cümlesi, yalnızca depoyu paylaşan iki ayrı değer varsa geçerlidir. Değer tipi bir sınıfın alanıysa sınıfın referans anlamı baskın çıkar: kopya oluşmaz, çünkü sınıf nesnesi zaten tektir.

**TS13.** Bu ölçüm tek bir sınıf nesnesi ve iki ad için. Sınıf nesnesinin kendisi `Document` alanıyla birlikte başka bir değere atandığında ya da iki farklı `Holder` nesnesinin aynı belgeyi taşıdığı durum ölçülmedi.

#### Özet

- Yapı atamada bağımsız bir kopya verir; sınıf atamasında iki ad aynı nesneye bağlanır.
- `let` ile tanımlanan sınıf adı nesneyi değişmez yapmaz; `let` ile tanımlanan yapı bütün değeri değişmez yapar.
- Varsayılan değer her örnekte bir kez değerlendirilir ve çağıran alanı verdiyse hiç değerlendirilmez.
- Yazarken kopyalamada kopya atamada değil ilk değişiklikte, depo paylaşılıyorsa oluşur.
- Tek sahipli ya da sınıf içinde paylaşılan değerde kopya hiç oluşmaz.
- Bu dersin anı: **ilk kullanımda**. Yazarken kopyalanan belgede kopya ilk değişiklikte oluşur.

#### Sonraki Adım

Bu derste bir tipin değer mi referans mı olduğu ve kopyanın ne zaman oluştuğu ölçüldü. Bazı tiplerin bir alan listesi değil, birkaç ayrı durumdan birinde olması gerekir: bir ölçüm ya derece cinsindendir ya da bir etikettir. Bir sonraki ders bu durumları ve her durumla birlikte taşınan değerleri anlatan numaralandırmaları ölçer.

### Numaralandırmalar

Source: https://academia.sh/tr/kurslar/swift/numaralandirmalar

Ham değerin ve ilişkili değerin ne zaman kurulduğunun, desen eşlemeyle nasıl açıldığının ve hesaplanan özelliğin her okunuşta koştuğunun ölçümü.

Önceki ders alan listesi olan tipleri ölçtü: yapı ve sınıf, hep aynı alanları taşır. Bazı veriler ise bir alan listesi değil, birkaç ayrı durumdan birinde olmayı gerektirir. Haftanın günü yedi değerden biridir; bir ölçüm ya derece cinsinden bir sayıdır ya da bir etikettir. Bu iki örnek farklı şeyleri anlatır, ama ikisi de **numaralandırma (enumeration)** ile ifade edilir.

Swift numaralandırması birçok dildeki karşılığından daha güçlüdür. Her durum (`case`) yalnızca bir ad değildir; tanımına göre bir **ham değer (raw value)** ya da durumla birlikte taşınan bir **ilişkili değer (associated value)** olabilir. Numaralandırmalar ayrıca hesaplanan özellik ve yöntem taşıyabilir; saklanan örnek özelliği taşıyamaz. Ders önce bu iki değer çeşidinin ne zaman kurulduğunu, sonra bir numaralandırma içindeki hesaplamanın ne zaman koştuğunu ölçer. Kursun ölçü ekseni değişmez: **bu ifade ne zaman değerlendiriliyor?** Dört an vardır: hemen, ilk kullanımda, her kullanımda, hiç. `Probe` sınıfı aşağıda olduğu gibi verilir.

#### Çekirdek

Her ders aynı çekirdeği kullanır. `Probe` bir ifadenin kaç kez değerlendirildiğini (`eval`) ve kaç kez kullanıldığını (`use`) sayar; `moment` özelliği bu iki sayıdan anı çıkarır. Açılış dersindeki bağlayıcı ölçümde sekiz ifadenin dağılımı 2/1/3/2 idi: ikisi hemen, biri ilk kullanımda, üçü her kullanımda, ikisi hiç. Bu derste yalnız kendi kesitini ölçeriz.

```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)")
    }
}
```

Ölçümlerde iki numaralandırma kullanılır. `Weekday` bir `Int` ham değerine sahiptir ve `CaseIterable` ile bütün durumlarını listeler; ilk duruma `1` verildiğinde sonrakiler `2` ve `3` olur. `Reading` ise durumlarıyla birlikte değer taşır: `celsius` bir `Double`, `label` bir `String` alır. Bu bloğun çıktısı yoktur, çünkü yalnızca tipleri tanımlar.

```swift
// Types.swift — ham degerli ve iliskili degerli numaralandirmalar
enum Weekday: Int, CaseIterable {
    case monday = 1, tuesday, wednesday
}

enum Reading {
    case celsius(Double)
    case label(String)
}
```

#### Ham Değer

Ham değer, bir durumun derleme zamanında sabit olarak atanmış karşılığıdır. Her durumun ham değeri farklı olmalıdır ve değerler tanımda yazılır. İlk durum için `1` yazıldığında sonraki durumların değeri bir artarak belirlenir. Ham değere `rawValue` özelliği ile ulaşılır ve ters yönde, ham değerden duruma gitmek için `Weekday(rawValue: 3)` biçimi kullanılır. Ters yönün her ham değer için bir durum bulamayacağı açıktır; bu yüzden bu biçim bir isteğe bağlı değer döndürür.

```swift
// main.swift — ham deger, rawValue ile kurma ve allCases
print(Weekday.tuesday.rawValue)
print(Weekday(rawValue: 3) == .wednesday)
print(Weekday(rawValue: 9) == nil)
print(Weekday.allCases.count)
```

```
2
true
true
3
```

Birinci satır `tuesday` durumunun ham değerini verir: ilk durum `1` ile başladığı için `2`'dir. İkinci satır `rawValue: 3` ile kurulan değerin `.wednesday` ile eşit olduğunu gösterir. Üçüncü satır, hiçbir durumun ham değeri olmayan `9` için sonucun `nil` olduğunu gösterir; ilk derste gördüğümüz boş kutu burada bir `Int` dönüşümünün değil, bir durum aramanın sonucudur. Dördüncü satır `CaseIterable`'ın eklediği `allCases` ile üç durumu saydığını gösterir.

Ham değer burada bir kimlik ve dönüşüm aracıdır. Bir ağ yanıtından, bir dosyadan ya da bir veritabanı alanından gelen tamsayıyı güvenli biçimde bir duruma çevirmek için kullanılır ve geçersiz değerin ne yapılacağı konusunda çağıranı karar vermeye zorlar.

Ham değer yalnız tamsayı olmak zorunda değildir. Tipi `String` olan bir numaralandırmada her durumun ham değeri, açıkça yazılmadıysa durumun kendi adıdır. Aynı üç işlemi bir gezegen listesiyle yapalım: ham değeri okumak, ham değerden durum kurmak ve geçersiz bir ham değerle kurmayı denemek.

```swift
// main.swift — String ham deger belirtilmezse durum adidir
enum Planet: String {
    case mercury, venus, earth
}

print(Planet.venus.rawValue)
print(Planet(rawValue: "earth") == .earth)
print(Planet(rawValue: "pluto") == nil)
```

```
venus
true
true
```

Birinci satır `venus` durumunun ham değerinin, hiçbir değer yazılmadığı hâlde `"venus"` olduğunu gösterir. İkinci satır `"earth"` ham değerinin `.earth` durumunu verdiğini, üçüncü satır ise listede olmayan `"pluto"` için sonucun `nil` olduğunu gösterir. Tamsayı örneğindeki ters yön burada da aynı biçimde isteğe bağlı bir değer döndürdü: ham değerden duruma giden yol her zaman bir yokluk olasılığı taşır.

**TS14.** Bu ölçümler `Int` ve `String` ham değerli birer numaralandırma için. Diğer ham değer tipleri (`Double`, `Character`) ve ham değeri yalnızca bir kısmı için yazılmış `Int` durumlarının davranışı ayrıca ölçülmedi.

#### İlişkili Değer ve Desen Eşleme

İlişkili değer ham değerden farklı çalışır. Ham değer tanım sırasında sabittir ve aynı durumun her örneğinde aynıdır. İlişkili değer ise durum **kurulurken** belirlenir ve her örnek kendi değerini taşır. `Reading.celsius(21.5)` ile `Reading.celsius(30.0)` aynı durumdur, ama farklı veri taşır. Böyle bir değerin içindekine erişmek için `switch` ile **desen eşleme (pattern matching)** kullanılır: her durumun kolunda ilişkili değer bir adla bağlanır.

```swift
// main.swift — iliskili degerin desen eslemeyle acilmasi
let readings = [Reading.celsius(21.5), Reading.label("sicak")]
for reading in readings {
    switch reading {
    case .celsius(let degrees):
        print("derece", degrees)
    case .label(let text):
        print("etiket", text)
    }
}
```

```
derece 21.5
etiket sicak
```

Dizideki iki `Reading` değeri sırayla `switch`'e girer. İlki `celsius` kolunu seçer ve içindeki `21.5`'i `degrees` adına bağlar; ikincisi `label` kolunu seçer ve `"sicak"` dizesini `text` adına bağlar. Her kolda bağlanan ad yalnızca o kolun gövdesinde geçerlidir ve tipi o durumun ilişkili değerinin tipidir. Derleyici bütün durumların ele alındığını denetler: bir durum atlanırsa `switch` tamamlanmamış sayılır ve eksik durum için bir kol yazmak ya da `default` kullanmak gerekir.

Bu davranış, ilk dersteki isteğe bağlı değer ile aynı kalıptır. `Optional` de bir numaralandırmadır ve `.some` durumu bir ilişkili değer taşır; `if let` ve `switch` içindeki `case .some(let value)` aynı eşlemenin iki yazılışıdır. Bu ders, o dersteki kutunun neden böyle davrandığını gösteren ikinci bir örnek verir.

Tek bir duruma bakmak gerektiğinde `switch` yazmak ağır kaçar. `if case` aynı eşlemeyi tek bir koşul olarak yazar: durum eşleşirse ilişkili değer bağlanır ve blok koşar, eşleşmezse `else` kolu koşar. Virgülle bir koşul daha eklenebilir; ilişkili değer bağlandıktan sonra o koşulda kullanılır.

```swift
// main.swift — if case ile tek bir duruma bakmak
let current = Reading.celsius(18.0)
if case .celsius(let degrees) = current, degrees < 20 {
    print("serin", degrees)
}
if case .label(let text) = current {
    print("etiket", text)
} else {
    print("etiket degil")
}
```

```
serin 18.0
etiket degil
```

Birinci `if case`, `current` değerini `celsius` durumuyla eşleştirdi, `18.0`'ı `degrees` adına bağladı ve virgülden sonraki `degrees < 20` koşulunu da geçti. İkinci `if case` aynı değeri `label` durumuyla eşleştirmeye çalıştı, eşleşme olmadığı için `else` kolu koşturdu. Bu, `switch` ile aynı davranıştır: eşleşmeyen durumda ilişkili değer hiç bağlanmaz ve ilgili ad kapsamda yoktur.

**TS15.** Bu ölçüm iki durumlu ve her durumunda tek ilişkili değer olan bir numaralandırma için. Birden çok ilişkili değer taşıyan durumlar, `indirect` numaralandırmalar ve `where` koşullu kollar ölçülmedi.

#### İlişkili Değer Ne Zaman Kurulur

İlişkili değer, durum kurulurken verilen bir ifadedir. `Reading.celsius(stamp.eval(19.0))` yazıldığında `stamp.eval(19.0)` ifadesi durum kurulurken bir kez değerlendirilir ve sonuç durumun içinde saklanır. Sonradan `switch` ile okunduğunda ifade yeniden hesaplanmaz; okunan şey durumun içindeki saklı değerdir. İki kez okuyarak bunu ölçelim.

```swift
// main.swift — iliskili deger case kurulurken bir kez degerlendirilir
let stamp = Probe("iliskili-deger")
let reading = Reading.celsius(stamp.eval(19.0))

for _ in 1...2 {
    _ = stamp.use { () -> Double in
        switch reading {
        case .celsius(let degrees):
            return degrees
        case .label:
            return -1
        }
    }
}
stamp.report()
```

```
iliskili-deger: 1 degerlendirme, 2 kullanim -> hemen
```

Bir değerlendirme, iki kullanım ve an **hemen**. Değerlendirme, `reading` kurulduğu satırda gerçekleşti, `use` çağrılarından önce. Her iki `switch` aynı saklı değeri okudu ve ifadeyi yeniden koşturmadı. Bu, ham değerden farklıdır: ham değerin ifadesi hiç koşmaz, çünkü o bir sabittir. İlişkili değerin ifadesi ise örnek başına bir kez koşar.

Ölçümün gösterdiği pratik sonuç şudur: ilişkili değere yazılan pahalı bir hesaplama, durum kurulurken ödenir ve okuyuşlar ek bir hesap gerektirmez. Hesabın okunduğu anda yapılmasını isteyen kod, ilişkili değeri değil bir hesaplanan özelliği kullanmalıdır. Bir sonraki bölüm bu özelliği ölçer.

**TS16.** Bu ölçüm sabit bir `Double` literalini sarmalayan bir ifade için. İlişkili değerin bir işlev çağrısı ya da başka bir ilişkili değer içeren ifade olduğu durumlar ayrıca ölçülmedi.

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

Ham değer ve ilişkili değer durum kurulurken ya da tanımda sabitlenir; yani okuyuş maliyetsizdir. Numaralandırma ise saklanan değer dışında bir de **hesaplanan özellik (computed property)** taşıyabilir. Bu özellik bir değer saklamaz, her okunduğunda gövdesini çalıştırır. `Sensor` numaralandırmasında `fahrenheit` özelliği, ilişkili değer olarak taşınan Celsius derecesini her okunuşta Fahrenheit'a çevirir. Çevirme ifadesi bir `Probe` ile işaretlidir ve özellik iki kez okunur.

```swift
// main.swift — numaralandirmanin hesaplanan ozelligi her okunusta kosar
enum Sensor {
    case reading(Probe, Double)

    var fahrenheit: Double {
        switch self {
        case .reading(let probe, let celsius):
            return probe.eval(celsius * 9 / 5 + 32)
        }
    }
}

let derived = Probe("hesaplanan-ozellik-sensor")
let sensor = Sensor.reading(derived, 20)
_ = derived.use { sensor.fahrenheit }
_ = derived.use { sensor.fahrenheit }
derived.report()
print(sensor.fahrenheit)
```

```
hesaplanan-ozellik-sensor: 2 degerlendirme, 2 kullanim -> her-kullanimda
68.0
```

İki okuyuş, iki değerlendirme, an **her kullanımda**. Hesaplanan özellik sonucu saklamaz; `sensor.fahrenheit` her yazıldığında gövdesi baştan koşar. İlişkili değer `20` ise kurulurken sabitlenmişti, ama ondan türeyen `68.0` her okunuşta yeniden hesaplanır. Çıktının ikinci satırı, aynı özelliğin bir sonraki okunuşunda aynı sonucu verdiğini gösterir.

Bu sonuç dersin kuralını daraltır. "İlişkili değer durum kurulurken bir kez değerlendirilir" cümlesi yalnızca ilişkili değerin kendisi için doğrudur. İlişkili değerden türeyen her hesaplanan özellik, her okunuşta baştan çalışır. Aynı hesabın birden çok yerde gerektiği durumda sonucu bir adda tutmak, hesaplanan özelliği her yerde yeniden okumaktan ucuzdur.

Ham değer için de bir sınır vardır ve o ölçülemez, çünkü derlenmez. Ham değer, kaynakta yazılan bir sabit olmak zorundadır. Aşağıdaki parça bir işlevin dönüşünü ham değer olarak atamaya çalışır ve derlenmez: derleyici onu reddeder. Hata iletisi, ham değerin bir sabit yazım olması gerektiğini bildirdi. Parça burada yalnızca anlatım için durur.

```swift
func rawOf() -> Int { 3 }
enum Bad: Int {
    case a = 1
    case b = rawOf()   // derlenmez: ham deger bir sabit olmali
}
```

Bu sınır iki değer çeşidinin bir ikinci farkını gösterir. İlişkili değer çalışma zamanında hesaplanan herhangi bir ifade olabilir; ham değer ise derleme zamanında bilinen bir sabittir. Ham değerin ifadesi bu yüzden hiçbir zaman çalışma zamanında değerlendirilmez.

**TS17.** Bu ölçüm iki saklı değer üzerinden türeyen tek bir hesaplanan özellik için. Salt okunur olmayan hesaplanan özellik (`set` içeren) ve numaralandırmanın yöntemleri ayrıca ölçülmedi. Reddedilen ham değer parçasında yalnızca işlev çağrısı denendi; başka ifade biçimleri denenmedi.

#### Üç Değerin Yan Yana Okunuşu

Derste üç ayrı değer çeşidi ölçüldü ve her biri başka bir ana düştü. Ham değer tanımda yazılan bir sabittir; ifade olarak hiçbir zaman çalışma zamanında koşmaz, bu yüzden bir ölçüm probu ona bağlanamaz. İlişkili değer, durum kurulurken verilen bir ifadedir ve örnek başına bir kez değerlendirilir; ölçümde bu an **hemen** çıktı. Hesaplanan özellik ise bir değer saklamaz, her okunuşta gövdesini koşturur; ölçümde bu an **her kullanımda** çıktı.

Bu üç çeşit birbirinin yerine kullanılabilir gibi görünse de ayrı sorulara yanıt verir. Dışarıdan gelen bir kodu bir duruma çevirmek gerekiyorsa ham değer uygundur, çünkü ters yön bir isteğe bağlı değer döndürür ve geçersiz girdiyi görünür kılar. Durumun yanında örneğe özgü veri taşımak gerekiyorsa ilişkili değer uygundur, çünkü bir durumun farklı örnekleri farklı veri taşıyabilir. Taşınan veriden türeyen bir sonucu gerektiği anda üretmek gerekiyorsa hesaplanan özellik uygundur, ama her okunuşta ödenen bedeli de hesaba katmak gerekir.

#### Özet

- Ham değer tanımda sabitlenir ve `Weekday(rawValue:)` bir isteğe bağlı değer döndürür; bulunamayan değer `nil` verir.
- `switch` ile desen eşleme, ilişkili değeri her kolda bir adla bağlar ve bütün durumların ele alınmasını denetler.
- İlişkili değer durum kurulurken bir kez değerlendirilir; okuyuşlar ifadeyi yeniden koşturmaz.
- Numaralandırmadaki hesaplanan özellik sonucu saklamaz ve her okunuşta yeniden koşar.
- Ham değer derleme zamanında bilinen bir sabit olmak zorundadır; işlev çağrısı ham değer olamaz.
- Bu dersin anı: **hemen**. İlişkili değerin ifadesi durum kurulduğu satırda değerlendirilir.

#### Sonraki Adım

Bu derste bir numaralandırmadaki özelliğin her okunuşta koştuğunu gördük. Özellikler yalnız numaralandırmaya ait değildir: yapılar ve sınıflar da saklanan, hesaplanan ve gözlemleyen özellik taşır, ayrıca bir ifadenin ilk okunuşa ertelenmesini sağlayan `lazy` vardır. Bir sonraki ders bu özellik çeşitlerini ve her birinin değerini ne zaman kurduğunu ölçer.

### Özellikler

Source: https://academia.sh/tr/kurslar/swift/ozellikler

Saklanan, hesaplanan ve gözlemleyen özelliğin, lazy özelliğin ve static let değerinin ne zaman kurulduğunun ölçümü.

Önceki ders numaralandırmaların ilişkili değerinin durum kurulurken bir kez, hesaplanan özelliğinin ise her okunuşta değerlendirildiğini gösterdi. Aynı ayrım yalnız numaralandırmaya özgü değildir. Yapının, sınıfın ve numaralandırmanın **özellikleri (properties)** bir tipin verisini adlandırır ve her özellik kendi değerini kendi zamanında kurar. Bazısı örnek oluşturulurken, bazısı her okunuşta, bazısı ilk okunuşta.

Bu derste özelliğin dört çeşidi ölçülür. **Saklanan özellik (stored property)** bir değeri örneğin içinde tutar. **Hesaplanan özellik (computed property)** değer saklamaz, okunuşta bir ifade koşturur. **Gözlemleyen özellik (observed property)**, değişiminden önce ve sonra `willSet` ile `didSet` **gözlemcilerini (property observer)** çalıştıran saklanan özelliktir. **Tembel özellik (lazy property)** ilk okunuşa kadar değerini kurmaz. Kursun ölçü ekseni bu derste de aynıdır: **bu ifade ne zaman değerlendiriliyor?** Dört an vardır: hemen, ilk kullanımda, her kullanımda, hiç. `Probe` sınıfı aşağıda olduğu gibi verilir.

#### Çekirdek

Her ders aynı çekirdeği kullanır. `Probe` bir ifadenin kaç kez değerlendirildiğini (`eval`) ve kaç kez kullanıldığını (`use`) sayar; `moment` özelliği bu iki sayıdan anı çıkarır. Açılış dersindeki bağlayıcı ölçümde sekiz ifadenin dağılımı 2/1/3/2 idi: ikisi hemen, biri ilk kullanımda, üçü her kullanımda, ikisi hiç. Bu derste yalnız kendi kesitini ölçeriz.

```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)")
    }
}
```

#### Üç Temel Çeşit

İlk ölçümde saklanan, hesaplanan ve gözlemleyen özellik yan yana durur. `Rectangle` yapısındaki `width` ve `height` saklanan, `area` ise hesaplanan özelliktir: bir değer tutmaz, her okunuşta çarpmayı yapar. `Thermostat` sınıfındaki `target` saklanan bir özelliktir ve `willSet` ile `didSet` gözlemcileri taşır. `willSet` değer yazılmadan önce gelecek değeri `newValue` adıyla, `didSet` yazıldıktan sonra eski değeri `oldValue` adıyla görür.

```swift
// main.swift — saklanan, hesaplanan ve gozlemleyen ozellik
struct Rectangle {
    var width: Int
    var height: Int

    var area: Int {
        width * height
    }
}

final class Thermostat {
    var target = 20 {
        willSet { print("willSet", target, "->", newValue) }
        didSet { print("didSet", oldValue, "->", target) }
    }

    init(target: Int) {
        self.target = target
    }
}

let rect = Rectangle(width: 3, height: 4)
print(rect.area)
let thermostat = Thermostat(target: 18)
print("baslatici bitti")
thermostat.target = 22
thermostat.target = 25
```

```
12
baslatici bitti
willSet 18 -> 22
didSet 18 -> 22
willSet 22 -> 25
didSet 22 -> 25
```

İlk satır `area` özelliğinin `3 * 4` sonucunu verir. İkinci satırdan önce başlatıcı `target` alanına `18` yazmıştı, ama gözlemciler çalışmadı: başlatıcı bitene kadar bir satır görünmedi. Gözlemciler yalnızca örnek tam kurulduktan sonraki atamalarda çalışır; tipin kendi başlatıcısındaki atama onları tetiklemez. Sonraki iki atama gözlemcileri çalıştırdı ve her atama için sıra `willSet`, atama, `didSet` oldu. `willSet` çıktısındaki sağ değer gelecek değeri, `didSet` çıktısındaki sol değer eski değeri gösterir; ikincisi `target` zaten güncellenmiş olduğundan, `oldValue` adı olmadan eski değere ulaşılamazdı.

Üç çeşit üç farklı soruya yanıt verir. Saklanan özellik "değer nerede duruyor?" sorusunu yanıtlar. Hesaplanan özellik "değer başka değerlerden nasıl türer?" sorusunu yanıtlar. Gözlemleyen özellik "değer değiştiğinde ne olmalı?" sorusunu yanıtlar. Hiçbiri tembel değildir: saklanan değer kurulurken, hesaplanan değer her okunuşta, gözlemci her yazımda çalışır.

**TS18.** Bu ölçüm bir yapıdaki hesaplanan, bir sınıftaki gözlemleyen özellik için. Yapıdaki gözlemciler, hesaplanan özelliğin değişim yönü ve gözlemcilerin `inout` ile geçirilen özellikte davranışı ayrıca ölçülmedi.

#### Hesaplanan Özellik ve set

Hesaplanan özellik yalnızca okunmaz, yazılabilir de. Bunun için `get` yanına bir `set` yazılır; `set` atanan değeri `newValue` adıyla alır ve bunu saklanan özelliklere yansıtmakla yükümlüdür. `Temperature` yapısında `fahrenheit` ne bir değer saklar ne de kendisi bir kaynaktır. Okunduğunda `celsius`'tan türer, yazıldığında `celsius`'u günceller.

```swift
// main.swift — hesaplanan ozellik get ve set ile saklanan alana baglanir
struct Temperature {
    var celsius: Double

    var fahrenheit: Double {
        get { celsius * 9 / 5 + 32 }
        set { celsius = (newValue - 32) * 5 / 9 }
    }
}

var reading = Temperature(celsius: 100)
print(reading.fahrenheit)
reading.fahrenheit = 32
print(reading.celsius)
```

```
212.0
0.0
```

`celsius` `100` iken `fahrenheit` okunduğunda `212.0` çıktı. Sonra `fahrenheit`'a `32` yazıldı ve `set` bloğu `celsius`'u `0.0`'a çekti. Tek bir veri, iki ayrı gösterimle okunup yazılabiliyor ve gerçek değer tek bir yerde, `celsius` alanında duruyor. Hesaplanan özelliğin gücü budur: tutarsızlığa yol açmadan aynı bilginin iki ayrı biçimini sunar.

Önceki dersteki numaralandırma örneğinden aynı sonuç çıkar: hesaplanan özellik her okunuşta yeniden koşar. Burada fark, `set` ile geri yönde de çalışmasıdır.

#### Tembel Özellik

Bazı özellikler pahalıdır ve her örnekte gerekmez. Örnek oluşturulurken hesaplanmaları, hiç okunmayacak bir örnek için boşa gidecek iştir. `lazy` anahtar sözcüğü özelliği, ilk okunuşa kadar kurulmamış bırakır. İlk okuma ifadeyi bir kez değerlendirir, sonucu özelliğin içinde saklar ve sonraki okumalar saklı değeri verir. `Report` sınıfındaki `summary` özelliği satırların toplamını tembel olarak hesaplar.

```swift
// main.swift — lazy ozellik ilk okumada bir kez degerlendirilir
final class Report {
    let probe: Probe
    var rows = [1, 2, 3]
    lazy var summary: Int = probe.eval(rows.reduce(0, +))

    init(probe: Probe) {
        self.probe = probe
    }
}

let summaryProbe = Probe("lazy-ilk-okuma")
let report = Report(probe: summaryProbe)
_ = summaryProbe.use { report.summary }
_ = summaryProbe.use { report.summary }
summaryProbe.report()
```

```
lazy-ilk-okuma: 1 degerlendirme, 2 kullanim -> ilk-kullanimda
```

Bir değerlendirme, iki okuma, an **ilk kullanımda**. Hesaplama ilk okuyuşta yapıldı ve ikinci okuyuş saklı sonucu verdi. Bunu hesaplanan özellikten ayıran şey budur: hesaplanan özellik iki okuyuşta iki değerlendirme yapardı. `lazy` ise bir hesaplanan özellik değildir, ertelenmiş bir saklanan özelliktir. Bu yüzden `lazy` bir `var` olmak zorundadır; ilk okuma değeri özelliğe yazdığı için, yazılabilir bir saklanan özellik gerekir.

#### Varsayılan Değer ile lazy Arasındaki Fark

Önceki derste varsayılan değerin her örnekte bir kez değerlendirildiğini ölçtük. Şimdi aynı ifadeyi iki biçimde yazalım ve fark görünür olsun. `Ticket` sınıfı iki özellik taşır: `eagerId` bir varsayılan değerle, `lazyId` `lazy` ile aynı `makeId` işlevini çağırır ve işlev her çağrıldığında bir satır yazdırır. İki bilet oluşturulur, ardından yalnızca ilk biletin `lazyId` özelliği iki kez okunur.

```swift
// main.swift — varsayilan deger olusturmada, lazy ilk okumada
func makeId(_ kind: String) -> Int {
    print("uretildi", kind)
    return 7
}

final class Ticket {
    var eagerId = makeId("hemen")
    lazy var lazyId = makeId("tembel")
}

let tickets = [Ticket(), Ticket()]
print("olusturuldu")
print(tickets[0].lazyId)
print(tickets[0].lazyId)
```

```
uretildi hemen
uretildi hemen
olusturuldu
uretildi tembel
7
7
```

İlk iki satır, iki biletin `eagerId` özelliğinin `olusturuldu` yazılmadan önce kurulduğunu gösterir: her örnek kendi varsayılan değerini oluşturma anında hesapladı. `lazyId` için bu satırlar yok. `tembel` satırı ancak `tickets[0].lazyId` ilk okunduğunda göründü ve yalnız bir kez göründü; ikinci okuma `7`'yi saklı değerden verdi. İkinci biletin `lazyId` özelliği hiç okunmadığı için hiç kurulmadı.

Fark kısaca şöyle özetlenir. Varsayılan değerin ifadesi her örnek için, örnek oluşurken koşar. `lazy` ifadesi, yalnızca okunan örnekler için, ilk okunuşta koşar. Yirmi bilet oluşturulup yalnızca birinin kimliğine bakılıyorsa `lazy` yirmi yerine bir hesap yapar.

#### static let

Tipe ait değerler, yani `static` özellikler, örneklere değil tipin kendisine bağlıdır. `static let` ile tanımlanan bir değer tembeldir: ilk erişimde kurulur ve bir kez kurulur. `Config.version` bir kapanışla oluşturulur ve kapanış her çalıştığında bir satır yazdırır.

```swift
// main.swift — static let ilk erisimde kurulur
enum Config {
    static let version: String = {
        print("static let kuruldu")
        return "1.0"
    }()
}

print("once")
print(Config.version)
print(Config.version)
```

```
once
static let kuruldu
1.0
1.0
```

`once` satırı ilk erişimden önce yazıldı; `static let kuruldu` satırı ancak `Config.version` ilk okunduğunda göründü ve yalnız bir kez göründü. Sonraki okuma `1.0` değerini saklı kopyadan verdi. Bu, `lazy` ile aynı davranıştır, ama bir fark vardır: `static let` bir örneğe bağlı değildir ve `lazy` gibi `var` olmak zorunda değildir, bu yüzden değişmez bir tembel sabit olarak kullanılabilir.

**TS19.** Bu ölçüm bir kapanışla kurulan `static let` için. `static var` ve `static` hesaplanan özellikler ayrıca ölçülmedi.

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

`lazy` özellik için kurduğumuz kural şu: ifade ilk okunuşta bir kez değerlendirilir. Bu kural bir sınıf örneği için doğrudur, çünkü sınıf örneği paylaşılan tek bir nesnedir. Aynı özelliği bir yapıya taşıyalım. Yapılar atamada kopyalanır ve kopya henüz okunmamış bir `lazy` özelliğin kendi kopyasını taşır. `Sheet` yapısında `total` tembel bir özelliktir; `sheetA` kopyalanır ve iki kopya da bir kez okunur.

```swift
// main.swift — yapidaki lazy ozellik kopyayla coaltir
struct Sheet {
    let probe: Probe
    var rows = [1, 2, 3]
    lazy var total: Int = probe.eval(rows.reduce(0, +))
}

let sheetProbe = Probe("yapi-lazy-kopya")
var sheetA = Sheet(probe: sheetProbe)
var sheetB = sheetA
_ = sheetProbe.use { sheetA.total }
_ = sheetProbe.use { sheetB.total }
sheetProbe.report()
```

```
yapi-lazy-kopya: 2 degerlendirme, 2 kullanim -> her-kullanimda
```

İki okuma, iki değerlendirme, an **her kullanımda**. İki kopya da aynı `lazy` özelliği ayrı ayrı kurdu, çünkü kopyalama anında özellik henüz kurulmamıştı ve her kopya kendi kurulmamış özelliğini aldı. Aynı değer iki kez hesaplandı.

Bu sonuç dersin kuralını daraltır. "Bir kez" cümlesi, bir örnek için geçerlidir. Yapıda kopya sayısı kadar örnek vardır ve henüz okunmamış bir `lazy` özellik her kopyada bağımsız olarak ilk okunuşta kurulur. Sınıfta kopya oluşmadığı için bu durum doğmaz.

Yapıdaki `lazy` özelliğin ikinci bir sınırı vardır ve o da ölçülemez, çünkü derlenmez. Okuma, özelliğe yazdığı için yapının değiştirilebilir olmasını gerektirir. Aşağıdaki parça `let` ile tanımlanan bir yapının `lazy` özelliğini okur ve derlenmez: derleyici onu reddeder. Hata iletisi, `let` ile tanımlanan bir yapı üzerinde değiştirici okumanın kullanılamayacağını bildirdi. Parça burada yalnızca anlatım için durur.

```swift
struct Sheet2 {
    var rows = [1, 2, 3]
    lazy var total: Int = rows.reduce(0, +)
}
let fixed = Sheet2()
print(fixed.total)   // derlenmez: let yapida lazy ozellik okunamaz
```

Bu, `lazy` özelliğin bir yan sonucudur: okuma bir yazma içerir. Okuma amaçlı kullanılacak yapı örneğinin `var` olması gerekir. Sınıfta aynı kısıt yoktur, çünkü `let` sınıf adı yalnız adı sabitler, nesnenin içeriğini değil.

**TS20.** Bu ölçüm iki kopya ve tek bir `lazy` özellik için. İç içe yapılar, `lazy` özelliğin başka bir `lazy` özelliğe bağlı olduğu durumlar ve reddedilen parçada yalnızca `let` yapı denendi; başka sabitleme biçimleri denenmedi.

#### Özet

- Saklanan özellik değeri kurulurken, hesaplanan özellik her okunuşta, gözlemci her yazımda çalışır; tipin kendi başlatıcısındaki atama gözlemcileri tetiklemez.
- Hesaplanan özellik `set` ile yazılabilir ve gerçek değeri başka bir saklanan özelliğe yansıtır.
- `lazy` özellik ilk okunuşta bir kez değerlendirilir; varsayılan değer ise her örnekte oluşturma sırasında koşar.
- `static let` ilk erişimde kurulur ve bir kez kurulur.
- Yapıdaki henüz okunmamış `lazy` özellik kopyayla birlikte çoğalır; `let` yapıda okunamaz.
- Bu dersin anı: **ilk kullanımda**. `lazy` özellik ilk okunuşta bir kez değerlendirilir.

#### Sonraki Adım

Bu derste özelliklerin değerini ne zaman kurduğunu ölçtük. Tipler yalnız veri taşımaz, aynı zamanda davranış sözü de verebilir. Bir sonraki ders protokolleri ölçer: bir tipin hangi özellikleri ve yöntemleri sağlaması gerektiğini bildiren sözleşmeleri, ve sözleşmeyi kullanan koddan çağrıldığında özelliğin hangi anda değerlendirildiğini.

### Uzantılar

Source: https://academia.sh/tr/kurslar/swift/uzantilar

Var olan bir tipe dışarıdan yöntem, hesaplanan özellik ve protokol uyumu eklemeyi ölçer; saklanan özelliğin eklenemeyeceğini ve tip düzeyindeki istisnayı gösterir.

Önceki derste protokolün uzantısı, uyan her tipe bir varsayılan gövde verdi ve o gövde her okunuşta yeniden koştu. Aynı mekanizmanın somut hâli, bir tipi dışarıdan genişletmektir. `Int` ya da `String` gibi standart bir tipin kaynağına dokunamazsın, ama onun üzerine yeni bir yöntem yazabilirsin. Bu ders, uzantıyla eklenen şeyin ne zaman değerlendirildiğini soruyor.

**Uzantı (extension)**, var olan bir tipe, onun tanımını değiştirmeden yeni davranış ekler. Eklenebilenler: yöntemler, hesaplanan özellikler, alt simgeler, statik üyeler, başlatıcılar, iç içe tipler ve protokol uyumu. Eklenemeyen bir şey var, ve bu dersin ağırlığı onda: saklanan örnek özellik.

#### Ölçüm Düzeneği

`Probe`, her derste aynı biçimde ilk blokta durur.

```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)")
    }
}
```

**TS27.** Uzantının gövdesi `Probe`'a parametre ya da eklenen tipin alanı olarak ulaşır; ölçümlerde bu iki yoldan biri seçildi, uzantıya küresel bir `Probe` bağlamak denenmedi.

#### Eklenen Her Şey Hesaplanandır

Aşağıdaki blok üç tipe dört ekleme yapar. `Int`'e bir yöntem ve bir `mutating` yöntem, `String`'e bir yöntem eklenir. Kendi `Meter` tipimize ise bir hesaplanan özellik ve bir protokol uyumu eklenir: `CustomStringConvertible` uyumu, `description` özelliğini istediği için tipe bir metin gösterimi kazandırır.

```swift
// main.swift — uzantiyla eklenen her sey her kullanimda kosar
struct Meter {
    let value: Int
    let tracker: Probe
}

extension Int {
    func cubed(tracking tracker: Probe) -> Int { tracker.eval(self * self * self) }
    mutating func triple() { self *= 3 }
}

extension String {
    func padded(to width: Int, tracking tracker: Probe) -> String {
        tracker.eval(self + String(repeating: ".", count: max(0, width - count)))
    }
}

extension Meter {
    var centimeters: Int { tracker.eval(value * 100) }
}

extension Meter: CustomStringConvertible {
    var description: String { tracker.eval("\(value) m") }
}

let intMethod = Probe("int-yontemi")
let stringMethod = Probe("string-yontemi")
let computedExtra = Probe("eklenen-hesaplanan-ozellik")
let conformance = Probe("eklenen-uyum")

_ = intMethod.use { 3.cubed(tracking: intMethod) }
_ = intMethod.use { 4.cubed(tracking: intMethod) }

_ = stringMethod.use { "ab".padded(to: 5, tracking: stringMethod) }
_ = stringMethod.use { "ab".padded(to: 5, tracking: stringMethod) }

let meter = Meter(value: 7, tracker: computedExtra)
_ = computedExtra.use { meter.centimeters }
_ = computedExtra.use { meter.centimeters }

let described = Meter(value: 7, tracker: conformance)
_ = conformance.use { "\(described)" }
_ = conformance.use { "\(described)" }

var counter = 2
counter.triple()
counter.triple()
print(counter)

let all = [intMethod, stringMethod, computedExtra, conformance]
for probe in all { probe.report() }
tally(all)
```

```
18
int-yontemi: 2 degerlendirme, 2 kullanim -> her-kullanimda
string-yontemi: 2 degerlendirme, 2 kullanim -> her-kullanimda
eklenen-hesaplanan-ozellik: 2 degerlendirme, 2 kullanim -> her-kullanimda
eklenen-uyum: 2 degerlendirme, 2 kullanim -> her-kullanimda
hemen: 0
ilk-kullanimda: 0
her-kullanimda: 4
hic: 0
```

Dört eklemenin dördü de `her-kullanimda` çıktı. Yöntem her çağrıda koşar; bunu beklersin. Asıl dikkat edilecek olan `centimeters` özelliği: tip üzerinde bir alan gibi okunur ama saklanmaz, her okunuşta yeniden hesaplanır. `CustomStringConvertible` uyumu da aynı: `description` her metin enterpolasyonunda yeniden kurulur. `mutating` yöntem ise bir değeri yerinde değiştirir; `counter` iki üçleme sonunda 18 oldu. Bu değişiklik bir ifadenin değerlendirilmesi değil, bir değerin güncellenmesi olduğu için ayrı bir `Probe` gerektirmedi.

#### Saklanan Özellik Eklenemez

Uzantıya bir saklanan örnek özellik yazmak istesen derleyici seni durdurur. Önce bunu doğruladık: aşağıdaki parça Swift 6.4'te derlenmedi, bu yüzden çıktısız kalıyor.

```swift
extension Meter {
    var unit = "m"
}
```

Gerekçe, bellek düzeninde. Bir tipin örneği, tanımındaki saklanan özelliklerin toplamı kadar yer kaplar; bu boyut tip tanımlandığında sabitlenir. Dışarıdan eklenen bir alan, bu boyutu tanımın dışında değiştirmek zorunda kalırdı; dilin kuralı bu yüzden saklanan örnek özelliği uzantıya kapatır. Bu yüzden uzantıyla eklediğin her özellik, tanım gereği, hesaplanan bir özelliktir: değer bir yerde durmaz, istendiğinde üretilir. Önceki bloğun hepsinin `her-kullanimda` çıkmasının nedeni budur. **TS28.** Bu bölümdeki red yalnız örnek düzeyindeki saklama için doğrulandı; `lazy` ya da gözlemleyen özelliği uzantıya eklemeyi ayrıca denemedik.

#### Kısıtlı Uzantı

Bir uzantı her tipe değil, yalnız belirli bir koşulu sağlayan tiplere eklenebilir. `extension Array where Element: Numeric` yazdığında eklenen üyeler yalnız elemanları sayısal olan dizilerde görünür. **Kısıtlı uzantı (constrained extension)**, 06. derste protokol uzantısı için gördüğümüz `where` koşulunun tipler için olan hâlidir. Kısıt, eklemenin nereye uygulanacağını daraltır; eklenen gövdenin ne zaman koşacağını değiştirmez. Bunu aynı yöntemi iki farklı dizi tipinde çağırarak ölçüyoruz.

```swift
// main.swift — kisitli uzanti yalniz kosulu saglayan dizilere eklenir
extension Array where Element: Numeric {
    func total(tracking tracker: Probe) -> Element {
        tracker.eval(reduce(0, +))
    }
}

let intTotal = Probe("int-dizisi")
let doubleTotal = Probe("double-dizisi")

let counts = [1, 2, 3]
let weights = [0.5, 1.5]

_ = intTotal.use { counts.total(tracking: intTotal) }
_ = intTotal.use { counts.total(tracking: intTotal) }
_ = doubleTotal.use { weights.total(tracking: doubleTotal) }
_ = doubleTotal.use { weights.total(tracking: doubleTotal) }

let all = [intTotal, doubleTotal]
for probe in all { probe.report() }
```

```
int-dizisi: 2 degerlendirme, 2 kullanim -> her-kullanimda
double-dizisi: 2 degerlendirme, 2 kullanim -> her-kullanimda
```

Aynı gövde `Int` ve `Double` dizilerinde ayrı ayrı çalıştı ve ikisinde de her çağrıda koştu. Kısıt, `total` yönteminin bir `[String]` üzerinde görünmemesini sağlar; bu, derleme zamanında verilen bir karardır. Koşumda gördüğümüz tek şey, kısıtlı uzantının yöntemi sıradan bir yöntem gibi davrandığıdır: sonuç hiçbir yerde saklanmaz. **TS29.** Kısıtlı uzantının `[String]` üzerinde görünmediği ayrıca doğrulanmadı; burada yalnız kısıtı sağlayan iki tip ölçüldü.

#### Uzantıda Başlatıcı ve İç İçe Tip

Uzantı bir tipe başlatıcı da ekleyebilir. Yapılarda bunun özel bir faydası vardır: başlatıcıyı tanım gövdesine yazarsan derleyicinin ürettiği üye bazlı başlatıcı (memberwise initializer) kaybolur; uzantıya yazarsan ikisi birlikte kalır. Uzantı ayrıca **iç içe tip (nested type)** ekleyebilir: başka bir tipin içinde tanımlanan, ona ait bir `enum`, `struct` ya da `class`. Aşağıdaki blok ikisini birlikte kullanır. Başlatıcı bir `Probe` değerlendirmesini kurulum anında yapar; iç içe tip ise çağrıldıkça koşan bir yöntem taşır.

```swift
// main.swift — uzantidaki baslatici ve ic ice tip
struct Length {
    let meters: Int
}

extension Length {
    init(centimeters: Int, tracking tracker: Probe) {
        self.init(meters: tracker.eval(centimeters / 100))
    }

    enum Unit {
        case meter, kilometer

        func factor(tracking tracker: Probe) -> Int {
            switch self {
            case .meter: tracker.eval(1)
            case .kilometer: tracker.eval(1000)
            }
        }
    }
}

let initProbe = Probe("uzantidaki-baslatici")
let nestedProbe = Probe("ic-ice-tip")

let direct = Length(meters: 5)
let converted = Length(centimeters: 700, tracking: initProbe)
_ = initProbe.use { converted.meters }
_ = initProbe.use { converted.meters }
print(direct.meters)

_ = nestedProbe.use { Length.Unit.kilometer.factor(tracking: nestedProbe) }
_ = nestedProbe.use { Length.Unit.kilometer.factor(tracking: nestedProbe) }

let all = [initProbe, nestedProbe]
for probe in all { probe.report() }
```

```
5
uzantidaki-baslatici: 1 degerlendirme, 2 kullanim -> hemen
ic-ice-tip: 2 degerlendirme, 2 kullanim -> her-kullanimda
```

`Length(meters:)` üye bazlı başlatıcısı, uzantıdaki başlatıcıya rağmen kullanılabilir kaldı; ikisi yan yana durdu. Uzantıdaki başlatıcıda değerlendirme kurulum anında, bir kez yapıldı ve sonuç `meters` alanında saklandı; alanı iki kez okumak yeni bir değerlendirme üretmedi: `hemen`. Bu, eklenen özelliklerin her okunuşta koşmasıyla bir çelişki değildir. Başlatıcı bir özellik eklemez, var olan saklanan alanı doldurur; saklama tanımdaki `meters` alanındadır. İç içe tipin yöntemi ise sıradan bir yöntem gibi her çağrıda koştu: `her-kullanimda`. **TS30.** İç içe tip burada bir `enum` olarak ölçüldü; uzantıdan eklenen iç içe `struct` ya da `class` ayrıca denenmedi.

#### Uzantıda Alt Simge

Uzantı yalnız yöntem ve hesaplanan özellik değil, **alt simge (subscript)** de ekleyebilir. Alt simge, bir değeri `değer[indeks]` biçiminde okumayı sağlayan üyedir ve gövdesi bir hesaplanan özelliğin okuyucusu gibi çalışır. Aşağıda `Array`'e sınır dışı indekste çökmeyen bir okuma ekliyoruz. Alt simge fazladan parametre alabilir; ölçüm düzeneğimizi buradan geçiriyoruz.

```swift
// main.swift — uzantidaki alt simge her okumada kosar
extension Array {
    subscript(safe index: Int, tracking tracker: Probe) -> Element? {
        let found: Element? = indices.contains(index) ? self[index] : nil
        return tracker.eval(found)
    }
}

let subscriptProbe = Probe("alt-simge")
let names = ["a", "b", "c"]

_ = subscriptProbe.use { names[safe: 1, tracking: subscriptProbe] }
_ = subscriptProbe.use { names[safe: 9, tracking: subscriptProbe] }

subscriptProbe.report()
```

```
alt-simge: 2 degerlendirme, 2 kullanim -> her-kullanimda
```

İki okuma iki değerlendirme verdi; sınır dışı indeks de gövdeyi çalıştırdı ve yalnız sonuç `nil` oldu. Alt simge, bir özellik gibi görünse de saklama yapmaz: her `[...]` ifadesi gövdeyi yeniden koşturur, `her-kullanimda`. Bu, "Eklenen Her Şey Hesaplanandır" bölümündeki kuralın alt simgeye de uzandığını gösterir. **TS31.** Burada yalnız okuma yapan bir alt simge ölçüldü; yazan (`set`) alt simge denenmedi.

#### Uzantıda Değiştiren Yöntem

Değer tiplerinde (yapı ve enum) bir yöntem, `self`'i değiştiriyorsa `mutating` ile işaretlenir. Uzantıdaki yöntem de aynı kurala uyar; bir uzantı, tanımda olmayan bir `mutating` yöntem ekleyebilir. Değiştirilen şey tanımdaki saklanan alandır, yani uzantı saklama eklemeden var olan saklamayı kullanır.

```swift
// main.swift — uzantidaki mutating yontem
struct Counter {
    var count = 0
}

extension Counter {
    mutating func bump(tracking tracker: Probe) {
        count += tracker.eval(1)
    }
}

let bumpProbe = Probe("mutating-yontem")
var counter = Counter()

bumpProbe.use { counter.bump(tracking: bumpProbe) }
bumpProbe.use { counter.bump(tracking: bumpProbe) }

print(counter.count)
bumpProbe.report()
```

```
2
mutating-yontem: 2 degerlendirme, 2 kullanim -> her-kullanimda
```

Alan iki çağrıda iki artış aldı ve değer `counter.count` içinde kaldı; uzantı yöntemin kendisini ekledi, saklamayı eklemedi. Gövde her çağrıda yeniden koştu: `her-kullanimda`. Önceki bölümlerle birleştirince tablo şöyle çıkar: uzantı ne eklerse eklesin, ister özellik, ister alt simge, ister yöntem olsun, eklenen gövde her kullanımda koşar. Başlatıcı bu tablonun dışında kalır, çünkü o bir okuma değil bir kurulum adımıdır. **TS32.** `mutating` yöntem yalnız yapı üzerinde ölçüldü; sınıf ya da enum için ayrıca doğrulanmadı.

#### Uzantıda Statik Üye

Tipe ait, bir değerle değil tipin kendisiyle çağrılan üyeler **statik üye**dir (`static`). Uzantı bunları da ekleyebilir. Özellikler dersinde bir `static let` gördük ve o, ilk erişimde bir kez kuruluyordu. Statik bir yöntem ise sıradan bir yöntemdir; ayrım, yöntemle özellik arasındaki ayrımdır. Aşağıdaki uzantı `Length`'e tipin kendisinden çağrılan bir üretici ekler ve ölçer.

```swift
// main.swift — uzantidaki static yontem
struct Length {
    let meters: Int
}

extension Length {
    static func fromKilometers(_ kilometers: Int, tracking tracker: Probe) -> Length {
        Length(meters: tracker.eval(kilometers * 1000))
    }
}

let staticProbe = Probe("static-yontem")
_ = staticProbe.use { Length.fromKilometers(2, tracking: staticProbe) }
_ = staticProbe.use { Length.fromKilometers(3, tracking: staticProbe) }

staticProbe.report()
```

```
static-yontem: 2 degerlendirme, 2 kullanim -> her-kullanimda
```

Statik yöntem her çağrıda koştu: `her-kullanimda`. Bir tipe ait olması gövdesini önbelleğe almaz; bu ölçümde önbellekleme yalnız `static let` gibi saklanan bir statik özellikte görüldü. Demek ki uzantıya ne yazdığın, hesaplanan mı saklanan mı olduğuna bağlıdır, ve uzantı saklanan özelliği zaten yasaklar. Statik saklanan özellik bu yasağa takılmaz, çünkü saklama örnekte değil tipin kendisindedir. **TS33.** Statik yöntemin sonucu burada her seferinde yeni bir `Length` üretti; bir örnek eşitliği ya da kimliği ölçülmedi.

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

Yasak yalnız örnek düzeyindedir. Uzantı, tipe bir `static let` ekleyebilir, çünkü tip başına tek bir kopya vardır ve bellek düzenini etkilemez. Bu özelliğin ne zaman kurulduğunu `print` ile gözlüyoruz. `Probe` burada kullanılamaz: `Probe` `Sendable` değildir ve Swift 6 dil kipinde küresel ya da tip düzeyindeki `let` özellik `Sendable` bir tip ister. Kurulumun içindeki `print` satırı, kurulumun kaç kez ve hangi sırada koştuğunu gösteren sayaçtır.

```swift
// main.swift — uzantidaki static let ilk erisimde bir kez kurulur
struct Scale {
    let factor: Int
}

extension Scale {
    static let unit: Int = {
        print("unit kuruldu")
        return 100
    }()
}

print("erisimden once")
print(Scale.unit)
print(Scale(factor: 3).factor * Scale.unit)
print(Scale.unit)
```

```
erisimden once
unit kuruldu
100
300
100
```

`unit kuruldu` satırı bir kez çıktı ve ilk erişimin hemen önünde. Eklenen `static let`, tip düzeyinde `ilk-kullanimda` anını verir: ilk okumaya kadar bekler, bir kez kurulur, sonra saklanır. Böylece uzantıda örnek düzeyinde yasak olan saklama, tip düzeyinde izinli ve tembeldir. Eklenen örnek özellikler `her-kullanimda`, eklenen tip özelliği `ilk-kullanimda` ölçüldü; ikisi aynı uzantıda yan yana durabilir. **TS34.** Bu gözlem `print` sırasına dayanır; ilk erişimin eşzamanlı iki yerden gelmesi durumunda kurulumun bir kez koştuğu burada ölçülmedi.

#### Özet

- Uzantı, var olan bir tipe tanımını değiştirmeden yöntem, hesaplanan özellik, başlatıcı ve protokol uyumu ekler.
- `Int` ve `String` gibi standart tiplere de yöntem eklenir; `mutating` yöntem değeri yerinde günceller.
- Uzantı saklanan örnek özellik ekleyemez; eklenen her özellik hesaplanandır ve her okunuşta yeniden koşar.
- Uzantıdaki `static let` istisnadır: tip düzeyinde, ilk erişimde bir kez kurulur.
- Bu dersin anı: **her kullanımda** (uzantıyla eklenen hesaplanan özellik ve yöntem).

#### Sonraki Adım

Uzantı bir tipe davranış ekledi, ama eklenen davranış tek bir tipe bağlıydı. `Int` için yazdığın `cubed`, `Double` için yeniden yazılmak zorunda. Sıradaki ders, aynı gövdeyi birçok tip için bir kez yazmayı sağlayan genelliklere geçiyor.

### Genellikler

Source: https://academia.sh/tr/kurslar/swift/genellikler

Tip parametreleri ve kısıtlarla tek bir gövdeyi birçok tipe uygular; tembel dizinin dönüştürücüsünü her erişimde, istekli map'inkini hemen ölçer.

Önceki derste `Int` için yazdığımız `cubed`, `Double` için yeniden yazılmak zorundaydı. Aynı gövdeyi bir kez yazıp birçok tipe uygulamak, genelliklerin işidir. **Genellik (generics)**, bir fonksiyonu ya da tipi, henüz belli olmayan bir tip için yazmayı sağlar; tip, kullanım anında belli olur. Bu derste iki davranışı ölçüyoruz: genel bir gövdenin her çağrıda nasıl koştuğunu ve standart kitaplığın tembel dizilerinin dönüştürücüyü ne zaman çağırdığını.

**Tip parametresi (type parameter)**, açılı parantez içinde yazılan yer tutucudur: `<T>`. **Kısıt (constraint)**, o yer tutucunun taşıması gereken bir protokolü söyler: `<T: Comparable>` yalnız karşılaştırılabilen tipleri kabul eder. Kısıt olmadan gövde `T` hakkında hiçbir şey bilmez; `<` işlecini kullanamaz bile.

#### Ölçüm Düzeneği

`Probe` her derste aynı biçimde ilk bloktur. Bu derste ayrıca genel bir yapı ve ilişkili tipli bir protokol, ayrı bir yardımcı dosyada durur.

```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)")
    }
}
```

**TS35.** Her gözlem, `hic` dışında, iki kullanım taşır; dönüştürücüyü tek elemanlı dizilere bağlayarak eleman sayısının değerlendirme sayısını şişirmesini önlüyoruz.

```swift
// Stack.swift — genel yigit ve iliskili tipli protokol
protocol Container {
    associatedtype Item
    var count: Int { get }
    mutating func append(_ item: Item)
}

struct Stack<Element>: Container {
    private(set) var items: [Element] = []
    var count: Int { items.count }
    mutating func append(_ item: Element) { items.append(item) }
    mutating func pop() -> Element? { items.popLast() }
}
```

`Container`, bir **ilişkili tip (associated type)** taşır: `Item`. Protokol hangi tipin saklanacağını söylemez, yalnız böyle bir tipin bulunacağını söyler; uyan tip belirler. `Stack<Element>` ise bir **genel yapı**dır: `Element` yer tutucusu, `Stack<Int>` ya da `Stack<String>` yazıldığında gerçek bir tipe dönüşür ve `Container`'ın `Item` tipi `Element` olarak çıkarılır.

#### Genel Gövde Her Çağrıda Koşar

Aşağıdaki blok üç şeyi yan yana koyar. `largest(_:tracking:)`, `Comparable` kısıtlı genel bir fonksiyondur ve iki farklı tipte çağrılır. `describe`, ilişkili tipe `where` ile kısıt koyar. Son iki gözlem aynı dönüşümün iki biçimidir: istekli `map` ve `lazy.map`.

```swift
// main.swift — genel fonksiyon, genel yapi, istekli ve tembel map
func largest<T: Comparable>(_ values: [T], tracking tracker: Probe) -> T? {
    tracker.eval(values.max())
}

func describe<C: Container>(_ box: C) -> Int where C.Item: Comparable {
    box.count
}

let generic = Probe("genel-fonksiyon")
let eager = Probe("istekli-map")
let lazyMap = Probe("lazy-map")

_ = generic.use { largest([3, 9, 4], tracking: generic) }
_ = generic.use { largest(["b", "a"], tracking: generic) }

var stack = Stack<Int>()
stack.append(5)
stack.append(8)
print(describe(stack))
print(stack.pop() ?? -1)

let eagerDoubled = [4].map { eager.eval($0 * 2) }
_ = eager.use { eagerDoubled[0] }
_ = eager.use { eagerDoubled[0] }

let lazyDoubled = [4].lazy.map { lazyMap.eval($0 * 2) }
_ = lazyMap.use { lazyDoubled[0] }
_ = lazyMap.use { lazyDoubled[0] }

let all = [generic, eager, lazyMap]
for probe in all { probe.report() }
tally(all)
```

```
2
8
genel-fonksiyon: 2 degerlendirme, 2 kullanim -> her-kullanimda
istekli-map: 1 degerlendirme, 2 kullanim -> hemen
lazy-map: 2 degerlendirme, 2 kullanim -> her-kullanimda
hemen: 1
ilk-kullanimda: 0
her-kullanimda: 2
hic: 0
```

Genel fonksiyon her çağrıda koştu: `largest` iki çağrıda iki kez değerlendirildi, ve çağrılar farklı tiplerle yapıldı. Genel bir gövde tek bir yazılıştır ama her tip için ayrı bir çağrıdır; değerlendirme anı tip parametresine bağlı değildir. `describe` ve `Stack` yalnız tip düzeyinde çalıştı: `where C.Item: Comparable` kısıtı, `Stack<Int>`'in `Item`'ı `Int` olduğu için sağlandı, ve derleyici bunu çağrı anında denetledi.

Asıl ayrım son iki satırda. İstekli `map`, dizinin elemanlarını hemen dönüştürüp yeni bir dizi kurar; dönüştürücü `eagerDoubled` kurulurken bir kez koştu ve iki okuma yeniden hesaplamadı, `hemen`. `lazy.map` ise dönüştürücüyü saklayan bir **tembel sarmalayıcı** döndürür; dizi hiçbir şey hesaplamadan kurulur ve her erişimde dönüştürücü yeniden çağrılır. Aynı elemana iki erişim iki değerlendirme verdi: `her-kullanimda`. **TS36.** Bu sonuç dizi tabanlı tembel koleksiyon için ölçüldü; tembel bir `Sequence`'in yeniden dolaşılabilir olduğu varsayılmadı.

Tembel `map` hesaplanan özellik gibi davranır, saklanan özellik gibi değil: sonuç hiçbir yerde durmaz. Hesabı bir kez yapıp saklamak istersen dizi sarmalayıcıdan bir dizi türetirsin, ki bu istekli bir adımdır.

#### `where` Kısıtı İki Tipi Bağlar

Tek bir tip parametresine koyulan kısıt (`T: Comparable`) bir protokolü zorunlu kılar. **`where` yan tümcesi** ise daha fazlasını söyleyebilir: iki tip parametresinin ilişkili tiplerinin aynı olmasını ya da bir ilişkili tipin bir protokole uymasını isteyebilir. Aşağıdaki fonksiyon iki kapsayıcıyı alır ve yalnız eleman tipleri aynıysa karşılaştırır: `A.Item == B.Item`. Bu bir derleme zamanı denetimidir; ölçümde gövdenin değerlendirme anının değişmediğini görüyoruz.

```swift
// main.swift — where ile iki kapsayicinin eleman tipini baglamak
func sameCount<A: Container, B: Container>(_ left: A, _ right: B, tracking tracker: Probe) -> Bool
    where A.Item == B.Item {
    tracker.eval(left.count == right.count)
}

let whereProbe = Probe("where-kisiti")

var first = Stack<Int>()
first.append(1)
var second = Stack<Int>()
second.append(2)
second.append(3)

_ = whereProbe.use { sameCount(first, second, tracking: whereProbe) }
_ = whereProbe.use { sameCount(first, first, tracking: whereProbe) }

whereProbe.report()
```

```
where-kisiti: 2 degerlendirme, 2 kullanim -> her-kullanimda
```

Gövde iki çağrıda iki kez koştu: `her-kullanimda`. `where` yan tümcesi, çağrının geçerli olup olmadığını belirler; geçerli bir çağrının maliyetine ya da zamanına karışmaz. İki yığıtın eleman tipinin aynı olması burada derleyicinin çıkarımıdır ve ölçümde iz bırakmaz. Farklı eleman tipli iki yığıtın bu fonksiyona verilemeyeceğini ayrıca denemedik; kısıtın ne yaptığını gövdenin davranışından değil, imzanın okunuşundan anlıyoruz. **TS37.** Kısıtın reddedilmesi bu derste ölçülmedi.

#### Genel Yapıya Kısıtlı Uzantı

07. derste `Array`'e kısıtlı uzantı eklemiştik. Aynı şey kendi genel tipimiz için de geçerlidir: `Stack` her eleman tipiyle kullanılabilir, ama karşılaştırma isteyen bir yöntem yalnız `Element: Comparable` olan yığıtlarda anlamlıdır. Kısıtlı uzantı, yöntemi o durumla sınırlar. Aşağıdaki uzantı en büyük elemanı bulur.

```swift
// main.swift — Stack icin kisitli uzanti
extension Stack where Element: Comparable {
    func largest(tracking tracker: Probe) -> Element? {
        tracker.eval(items.max())
    }
}

let constrained = Probe("kisitli-uzanti")

var numbers = Stack<Int>()
numbers.append(4)
numbers.append(9)

_ = constrained.use { numbers.largest(tracking: constrained) }
_ = constrained.use { numbers.largest(tracking: constrained) }

constrained.report()
```

```
kisitli-uzanti: 2 degerlendirme, 2 kullanim -> her-kullanimda
```

`largest` her çağrıda yığıtı yeniden tarar: `her-kullanimda`. Yığıtın içinde bir önbellek yok; uzantı, 07. dersteki kuralın aynısına tabidir ve saklanan bir alan ekleyemez. Genel tiplerin avantajı burada şudur: `Stack<Int>` için `largest` var, `Stack<Counter>` gibi karşılaştırılamayan bir eleman tipi için ise kısıtlı uzantının yöntemi sunulmaz. Genel yapı bir kez yazılır, yöntem kümesi ise eleman tipine göre değişir. **TS38.** Ölçüm yalnız `Int` elemanlı yığıtda yapıldı; `Comparable` olmayan elemanla yöntemin görünmediği ayrıca doğrulanmadı.

#### `some` ile Opak Dönüş Tipi

Bir genel fonksiyon, dönüş tipini çağıranın seçmesine bırakmak yerine kendisi seçebilir ve bunu gizleyebilir. **Opak dönüş tipi (opaque return type)**, `some Sequence<Int>` gibi yazılır: fonksiyon belirli bir tip döndürür, ama imza yalnız "bu protokole uyan bir şey" der. Çağıran gerçek tipi bilmez, derleyici bilir. Burada soru, opak dönüşün değerlendirme anını değiştirip değiştirmediğidir. İki fonksiyon aynı dönüşümü yapar; biri `lazy` dizi, öteki istekli dizi döndürür, ikisi de `some Sequence<Int>` olarak.

```swift
// main.swift — some ile opak donus: tembel ve istekli
func lazySquares(of values: [Int], tracking tracker: Probe) -> some Sequence<Int> {
    values.lazy.map { tracker.eval($0 * $0) }
}

func eagerSquares(of values: [Int], tracking tracker: Probe) -> some Sequence<Int> {
    values.map { tracker.eval($0 * $0) }
}

let lazyOpaque = Probe("opak-tembel")
let eagerOpaque = Probe("opak-istekli")

let lazyResult = lazySquares(of: [3], tracking: lazyOpaque)
let eagerResult = eagerSquares(of: [3], tracking: eagerOpaque)

_ = lazyOpaque.use { Array(lazyResult) }
_ = lazyOpaque.use { Array(lazyResult) }
_ = eagerOpaque.use { Array(eagerResult) }
_ = eagerOpaque.use { Array(eagerResult) }

let all = [lazyOpaque, eagerOpaque]
for probe in all { probe.report() }
```

```
opak-tembel: 2 degerlendirme, 2 kullanim -> her-kullanimda
opak-istekli: 1 degerlendirme, 2 kullanim -> hemen
```

İmzaları aynı olan iki fonksiyon, farklı anlar verdi. Tembel olan, her tüketimde yeniden hesapladı: `her-kullanimda`. İstekli olan, çağrı sırasında bir kez hesapladı ve iki tüketim bunu tekrar etmedi: `hemen`. `some`, gerçek tipi gizler ama davranışı gizlemez; dönen şeyin tembel mi istekli mi olduğu imzada yazmaz, ölçümde görünür. Bu yüzden opak dönüş tipi taşıyan bir API'de tüketimin maliyetini belgede ayrıca söylemek yararlıdır. **TS39.** İki fonksiyon tek elemanlı girdiyle ölçüldü; çok elemanlı girdideki değerlendirme sayısı burada kasıtlı olarak dışarıda bırakıldı.

#### `@autoclosure` Parametreli Genel Fonksiyon

Standart kitaplığın `??` işleci, sağ tarafı yalnız sol taraf `nil` ise değerlendirir. Bu, **`@autoclosure`** ile yapılır: parametre bir kapanışa sarılır, ama çağıran sıradan bir ifade yazar. Kendi genel fonksiyonumuzda aynı kalıbı kuruyoruz: `orElse` parametresi yalnız gerektiğinde çağrılır.

```swift
// main.swift — @autoclosure ile genel bir varsayilan deger
func unwrap<T>(_ value: T?, orElse backup: @autoclosure () -> T) -> T {
    if let value { return value }
    return backup()
}

let present = Probe("deger-var")
let absent = Probe("deger-yok")

let known: Int? = 7
let missing: Int? = nil

_ = present.use { unwrap(known, orElse: present.eval(0)) }
_ = present.use { unwrap(known, orElse: present.eval(0)) }
_ = absent.use { unwrap(missing, orElse: absent.eval(0)) }
_ = absent.use { unwrap(missing, orElse: absent.eval(0)) }

let all = [present, absent]
for probe in all { probe.report() }
tally(all)
```

```
deger-var: 0 degerlendirme, 2 kullanim -> hic
deger-yok: 2 degerlendirme, 2 kullanim -> her-kullanimda
hemen: 0
ilk-kullanimda: 0
her-kullanimda: 1
hic: 1
```

Değer varken yedek ifade hiç değerlendirilmedi: `hic`. Değer yokken her çağrıda değerlendirildi: `her-kullanimda`. Genel fonksiyon `T` hakkında hiçbir şey bilmez, ama kapanışın çağrılıp çağrılmayacağına kendi gövdesi karar verir; ertelenen şey tipten bağımsızdır. Bu, dersin ana temasıyla uyumludur: genel bir gövde her çağrıda koşar, ve gövdenin içindeki bir ifade bile koşmayabilir. **TS40.** Ölçüm `Int` ile yapıldı; ertelenen ifadenin yan etkisi olan tiplerde de aynı kalacağı varsayılmadı.

#### Genel Yapı Elemanı Saklar

Genel bir yapının içine koyduğun değer, tip parametresi ne olursa olsun, ekleme anında değerlendirilir ve yapı o değeri tutar. `Stack<Int>`'e `append` ile verilen ifade, `append` çağrılmadan önce çağıran tarafta hesaplanır; yığıt yalnız sonucu saklar. Bunu ölçüyoruz, çünkü genel bir kapsayıcının "tembel" ya da "hesaplanan" davranmadığını, yalnız taşıdığını görmek önemlidir.

```swift
// main.swift — genel yapi eklenen degeri saklar
let element = Probe("yigin-elemani")

var holder = Stack<Int>()
holder.append(element.eval(5))

_ = element.use { holder.count }
_ = element.use { holder.count }
_ = element.use { holder.pop() }

element.report()
```

```
yigin-elemani: 1 degerlendirme, 3 kullanim -> hemen
```

İfade bir kez, ekleme satırında değerlendirildi; yığıtı okumak ya da elemanı çekmek onu yeniden hesaplamadı: `hemen`. Genel yapı bu bakımdan bir saklanan özellik gibi davranır. Hesaplanan davranış istiyorsan, yapıya bir değer yerine bir kapanış verirsin, ve bu durumda saklanan şey kapanıştır. **TS41.** Tek bir ekleme ve `Int` ile ölçüldü; yığıtın dolup yeniden boyutlandığı durumlar bu ölçümün dışındadır.

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

Tembel dizinin eleman başına bir dönüştürücü çağrısı vardır, ve çağrı yalnız okunan elemana yapılır. Aşağıdaki dizi iki elemanlıdır. Dönüştürücü, ilk eleman için bir `Probe`'u, ikinci eleman için ötekini kullanır. Dizi yalnız `first` ile okunur.

```swift
// main.swift — ayni tembel dizi, hem her kullanimda hem hic
let hot = Probe("okunan-eleman")
let cold = Probe("okunmayan-eleman")

let pipeline = [10, 20].lazy.map { value -> Int in
    value == 10 ? hot.eval(value + 1) : cold.eval(value + 1)
}

_ = hot.use { pipeline.first }
_ = hot.use { pipeline.first }
_ = cold.use { pipeline.first }
_ = cold.use { pipeline.first }

hot.report()
cold.report()
```

```
okunan-eleman: 4 degerlendirme, 2 kullanim -> her-kullanimda
okunmayan-eleman: 0 degerlendirme, 2 kullanim -> hic
```

Tek bir tembel dizi iki ayrı an taşıdı. İlk eleman her `first` çağrısında yeniden dönüştürüldü. Blokta dört `first` çağrısı var ve dördü de ilk elemanı okur; `hot` yalnız kendi iki kullanımını saydı, ama dört değerlendirmenin hepsini gördü: `her-kullanimda`. İkinci elemana kimse uzanmadı, dönüştürücü ona hiç koşmadı: `hic`, hem de iki kullanıma karşın. Tembel dizi, istekli `map`'in tersini yapar: istekli dizi tüm elemanları hemen hesaplar, tembel dizi hiçbirini önceden hesaplamaz. **TS42.** `first` yalnız ilk elemanı değerlendirdi; bu, koleksiyonun `first` gerçekleştirimine bağlı bir sonuçtur ve Swift 6.4'te ölçüldü.

#### Özet

- Genellik, bir gövdeyi tip parametresiyle bir kez yazıp birçok tipte kullanmayı sağlar; kısıt, tipin taşıması gereken protokolü söyler.
- İlişkili tip, protokolün bir tipe ihtiyaç duyduğunu ama onu uyan tipin belirleyeceğini bildirir; `where` ile bu tipe kısıt konur.
- İstekli `map` dönüştürücüyü hemen çağırır; `lazy.map` her erişimde yeniden çağırır.
- Tembel dizinin hiç okunmayan elemanı için dönüştürücü koşmaz; aynı dizi hem `her-kullanimda` hem `hic` taşıyabilir.
- Bu dersin anı: **her kullanımda** (`lazy.map` dönüştürücüsü, aynı elemana iki erişim).

#### Sonraki Adım

Şimdiye kadar her gövde sonuna kadar koştu. Peki gövdenin ortasında bir şey ters giderse, geri kalan satırlara ne olur? Sıradaki ders, hata fırlatmayı ve fırlatmadan sonraki ifadelerin değerlendirilmediğini ölçüyor.

## Bellek ve İleri Özellikler

Otomatik referans sayımı, güçlü, zayıf ve sahipsiz referanslarla tutma döngülerinin kırılması, kapanışlarda yakalama, özellik sarmalayıcıları, sonuç kurucular ve katı eşzamanlılık denetimiyle yalıtım.

### Otomatik Referans Sayımı

Source: https://academia.sh/tr/kurslar/swift/otomatik-referans-sayimi

Sınıf örneğinin son güçlü referansı bırakıldığı anda deinit'in çalıştığını dört bırakma yolunda ölçer ve ikinci bir referansın bu anı ertelediğini gösterir.

Önceki ders erişim düzeylerinin bir adı kimin görebildiğini değiştirdiğini, ama anı değiştirmediğini ölçtü: gizli bir `lazy` önbellek, `private` olsa da ilk kullanımda kuruluyordu. Bu ders görünürlükten çıkıp bir nesnenin ne zaman yok edildiğine geçer. Sınıf örnekleri **otomatik referans sayımı (ARC)** ile yönetilir: her örnek üzerindeki güçlü referansların sayısı tutulur ve bu sayı sıfıra düştüğü anda örnek yok edilir. Yok edilirken sınıfın `deinit` gövdesi koşar. Soru bu kursun sorusudur: `deinit` gövdesi ne zaman değerlendiriliyor?

Önceki müfredatlarda ömrün nerede bittiği ve belleği kimin sahiplendiği sorulmuştu. Bu ders ikisini de kategori yapmaz. Ömrün sınırı ya da sahibi ölçülmez; ölçülen, `deinit` gövdesinin hangi satırdan sonra ve kaç kez koştuğudur, yani an. Önce çekirdek verilir. `Probe`, bir ifadenin kaç kez değerlendirildiğini ve bunun kullanımdan önce mi sonra mı olduğunu kaydeder; `tally` dört anı sayar.

```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)")
    }
}
```


Ölçülecek sınıf `Session`'dır. `deinit` gövdesi önce bir satır basar, sonra sahibi olan `Probe`'a `eval` ile haber verir. Böylece hem `print` sırası hem de `Probe` sayacı aynı olayı gösterir.

```swift
// Session.swift — deinit'inde Probe'a yazan sinif
final class Session {
    let name: String
    let owner: Probe

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

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


#### Dört Bırakma Yolu

Birinci ölçüm bir `Session` örneğini dört farklı yolla bırakır. İlk yol bir `do` kapsamının sonudur. İkincisi isteğe bağlı bir değişkene `nil` atamaktır. Üçüncüsü diziden `removeLast()` ile çıkarmaktır. Dördüncüsü bir `for` döngüsünün her yinelemesinde yeni örnek kurmaktır. Her yol en az iki kez çalışır, çünkü `Probe`'un kuralı `hic` dışındaki her gözlem için en az iki kullanım ister. Kural sayar, niyeti okumaz.

```swift
// main.swift — deinit hangi birakista kosar
let scopeEnd = Probe("kapsam-sonu")
let niled = Probe("nil-atamasi")
let removed = Probe("diziden-cikarma")
let looped = Probe("dongu-yinelemesi")

for round in 1...2 {
    _ = scopeEnd.use {
        do {
            let temp = Session("kapsam-\(round)", owner: scopeEnd)
            print("kullanildi \(temp.name)")
        }
    }
}

for round in 1...2 {
    var held: Session? = Session("nil-\(round)", owner: niled)
    _ = niled.use { held = nil }
    print("sonra \(held == nil)")
}

var list = [Session("dizi-1", owner: removed), Session("dizi-2", owner: removed)]
_ = removed.use { list.removeLast() }
_ = removed.use { list.removeLast() }
print("dizide kalan \(list.count)")

for round in 1...3 {
    let item = Session("dongu-\(round)", owner: looped)
    _ = looped.use { item.name }
}

let probes = [scopeEnd, niled, removed, looped]
for probe in probes {
    probe.report()
}
tally(probes)
```

```
kullanildi kapsam-1
deinit kapsam-1
kullanildi kapsam-2
deinit kapsam-2
deinit nil-1
sonra true
deinit nil-2
sonra true
deinit dizi-2
deinit dizi-1
dizide kalan 0
deinit dongu-1
deinit dongu-2
deinit dongu-3
kapsam-sonu: 2 degerlendirme, 2 kullanim -> her-kullanimda
nil-atamasi: 2 degerlendirme, 2 kullanim -> her-kullanimda
diziden-cikarma: 2 degerlendirme, 2 kullanim -> her-kullanimda
dongu-yinelemesi: 3 degerlendirme, 3 kullanim -> her-kullanimda
hemen: 0
ilk-kullanimda: 0
her-kullanimda: 4
hic: 0
```


Çıktıda önce `print` satırlarının sırasına bakılır. `kullanildi kapsam-1` satırı `deinit kapsam-1` satırından önce gelir: ölçülen koşumda örnek kapsamın sonunda yok edildi ve `deinit` satırı kapsam çıkışında göründü. `nil` atamasında `deinit` atamanın içinde koşar, ardından gelen `sonra true` satırı bunu doğrular. Dizide iki örnek vardır; `removeLast()` sondakini çıkarır, bu yüzden `dizi-2` `dizi-1`'den önce yok edilir. Döngüde üç yineleme üç ayrı `deinit` üretir ve her `deinit` bir sonraki yinelemeden önce biter.

Dört `Probe`'un hepsi `her-kullanimda` der. İlk üçünde iki tur olduğu için iki değerlendirme, döngüde üç tur olduğu için üç değerlendirme sayılır. `deinit` gövdesi bir örnek her yok edildiğinde yeniden koşar ve her yok ediliş yeni bir örneğe aittir. Bu dersin anı bu yüzden **her kullanımda**dır. `tally` çıktısında `hemen`, `ilk-kullanimda` ve `hic` sıfırdır.

#### Fonksiyon Sınırı

Referansı yalnız değişkenler tutmaz; bir fonksiyona parametre olarak geçen örnek de çağrı süresince canlıdır. İkinci ölçüm iki durumu ayırır. Birinde fonksiyon örneği üretir ve çağıran tutar. Ötekinde üretilen örnek hiçbir değişkene bağlanmaz, yalnız bir ifadenin ortasında yaşar.

```swift
// main.swift — fonksiyon sinirinda referans
func inspect(_ session: Session) -> String {
    session.name
}

func make(_ name: String, owner: Probe) -> Session {
    Session(name, owner: owner)
}

let crossing = Probe("fonksiyon-siniri")

for round in 1...2 {
    let made = make("uretilen-\(round)", owner: crossing)
    _ = crossing.use { inspect(made) }
    print("tur bitti \(round)")
}

for round in 1...2 {
    _ = crossing.use { inspect(make("gecici-\(round)", owner: crossing)) }
    print("gecici tur bitti \(round)")
}

crossing.report()
```

```
tur bitti 1
deinit uretilen-1
tur bitti 2
deinit uretilen-2
deinit gecici-1
gecici tur bitti 1
deinit gecici-2
gecici tur bitti 2
fonksiyon-siniri: 4 degerlendirme, 4 kullanim -> her-kullanimda
```

Birinci döngüde `tur bitti 1` satırı `deinit uretilen-1` satırından önce gelir. `inspect` çağrısı süresince örnek canlıdır ve çağrı örneğin ömrünü uzatmaz; örneğin sahibi hâlâ `made` sabitidir ve `deinit` yinelemenin sonunda, `made` kapsamdan çıkarken koşar. İkinci döngüde ise `make` ile üretilen örnek hiçbir ada bağlanmaz. Ölçülen koşumda örnek çağıran ifade biterken bırakıldı ve `deinit gecici-1` satırı `gecici tur bitti 1` satırından önce görünür. İki döngü birlikte `fonksiyon-siniri` için dört değerlendirme ve dört kullanım kaydeder. Yani bir fonksiyona örnek geçirmek ömrü uzatmaz; ömrü uzatan, örneği bir adda ya da bir özellikte saklamaktır.

#### Zincirleme Bırakma

Bir örnek başka bir örneği özelliğinde tutabilir. Bu durumda dıştaki örneğin yok edilmesi içtekinin son referansını da bırakır. Üçüncü ölçüm iki zinciri birlikte kurar: `Holder` bir `Session` tutar, `Car` ise `Vehicle` sınıfından türer ve iki sınıfın da `deinit` gövdesi vardır. Soru, hangi `deinit` gövdesinin önce koştuğudur.

```swift
// main.swift — ozellikteki ornek ve alt sinif zinciri
final class Holder {
    let name: String
    let child: Session

    init(_ name: String, owner: Probe) {
        self.name = name
        child = Session("cocuk-\(name)", owner: owner)
    }

    deinit {
        print("deinit \(name)")
    }
}

class Vehicle {
    let owner: Probe

    init(owner: Probe) {
        self.owner = owner
    }

    deinit {
        print("deinit ust-sinif")
        _ = owner.eval("ust")
    }
}

final class Car: Vehicle {
    deinit {
        print("deinit alt-sinif")
        _ = owner.eval("alt")
    }
}

let nested = Probe("ozellikteki-ornek")
let inherited = Probe("ust-sinif-zinciri")

for round in 1...2 {
    var holder: Holder? = Holder("sahip-\(round)", owner: nested)
    _ = nested.use { holder = nil }
}

for _ in 1...2 {
    var car: Car? = Car(owner: inherited)
    _ = inherited.use { car = nil }
}

nested.report()
inherited.report()
```

```
deinit sahip-1
deinit cocuk-sahip-1
deinit sahip-2
deinit cocuk-sahip-2
deinit alt-sinif
deinit ust-sinif
deinit alt-sinif
deinit ust-sinif
ozellikteki-ornek: 2 degerlendirme, 2 kullanim -> her-kullanimda
ust-sinif-zinciri: 4 degerlendirme, 2 kullanim -> her-kullanimda
```

Çıktıda önce `deinit sahip-1`, ardından `deinit cocuk-sahip-1` görünür: dıştaki örneğin `deinit` gövdesi önce koşar, gövde biter bitmez özellikleri bırakılır ve içteki örneğin `deinit`'i bunu izler. Bu yüzden dıştaki `deinit` gövdesi içtekine hâlâ erişebilir. Alt sınıf zincirinde önce alt sınıfın, sonra üst sınıfın `deinit` gövdesi koşar. `ozellikteki-ornek` iki turda iki değerlendirme sayar, çünkü yalnız içteki `Session` onu çağırır. `ust-sinif-zinciri` ise tur başına iki `eval` gördüğü için iki turda dört değerlendirme sayar. İkisi de `her-kullanimda` der: değerlendirme sayısı kullanım sayısından fazla ya da ona eşit olabilir, kural yalnız bir kereden fazla koşup koşmadığına bakar. `deinit` açıkça çağrılamaz ve parametre almaz; yalnız sayaç sıfıra düştüğünde, bir kez, çalışma zamanı tarafından koşturulur.

#### Üzerine Yazma Eskiyi Bırakır

`nil` ataması bırakmanın tek biçimi değildir. Bir `var` özelliğe yeni bir örnek atamak, özelliğin tuttuğu eski örneğin referansını da bırakır. Bu ölçüm bir `Slot` sınıfının `session` özelliğine ikinci bir `Session` atar ve atamanın iki yanında `evaluations` değerini yazdırır. Yeni örnek ayrı bir `Probe`'a bağlıdır; böylece sayılan `deinit` yalnız eski örneğe aittir.

```swift
// main.swift — ozelligin ustune yazma
final class Slot {
    var session: Session?

    init(_ session: Session?) {
        self.session = session
    }
}

let overwritten = Probe("ustune-yazma")
let spare = Probe("yedek")

for round in 1...2 {
    let slot = Slot(Session("eski-\(round)", owner: overwritten))
    print("yazmadan once \(overwritten.evaluations)")
    _ = overwritten.use { slot.session = Session("yeni-\(round)", owner: spare) }
    print("yazmadan sonra \(overwritten.evaluations)")
}

overwritten.report()
```

```
yazmadan once 0
deinit eski-1
yazmadan sonra 1
deinit yeni-1
yazmadan once 1
deinit eski-2
yazmadan sonra 2
deinit yeni-2
ustune-yazma: 2 degerlendirme, 2 kullanim -> her-kullanimda
```

Her turda `yazmadan once 0` ve `yazmadan sonra 1` satırları arasında `deinit eski-1` görünür: atama satırı yeni değeri yerleştirirken eski değerin son güçlü referansını bırakır ve eski örnek, atama bitmeden yok edilir. `deinit yeni-1` ise tur sonunda gelir, çünkü `slot` o noktada kapsamdan çıkar ve içindeki örneği de bırakır. Yani aynı özellik tur boyunca iki ayrı örneği sırayla tutmuş, iki ayrı `deinit` üretmiştir. `ustune-yazma` iki turda iki değerlendirme sayar ve `her-kullanimda` der. Kural değişmemiştir: `deinit` bir bırakma satırına bağlı değildir, sayacın sıfıra düşmesine bağlıdır. Üzerine yazma bu sayaçtan bir azaltır; `nil` ataması da, kapsam sonu da aynı işi yapar.

#### Toplu Boşaltma

Bir dizinin tuttuğu örnekler tek tek değil, dizi boşaltılırken bırakılır. Bu ölçüm iki ayrı dizi kurar ve her birini `removeAll()` ile boşaltır. Soru, tek bir çağrının kaç `deinit` gövdesi koşturduğudur.

```swift
// main.swift — removeAll ile toplu birakma
let cleared = Probe("toplu-bosaltma")

for round in 1...2 {
    var batch = [
        Session("toplu-\(round)-a", owner: cleared),
        Session("toplu-\(round)-b", owner: cleared)
    ]
    print("bosaltmadan once \(cleared.evaluations)")
    _ = cleared.use { batch.removeAll() }
    print("bosaltmadan sonra \(cleared.evaluations), dizide \(batch.count)")
}

cleared.report()
```

```
bosaltmadan once 0
deinit toplu-1-a
deinit toplu-1-b
bosaltmadan sonra 2, dizide 0
bosaltmadan once 2
deinit toplu-2-a
deinit toplu-2-b
bosaltmadan sonra 4, dizide 0
toplu-bosaltma: 4 degerlendirme, 2 kullanim -> her-kullanimda
```

`removeAll()` çağrısı tek satırdır, ama içinde iki örnek bulunduğundan iki `deinit` koşar: `bosaltmadan once 0` ile `bosaltmadan sonra 2` arasında `deinit toplu-1-a` ve `deinit toplu-1-b` görünür. İkinci turda sayaç 2'den 4'e çıkar. Burada `Probe` iki kullanıma karşılık dört değerlendirme sayar ve yine `her-kullanimda` der; bir çağrının birden çok `deinit` koşturabilmesi `her-kullanimda` kuralıyla çelişmez, çünkü kural değerlendirme sayısına değil, bir kereden fazla koşup koşmadığına bakar. Dizinin bıraktığı her örnek kendi sayacına sahiptir. Biri başka bir değişkende de tutuluyor olsaydı, `removeAll()` yalnız dizinin payını bırakır ve o örneğin `deinit`'i sayaç sıfıra düşene kadar beklerdi; sınırlayıcı ölçüm bu durumu gösterir.

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

Yukarıdaki kural, `deinit`'in her bırakmada koştuğu izlenimini verebilir. Son ölçüm bunun yanlış olduğunu gösterir. Aynı örneği iki değişken tutar: `first` ve `second`. Önce `first` bırakılır, sonra `second`. `shared.evaluations` her adımdan sonra okunur.

```swift
// main.swift — ikinci referans ve ust duzey let
let shared = Probe("ikinci-referans")
let outlives = Probe("ust-duzey-let")

for round in 1...2 {
    var first: Session? = Session("ortak-\(round)", owner: shared)
    var second = first
    _ = shared.use { first = nil }
    print("birinci birakildi: \(shared.evaluations) deinit, ikinci \(second == nil ? "yok" : "var")")
    _ = shared.use { second = nil }
    print("ikinci birakildi: \(shared.evaluations) deinit")
}

let pinned = Session("ust-duzey", owner: outlives)
_ = outlives.use { pinned.name }
_ = outlives.use { pinned.name }

shared.report()
outlives.report()
```

```
birinci birakildi: 0 deinit, ikinci var
deinit ortak-1
ikinci birakildi: 1 deinit
birinci birakildi: 1 deinit, ikinci var
deinit ortak-2
ikinci birakildi: 2 deinit
ikinci-referans: 2 degerlendirme, 4 kullanim -> her-kullanimda
ust-duzey-let: 0 degerlendirme, 2 kullanim -> hic
```

İlk bırakmadan sonra `deinit` sayısı sıfırdır ve `second` hâlâ örneği tutar. `deinit ortak-1` satırı yalnız ikinci bırakmada görünür. İkinci turda aynı sıra yinelenir. Sayacın sıfıra düşmesi `deinit`'in koşma koşuludur; bırakma satırının kendisi koşul değildir. `ikinci-referans` `Probe`'u iki değerlendirme ve dört kullanım kaydeder ve `her-kullanimda` der, ama bunu tek başına söylemez: hangi bırakmanın `deinit`'i tetiklediği yalnız `print` sırasından ve araya konan `evaluations` okumalarından anlaşılır.

Son iki satır ikinci sınırı gösterir. Üst düzey `let` ile tutulan `pinned` iki kez kullanılır, ama `ust-duzey-let` `Probe`'u sıfır değerlendirme kaydeder ve `hic` der. Üst düzey bir `let` süreç sonuna kadar tutulur ve bu ölçümde süreç sonunda `deinit` koşmaz. `deinit`'in koşmaması bir hata değildir; referans sayısı hiç sıfıra düşmemiştir. Böyle bir örneğin `deinit` gövdesine güvenerek bir kaynağı kapatmak yanlış olur.

**BL1.** Her bırakma yolu için yalnızca bir örnek ölçüldü; kuralın başka bırakma yollarında da geçtiği bu derste gösterilmedi. Dört yolun seçilmesi tasarım tercihidir.

#### Özet

- Sınıf örnekleri otomatik referans sayımıyla (ARC) yönetilir; son güçlü referans bırakıldığı anda `deinit` koşar.
- Kapsam sonu, `nil` ataması, diziden çıkarma ve döngü yinelemesi aynı kuralı izler: `deinit` bırakma noktasında koşar.
- İkinci bir güçlü referans `deinit`'i erteler; sayaç sıfıra düşmeden `deinit` koşmaz.
- Üst düzey bir `let`'in tuttuğu örnek süreç sonuna kadar bırakılmaz ve bu ölçümde `deinit` koşmaz (`hic`).
- Bu dersin anı: **her kullanımda**. Her yeni örneğin yok edilişi `deinit` gövdesini yeniden koşturur.

#### Sonraki Adım

Bu derste sayaç sıfıra düşmek zorunda kaldı ve düştü. İki sınıf birbirini güçlü tutarsa sayaç hiçbir zaman sıfıra düşmez ve iki `deinit` de koşmaz. Sonraki ders, `weak` ve `unowned` ile bu döngüyü kırmayı ve `deinit` gövdesinin neden hiç değerlendirilmediğini ölçer.

### Güçlü, Zayıf ve Sahipsiz Referanslar

Source: https://academia.sh/tr/kurslar/swift/guclu-zayif-ve-sahipsiz-referanslar

İki sınıfın birbirini güçlü tutunca deinit'in hiç çalışmadığını, weak ve unowned ile döngünün kırıldığını ve unowned referansın nesne gittikten sonra okunamayacağını ölçer.

Önceki derste referans sayısı sıfıra düştü ve `deinit` koştu. Sayının sıfıra düşmesi bir koşuldu, garanti değil. İki sınıf örneği birbirini **güçlü referans** ile tutarsa her biri ötekinin sayacını bir üstte tutar ve dışarıdaki tüm adlar bırakılsa bile ikisi de yaşar. Bu duruma **tutma döngüsü** denir. Bu ders döngüyü kurar, iki yoldan kırar ve her yolda `deinit` gövdesinin ne zaman koştuğunu ölçer.

İki kırma aracı vardır. **Zayıf referans (`weak`)** örneği tutmaz: sayacı artırmaz ve gösterdiği örnek yok edilince kendiliğinden `nil` olur, bu yüzden her zaman isteğe bağlı bir `var` olur. **Sahipsiz referans (`unowned`)** da sayacı artırmaz, ama `nil` olmaz ve isteğe bağlı olmak zorunda değildir; bunun karşılığında gösterdiği örneğin kendisinden uzun yaşayacağı sözünü ister. `Probe` önceki dersteki gibi verilir.

```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)")
    }
}
```


#### Tutma Döngüsü

Ölçülen iki sınıf `Account` ve `Profile`'dır. Her ikisinin de `deinit` gövdesi bir satır basar ve ortak `Probe`'a `eval` ile haber verir. `Account` bir `Profile` tutar, `Profile` da bir `Account` tutar; ikisi de varsayılan, yani güçlü referanstır. Her turda iki örnek kurulur, birbirine bağlanır ve ardından ikisine de `nil` atanır. Dışarıdaki adlar böylece bırakılmış olur.

```swift
// main.swift — iki sinif birbirini guclu tutar
final class Account {
    let name: String
    let owner: Probe
    var profile: Profile?

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

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

final class Profile {
    let title: String
    let owner: Probe
    var account: Account?

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

    deinit {
        print("deinit profil \(title)")
        _ = owner.eval(title)
    }
}

let cyclic = Probe("tutma-dongusu")

for round in 1...2 {
    var account: Account? = Account("hesap-\(round)", owner: cyclic)
    var profile: Profile? = Profile("profil-\(round)", owner: cyclic)
    account?.profile = profile
    profile?.account = account
    _ = cyclic.use {
        account = nil
        profile = nil
    }
    print("birakildi \(round): \(cyclic.evaluations) deinit")
}

cyclic.report()
```

```
birakildi 1: 0 deinit
birakildi 2: 0 deinit
tutma-dongusu: 0 degerlendirme, 2 kullanim -> hic
```

İki turun ikisinde de bırakmadan sonra `deinit` sayısı sıfırdır ve `deinit` satırı hiç görünmez. Her örnek ötekinin özelliği tarafından tutulduğu için iki sayaç da en az bire eşit kalır. `tutma-dongusu` iki kullanım ve sıfır değerlendirme kaydeder; kural `hic` der. Bu dersin anı bu yüzden **hiç**tir: `deinit` gövdesi hiçbir anda değerlendirilmez. Üstelik hiçbir şey hata vermez. Döngüdeki iki örnek program sonuna kadar bellekte kalır.

#### Zayıf Referansla Kırmak

Döngüyü kırmak için iki taraftan yalnız birinin referansı zayıf yapılır. İkinci ölçüm `Profile.account` özelliğini `weak var` olarak tanımlar, başka hiçbir şeyi değiştirmez. Bu kez örnekler iki adımda bırakılır: önce `account`, sonra `profile`. Her adımdan sonra `profile?.account` okunur ve `deinit` sayısı basılır.

```swift
// main.swift — bir taraf zayif tutar
final class Account {
    let name: String
    let owner: Probe
    var profile: Profile?

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

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

final class Profile {
    let title: String
    let owner: Probe
    weak var account: Account?

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

    deinit {
        print("deinit profil \(title)")
        _ = owner.eval(title)
    }
}

let broken = Probe("zayif-referans")

for round in 1...2 {
    var account: Account? = Account("hesap-\(round)", owner: broken)
    var profile: Profile? = Profile("profil-\(round)", owner: broken)
    account?.profile = profile
    profile?.account = account
    print("baglandi: \(profile?.account == nil ? "nil" : "dolu")")
    _ = broken.use { account = nil }
    print("hesap birakildi: \(profile?.account == nil ? "nil" : "dolu"), \(broken.evaluations) deinit")
    _ = broken.use { profile = nil }
    print("profil birakildi: \(broken.evaluations) deinit")
}

broken.report()
```

```
baglandi: dolu
deinit hesap hesap-1
hesap birakildi: nil, 1 deinit
deinit profil profil-1
profil birakildi: 2 deinit
baglandi: dolu
deinit hesap hesap-2
hesap birakildi: nil, 3 deinit
deinit profil profil-2
profil birakildi: 4 deinit
zayif-referans: 4 degerlendirme, 4 kullanim -> her-kullanimda
```

Bağlandıktan sonra `profile?.account` doludur. `account` adı bırakıldığında `Account` örneğinin güçlü referansı kalmaz: `profile.account` zayıf olduğu için sayacı artırmamıştı. `deinit hesap hesap-1` satırı hemen çıkar ve ardından gelen okuma `nil` verir; zayıf referans, gösterdiği örnek yok edildiği anda kendiliğinden `nil` olur. `Profile` örneği ise `profile` adıyla hâlâ tutulduğundan yaşar, `deinit profil` satırı ancak o ad bırakılınca görünür. İki turda dört `deinit` koştu ve `zayif-referans` `her-kullanimda` der.

#### Sahipsiz Referansla Kırmak

Zayıf referans örneğin yok olabileceğini söyler. Bir ilişki bu yönde değilse, yani `Card` bir `Customer` olmadan hiçbir zaman var olmayacaksa, referans `unowned let` olarak yazılır. Üçüncü ölçüm bu ilişkiyi kurar: `Customer` isteğe bağlı bir `card` tutar, `Card` ise `unowned let customer` taşır. Müşteri kartı tutar, kart müşteriyi tutmaz. Ölçüm yalnız müşteri adını bırakır.

```swift
// main.swift — sahipsiz referans daha uzun yasayana baglanir
final class Customer {
    let name: String
    let owner: Probe
    var card: Card?

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

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

final class Card {
    let number: String
    let owner: Probe
    unowned let customer: Customer

    init(_ number: String, customer: Customer, owner: Probe) {
        self.number = number
        self.customer = customer
        self.owner = owner
    }

    deinit {
        print("deinit kart \(number)")
        _ = owner.eval(number)
    }
}

let bound = Probe("sahipsiz-referans")

for round in 1...2 {
    var customer: Customer? = Customer("musteri-\(round)", owner: bound)
    if let current = customer {
        current.card = Card("kart-\(round)", customer: current, owner: bound)
        print("kart sahibi: \(current.card?.customer.name ?? "yok")")
    }
    _ = bound.use { customer = nil }
    print("birakildi \(round): \(bound.evaluations) deinit")
}

bound.report()
```

```
kart sahibi: musteri-1
deinit musteri musteri-1
deinit kart kart-1
birakildi 1: 2 deinit
kart sahibi: musteri-2
deinit musteri musteri-2
deinit kart kart-2
birakildi 2: 4 deinit
sahipsiz-referans: 4 degerlendirme, 2 kullanim -> her-kullanimda
```

Her turda `deinit musteri` satırı `deinit kart` satırından önce gelir. Müşteri bırakılınca sayacı sıfıra düşer, `deinit` gövdesi koşar, ardından özellikleri bırakılır ve kartın tek güçlü referansı da gider. Bu zincirde döngüyü kıran, kartın müşteriyi güçlü tutmamasıdır; `unowned` bunu isteğe bağlı olmadan ifade eder. `sahipsiz-referans` iki kullanım ve dört değerlendirme kaydeder, iki `deinit` her turda koşar ve kural `her-kullanimda` der.

#### Temsilci Deseni

Zayıf referansın en yaygın yeri temsilci desenidir: bir nesne işini bitirince haber vereceği ikinci bir nesneyi saklar, haber alan ise işi yapan nesneyi zaten tutar. İki yönün de güçlü olması tutma döngüsü kurardı. Bu ölçüm `Worker` sınıfının `weak var delegate` özelliğini kullanır; `Reporter` ise `Worker`'ı güçlü tutar. Tur sonunda yalnız `reporter` adı bırakılır ve `worker` adı elde kalır.

```swift
// main.swift — temsilci zayif, calisan guclu
final class Reporter {
    let name: String
    let owner: Probe
    let worker: Worker

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

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

final class Worker {
    weak var delegate: Reporter?
}

let handedOver = Probe("temsilci")
for round in 1...2 {
    var reporter: Reporter? = Reporter("raporlayici-\(round)", owner: handedOver)
    let worker = reporter!.worker
    print("temsilci \(worker.delegate?.name ?? "nil")")
    _ = handedOver.use { reporter = nil }
    print("birakildi: temsilci \(worker.delegate?.name ?? "nil"), \(handedOver.evaluations) deinit")
}

handedOver.report()
```

```
temsilci raporlayici-1
deinit raporlayici raporlayici-1
birakildi: temsilci nil, 1 deinit
temsilci raporlayici-2
deinit raporlayici raporlayici-2
birakildi: temsilci nil, 2 deinit
temsilci: 2 degerlendirme, 2 kullanim -> her-kullanimda
```

Bağlandıktan sonra `worker.delegate` raporlayıcının adını verir. `reporter` adı bırakıldığında `Reporter` örneğinin güçlü referansı kalmaz: `Worker` onu zayıf tuttuğu için sayacı artırmamıştı. `deinit raporlayici raporlayici-1` satırı hemen çıkar ve ardından gelen okuma `nil` verir. `worker` ise `worker` adıyla elde tutulduğundan yaşamaya devam eder; `Reporter` onu özelliğinde tuttuğu halde kendisi gittiği için `Worker`'ın payı yalnız dışarıdaki addır. Desenin işleyişi bu asimetriye dayanır: haber alan, haber verenden uzun yaşamak zorunda değildir ve haber veren, haber alanın gittiğini `nil` okuyarak öğrenir. Bu yüzden temsilci özelliği her zaman isteğe bağlı bir değer olarak yazılır ve her kullanımdan önce kontrol edilir. İki turda iki `deinit` koşar ve `temsilci` `her-kullanimda` der.

#### Zayıf ve Sahipsiz Okuma

İki referans biçimi, gösterdikleri örnek yaşadığı sürece aynı değeri verir. Ayrım örnek gittikten sonra başlar. Bu ölçüm bir `Watcher` kurar: biri `weak var`, öteki `unowned let` iki özellik aynı `Target` örneğini gösterir. Örnek yaşarken ikisi de okunur, sonra örnek bırakılır ve yalnız zayıf özellik okunur. Sahipsiz özellik bu noktadan sonra okunmaz; okuma programı durdururdu.

```swift
// main.swift — ayni hedefe zayif ve sahipsiz bakis
final class Target {
    let name: String
    let owner: Probe

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

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

final class Watcher {
    weak var soft: Target?
    unowned let strict: Target

    init(_ target: Target) {
        soft = target
        strict = target
    }
}

let looked = Probe("zayif-ve-sahipsiz")
for round in 1...2 {
    var target: Target? = Target("hedef-\(round)", owner: looked)
    let watcher = Watcher(target!)
    print("yasarken: zayif \(watcher.soft?.name ?? "nil"), sahipsiz \(watcher.strict.name)")
    _ = looked.use { target = nil }
    print("gittikten sonra: zayif \(watcher.soft?.name ?? "nil")")
}

looked.report()
```

```
yasarken: zayif hedef-1, sahipsiz hedef-1
deinit hedef hedef-1
gittikten sonra: zayif nil
yasarken: zayif hedef-2, sahipsiz hedef-2
deinit hedef hedef-2
gittikten sonra: zayif nil
zayif-ve-sahipsiz: 2 degerlendirme, 2 kullanim -> her-kullanimda
```

Örnek yaşarken iki okuma da `hedef-1` verir; iki biçim bu anda birbirinden ayırt edilemez. `deinit hedef hedef-1` satırından sonra zayıf okuma `nil` verir: örnek yok edildikten sonra zayıf referans `nil` okunur. Sahipsiz okuma ise aynı noktada ölçülmez: `strict.name` satırı gitmiş bir örneği okuyacaktı ve çalışma zamanı programı durdururdu. Bu nedenle iki biçim arasındaki seçim bir okuma biçimi seçimi değil, ne olması gerektiği sözüdür. Hedefin `watcher`'dan önce gitmesi mümkünse zayıf, imkânsızsa sahipsiz yazılır. `zayif-ve-sahipsiz` iki turda iki değerlendirme sayar ve `her-kullanimda` der; zayıf okumanın sonucu ise her okuma anında yeniden belirlenir. Önceki okumanın sonucu sonraki okumaya taşınmaz.

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

Zayıf ve sahipsiz referans yalnız gösterdiklerini tutmamakta ortaktır; gösterdikleri örnek gittikten sonra okunduklarında ayrılırlar. Zayıf referans `nil` verir ve bunu ikinci ölçüm yukarıda gösterdi. Sahipsiz referansın böyle bir güvencesi yoktur: gitmiş bir örneğe `unowned` ile erişmek çalışma zamanında programı durdurur. Bu yüzden bu okuma ölçülmez. Aşağıdaki parça yalnız kaynaktır ve çalıştırılmaz. Çıktı iddiası da yoktur.

```swift
// Kaynak parca: calistirilmaz
var customer = Customer("musteri-3", owner: bound)
let card = Card("kart-3", customer: customer, owner: bound)
customer = Customer("musteri-4", owner: bound)
print(card.customer.name)
```

Üçüncü satır `musteri-3`'ün son güçlü referansını bırakır; `Card` onu tutmadığı için örnek yok edilir. Dördüncü satır yok edilmiş bir örneği okur. Aynı yapı `weak` ile yazılsaydı okuma `nil` verirdi ve program devam ederdi. Seçim bu yüzden bir ömür sözüdür: `unowned` yalnız referansı taşıyan örnekten daha uzun yaşadığı kesin olan bir örneğe bağlanır; emin olunmayan her yerde `weak` kullanılır. Derleyici bu sözü denetlemez, bu yüzden bu bir sınırdır, bir an değil.

**BL2.** Her bağlantı biçimi için tek bir sınıf çifti ölçüldü; üç çiftin hepsi iki sınıflı küçük zincirlerdir. Daha uzun döngülerde (üç ve daha çok sınıf) aynı kuralın geçtiği bu derste gösterilmedi. Çiftlerin seçimi tasarım tercihidir.

#### Özet

- İki sınıf birbirini güçlü tutarsa referans sayıları sıfıra düşmez ve iki `deinit` de koşmaz; bu bir tutma döngüsüdür.
- Bir tarafı `weak var` yapmak döngüyü kırar; zayıf referans gösterdiği örnek yok edilince `nil` olur.
- `unowned let` ömrü daha uzun olan tarafa bağlanır ve `nil` olmaz; yok edilmiş örneği okumak programı durdurur, bu yüzden ölçülmez.
- Bu dersin anı: **hiç**. Tutma döngüsündeki `deinit` gövdesi hiçbir anda değerlendirilmez; döngü kırıldığında her turda koşar.

#### Sonraki Adım

Bu derste döngü iki sınıf özelliği arasında kuruldu. Bir sınıfın kendi özelliğinde sakladığı kapanış da `self`'i güçlü yakalayarak aynı döngüyü kurabilir ve bu, tutma döngülerinin en sık rastlanan biçimidir. Sonraki ders kapanışlarda yakalamayı ve `[weak self]` yakalama listesinin ne zaman değerlendirildiğini ölçer.

### Kapanışlarda Yakalama

Source: https://academia.sh/tr/kurslar/swift/kapanislarda-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.

### Özellik Sarmalayıcıları

Source: https://academia.sh/tr/kurslar/swift/ozellik-sarmalayicilari

Özellik sarmalayıcısının wrappedValue okuyucusunun her erişimde çalıştığını, başlangıç değerinin bir kez kurulduğunu ve önbellekli bir sarmalayıcının anı ilk kullanıma çevirdiğini ölçer.

Önceki derste kapanış, bir değeri kendi içine alıp saklayan bir kutuydu. **Özellik sarmalayıcısı (`@propertyWrapper`)** benzer bir kutuyu özelliğin kendisi için kurar: özelliğin değerinin nasıl saklanacağı, okunurken ya da yazılırken ne olacağı ayrı bir tipe taşınır ve özellik o tipin adıyla işaretlenir. Sarmalayıcı tipinin zorunlu tek üyesi `wrappedValue`'dur; özellik okunduğunda ve yazıldığında bu üye çalışır. İsteğe bağlı `projectedValue` üyesi ise özelliğin adının başına `$` konarak erişilen ikinci bir değer sunar.

Bu ders bir sarmalayıcıyı yazar ve onun üç farklı anını ölçer: okuyucunun çalışma anı, başlangıç değerinin değerlendirme anı ve bir önbellek eklenince değişen an. Ölçümlerin çoğu bir `Clamped` sarmalayıcısını kullanır; değeri bir aralığa sıkıştıran küçük bir tip.

```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)")
    }
}
```

```swift
// Clamped.swift — degeri bir araliga sikistirir
@propertyWrapper
struct Clamped {
    private var current: Int
    let range: ClosedRange<Int>
    let owner: Probe

    init(wrappedValue: Int, _ range: ClosedRange<Int>, owner: Probe) {
        self.range = range
        self.owner = owner
        self.current = min(max(wrappedValue, range.lowerBound), range.upperBound)
    }

    var wrappedValue: Int {
        get { owner.eval(current) }
        set { current = min(max(newValue, range.lowerBound), range.upperBound) }
    }

    var projectedValue: Bool { current == range.upperBound }
}
```

#### Sarmalayıcıyı Yazmak

`Clamped` sarmalayıcısı bir `Int` tutar ve atanan değeri verilen aralığa sıkıştırır. `wrappedValue` okuyucusunun gövdesi değeri döndürmeden önce `owner.eval` çağırır; okuyucunun her çalışışı böylece kaydedilir. `projectedValue` değerin üst sınırda olup olmadığını söyleyen bir `Bool`'dur ve okunurken `eval` çağırmaz, çünkü ölçülen şey `wrappedValue`'dur.

#### Okuyucu Her Erişimde Çalışır

Sarmalayıcı kullanılırken özellik `@Clamped(0...10, owner: reads) var level = 5` biçiminde yazılır: parantez içindeki değerler sarmalayıcının `init` yöntemine, `= 5` ise `wrappedValue` parametresine gider. İlk ölçüm `level`'ı iki kez okur ve arada sınırın dışında bir değer atar. Son satırda `$level` yansıtılan değeri okur.

```swift
// main.swift — wrappedValue okuyucusu her erisimde calisir
let reads = Probe("sarmalayici-okuma")

do {
    @Clamped(0...10, owner: reads) var level = 5
    print("seviye: \(reads.use { level })")
    level = 42
    print("seviye: \(reads.use { level })")
    print("ust sinirda: \($level)")
}

reads.report()
```

```
seviye: 5
seviye: 10
ust sinirda: true
sarmalayici-okuma: 2 degerlendirme, 2 kullanim -> her-kullanimda
```

İlk okuma 5, ikincisi 10 verir: atanan 42 aralığın üst sınırına sıkıştırıldı. `$level` ise `projectedValue`'yu okur ve değer üst sınırda olduğu için `true` yazar. `sarmalayici-okuma` iki kullanım ve iki değerlendirme kaydeder; okuyucu her erişimde çalıştığı için anı `her-kullanimda` olur. `$level` okuması kaydı artırmadı, çünkü o başka bir üyeyi çağırır. Bu dersin anı **her kullanımda**dır.

#### Başlangıç Değeri Bir Kez Kurulur

Özelliğe yazılan `= 5` ifadesi okuyucunun her çalışmasında değil, özellik kurulurken bir kez değerlendirilir. İkinci ölçüm başlangıç değerini `start.eval(5)` ile sarar ve özelliği iki kez okur. Okuyucunun kendi `eval` çağrısı farklı bir `Probe`'a, `quiet`'e yazar; böylece `start` yalnızca başlangıç değerinin kaydını tutar.

```swift
// main.swift — baslangic degeri ozellik kurulurken bir kez degerlendirilir
let start = Probe("baslangic-degeri")
let quiet = Probe("sessiz-okuma")

do {
    @Clamped(0...10, owner: quiet) var level = start.eval(5)
    print("seviye: \(start.use { level }) \(start.use { level })")
}

start.report()
```

```
seviye: 5 5
baslangic-degeri: 1 degerlendirme, 2 kullanim -> hemen
```

İki okuma da 5 verir ve `baslangic-degeri` tek değerlendirme kaydeder. Bu değerlendirme ilk `use` çağrısından önce olduğu için anı `hemen`dir. Okuyucu iki kez çalıştı ama başlangıç ifadesini yeniden değerlendirmedi: sarmalayıcının `init` yöntemi ifadenin değerini bir kez aldı ve `current`'a yazdı.

```swift
// Cached.swift — degeri ilk okumada hesaplar ve saklar
@propertyWrapper
final class Cached {
    private var memo: Int?
    private let make: () -> Int

    init(wrappedValue make: @autoclosure @escaping () -> Int) {
        self.make = make
    }

    var wrappedValue: Int {
        if let memo { return memo }
        let result = make()
        memo = result
        return result
    }
}
```

#### Yazıcı da Her Atamada Çalışır

`wrappedValue` iki yönlüdür: `get` okumada, `set` atamada çalışır. Başlangıç değeri ise `set` üzerinden geçmez; `init` yöntemi onu doğrudan saklar. Bu fark yazma tarafında ayrıca ölçülebilir. `Counted` sarmalayıcısı `set` gövdesinde `owner.eval` çağırır ve hiçbir sınırlama yapmaz. Ölçüm özelliği 0 ile başlatır, sonra üç kez artırır.

```swift
// Counted.swift — her atamayi kaydeder
@propertyWrapper
struct Counted {
    private var current: Int
    let owner: Probe

    init(wrappedValue: Int, owner: Probe) {
        self.current = wrappedValue
        self.owner = owner
    }

    var wrappedValue: Int {
        get { current }
        set { current = owner.eval(newValue) }
    }
}
```

```swift
// main.swift — set her atamada calisir, baslangic degeri set'ten gecmez
let writes = Probe("sarmalayici-yazma")

do {
    @Counted(owner: writes) var score = 0
    for amount in 1...3 {
        _ = writes.use { score += amount }
    }
    print("puan: \(score)")
}

writes.report()
```

```
puan: 6
sarmalayici-yazma: 3 degerlendirme, 3 kullanim -> her-kullanimda
```

Puan 6 olur, çünkü 1, 2 ve 3 eklendi. `sarmalayici-yazma` üç kullanım ve üç değerlendirme kaydeder: başlangıçtaki `= 0` ataması değerlendirmeye girmedi, yalnızca üç artırma girdi. Okuma ve yazma tarafları aynı kuralı izler: sarmalayıcının üyesi her erişimde çalışır, başlangıç değeri ise `init` yönteminde bir kez alınır.

#### Yansıtılan Değer

`Clamped` iki şey sunar: `wrappedValue` ile özelliğin kendisi, `projectedValue` ile yanında taşınan ek bir bilgi. Ek bilgiye özelliğin adının başına `$` koyarak ulaşılır. Bu `$` okuması `wrappedValue` okuyucusunu çağırmaz; ayrı bir özelliktir. Ölçüm iki aynı sarmalayıcıyı ayrı `Probe`'larla kurar, ikisine de aralığın dışında bir değer atar. Birinden `$` ile, ötekinden doğrudan okur.

```swift
// main.swift — $ okumasi wrappedValue okuyucusunu cagirmaz
let projected = Probe("yansitilan-deger")
let plain = Probe("dogrudan-okuma")
do {
    @Clamped(0...10, owner: projected) var left = 5
    @Clamped(0...10, owner: plain) var right = 5
    left = 50
    right = 50
    print("ust sinirda: \(projected.use { $left }) \(projected.use { $left })")
    print("deger: \(plain.use { right }) \(plain.use { right })")
}
projected.report()
plain.report()
```

```
ust sinirda: true true
deger: 10 10
yansitilan-deger: 0 degerlendirme, 2 kullanim -> hic
dogrudan-okuma: 2 degerlendirme, 2 kullanim -> her-kullanimda
```

İki özelliğe de 50 atandı ve ikisi de 10'a sıkıştırıldı. `$left` okuması `true` verir: değer üst sınırdadır. Ama bu okuma `wrappedValue` okuyucusuna uğramaz. `yansitilan-deger` iki kullanıma karşılık sıfır değerlendirme kaydeder ve `hic` der; çünkü `projectedValue` gövdesinde `eval` yoktur ve `current` doğrudan karşılaştırılır. `dogrudan-okuma` ise aynı iki kullanımda iki değerlendirme kaydeder ve `her-kullanimda` der. `$` okuması `wrappedValue` okuyucusunu çalıştırmaz; ayrı bir üyedir: `$` yazımı sarmalayıcıya, `wrappedValue` okuyucusunun yan etkilerinden bağımsız bir soru sorar. Anı sorusu bu yüzden her erişim için ayrı sorulur: `left` ve `right` aynı sarmalayıcı tipidir, ama hangisinin okuyucusunun çalıştığı erişimin yazılışına bağlıdır.

#### Sarmalayıcı Argümanları Bir Kez Değerlendirilir

`@Clamped(0...10, owner: quiet)` yazımındaki parantezli ifadeler sarmalayıcının `init` yöntemine gider. Derleyici `@Clamped(...) var level = 5` satırını, kabaca, özellik kurulurken bir kez çalışan bir `Clamped(wrappedValue: 5, 0...10, owner: quiet)` çağrısına çevirir. Bu ölçüm hem özellik biçimini hem de elle yazılmış `init` çağrısını sınar. Aralığın üst sınırı ve başlangıç değeri `Probe` içinden geçer.

```swift
// main.swift — sarmalayici argumanlari ozellik kurulurken bir kez degerlendirilir
let ceiling = Probe("aralik-argumani")
let explicit = Probe("acik-baslatma")
let quiet = Probe("sessiz")
do {
    @Clamped(0...ceiling.eval(10), owner: quiet) var level = 5
    print("seviye: \(ceiling.use { level }) \(ceiling.use { level })")

    let wrapper = Clamped(wrappedValue: explicit.eval(5), 0...10, owner: quiet)
    print("acik: \(explicit.use { wrapper.wrappedValue }) \(explicit.use { wrapper.wrappedValue })")
}
ceiling.report()
explicit.report()
```

```
seviye: 5 5
acik: 5 5
aralik-argumani: 1 degerlendirme, 2 kullanim -> hemen
acik-baslatma: 1 degerlendirme, 2 kullanim -> hemen
```

İki yolda da sayaç aynı şeyi söyler. `aralik-argumani` tek değerlendirme kaydeder ve bu değerlendirme ilk `use` çağrısından önce olduğu için anı `hemen`dir. İki okuma aralığın üst sınırını yeniden hesaplamadı: `ceiling.eval(10)` özellik kurulurken bir kez çalıştı ve `ClosedRange` değeri sarmalayıcının içinde kaldı. Elle yazılmış `Clamped(wrappedValue: explicit.eval(5), ...)` çağrısı da aynı sonucu verir: `acik-baslatma` tek değerlendirme kaydeder ve `hemen` der. Sarmalayıcı sözdizimi yeni bir değerlendirme kuralı getirmez; bir `init` çağrısını kısa yazar ve çağrının argümanları her çağrıda olduğu gibi çağrı anında bir kez değerlendirilir. Okuyucunun kaç kez çalıştığı ile argümanın kaç kez değerlendirildiği iki ayrı sayıdır: ilki her erişimle artar, ikincisi özellik kurulurken bir kez sabitlenir. Bu ayrım, sarmalayıcıyı okurken hangi sayıya bakıldığını baştan seçmeyi gerektirir.

#### Sarmalayıcı Bileşimi

Bir özelliğe birden çok sarmalayıcı yazılabilir. Soldakiler dıştadır, sağdakiler içte. Aşağıdaki `Traced` genel bir sarmalayıcıdır; değeri olduğu gibi saklar ve her okumada `owner.eval` çağırır. Ölçüm onu `Clamped`'ın dışına koyar. Özelliği iki kez okur ve her iki katmanın okuyucusunun kaç kez çalıştığına bakar.

```swift
// Traced.swift — her okumayi kaydeden genel sarmalayici
@propertyWrapper
struct Traced<Value> {
    private var current: Value
    let owner: Probe

    init(wrappedValue: Value, owner: Probe) {
        self.current = wrappedValue
        self.owner = owner
    }

    var wrappedValue: Value {
        get { owner.eval(current) }
        set { current = newValue }
    }
}
```

```swift
// main.swift — iki sarmalayici ust uste
let outer = Probe("dis-sarmalayici")
let inner = Probe("ic-sarmalayici")
do {
    @Traced(owner: outer) @Clamped(0...10, owner: inner) var level = 50
    print("okuma: \(outer.use { inner.use { level } }) \(outer.use { inner.use { level } })")
    level = 3
    print("yazma sonrasi: \(outer.use { inner.use { level } })")
}
outer.report()
inner.report()
```

```
okuma: 10 10
yazma sonrasi: 3
dis-sarmalayici: 4 degerlendirme, 3 kullanim -> her-kullanimda
ic-sarmalayici: 3 degerlendirme, 3 kullanim -> her-kullanimda
```

İlk iki okuma 10 verir: başlangıç değeri 50 idi ve en içteki `Clamped` onu `init` içinde 10'a sıkıştırmıştı. `level = 3` atamasından sonra okuma 3 verir. Sayaçlar dikkat çekicidir: iç sarmalayıcının okuyucusu üç okumada üç kez çalışır. Dış sarmalayıcının okuyucusu ise dört kez çalışır: üç okuma ve atamanın kendisi. Atama, dıştaki sarmalayıcının değerini önce okuyup içteki `Clamped` üzerinde değiştirdiği için dış okuyucuyu bir kez daha çağırır. Bileşim bu yüzden iki katmanın her birinin çalışma sayısını ayrı ayrı sayar. İki `Probe` de `her-kullanimda` der. Katman eklemek, her okuma ve yazmaya bir `get` ve `set` zinciri eklemektir. Okuyucusu yan etki taşıyan bir sarmalayıcıyı bileşime sokarken bu ek çalışmaların hesaba katılması gerekir. Bu ölçümde dış okuyucu dört, iç okuyucu üç kez koştu; daha çok katmanlı bileşim ölçülmedi.

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

Başlangıç değerinin `hemen` olması sarmalayıcı sözdiziminin değil, `Clamped`'ın yazılışının sonucudur. Aşağıdaki `Cached` sarmalayıcısı `init` yöntemine verilen ifadeyi `@autoclosure` ile bir kapanışa çevirir ve `wrappedValue` ilk okunana kadar çağırmaz. Sonuç bir özelliğe saklanır; sonraki okumalar saklanan değeri döndürür. Aynı `@` işareti, aynı `= ifade` yazımı, ama değerlendirme anı değişti.

```swift
// main.swift — onbellekli sarmalayici ani ilk kullanima cevirir
let lazyValue = Probe("onbellekli-sarmalayici")

do {
    @Cached var total = lazyValue.eval(7)
    print("toplam: \(lazyValue.use { total }) \(lazyValue.use { total })")
}

lazyValue.report()
```

```
toplam: 7 7
onbellekli-sarmalayici: 1 degerlendirme, 2 kullanim -> ilk-kullanimda
```

İki okuma da 7 verir. `onbellekli-sarmalayici` tek değerlendirme kaydeder, ama bu kez değerlendirme ilk `use` çağrısının içinde olduğu için anı `ilk-kullanimda`dır. `@Cached` yazımı `@Clamped` yazımına benziyordu; farkı yaratan, ifadenin sarmalayıcının `init` yönteminde hemen kullanılması ya da bir kapanışta bekletilmesidir. An, `@` sözdiziminde değil sarmalayıcının gövdesinde yazılıdır.

**BL4.** Ölçülen iki sarmalayıcı da `Int` değer tutar. Genel tipli sarmalayıcıların ve sınıf özelliği olarak kullanılan sarmalayıcıların aynı kuralları izlediği bu derste ölçülmedi. Yerel değişken olarak kullanım tasarım tercihidir.

#### Özet

- Özellik sarmalayıcısı `@propertyWrapper` ile işaretlenir; `wrappedValue` özellik okunurken ve yazılırken çalışır, `projectedValue` `$` önekiyle okunur.
- `wrappedValue` okuyucusu her erişimde koşar; atama sarmalayıcının kuralına göre yapılır, örneğin aralığa sıkıştırılır.
- Özelliğe yazılan başlangıç değeri `Clamped` gibi bir sarmalayıcıda özellik kurulurken bir kez değerlendirilir.
- `@autoclosure` ve saklanan sonuçla yazılmış `Cached` aynı yazımı ilk kullanımda tek değerlendirmeye çevirir; an sarmalayıcının gövdesindedir.
- Bu dersin anı: **her kullanımda**. `wrappedValue` okuyucusu her erişimde çalışır.

#### Sonraki Adım

Bu derste sarmalayıcı bir özelliğin değer kuralını ayrı bir tipe taşıdı. Sonuç kurucular aynı fikri bir ifade listesine uygular: bir blok içindeki satırlar tek tek değerlendirilmek yerine bir kurucu tipin `buildBlock` gibi yöntemlerine verilir ve hangi satırın hangi anda çalışacağını o tip belirler. Sonraki ders kendi sonuç kurucumuzu yazar ve seçilmeyen dalın ifadelerinin hiç değerlendirilmediğini ölçer.

### Sonuç Kurucular

Source: https://academia.sh/tr/kurslar/swift/sonuc-kurucular

Kendi sonuç kurucumuzu yazar; seçilmeyen if ve else dalındaki ifadelerin hiç değerlendirilmediğini, for döngüsünün her elemanı değerlendirdiğini ve seçimin derlemede değil koşumda yapıldığını ölçer.

Önceki ders bir özelliğin değer kuralını ayrı bir tipe taşıdı. **Sonuç kurucu (`@resultBuilder`)** aynı fikri bir ifade listesine uygular. Bir kapanışın gövdesinde alt alta yazılan ifadeler normalde tek tek çalışır ve değerleri atılır. Sonuç kurucu ile işaretlenmiş bir parametreye verilen kapanışta ise her satır kurucu tipin bir yöntemine verilir ve yöntemlerin döndürdüğü değerler birleştirilerek tek bir sonuç elde edilir. Hangi satırın hangi anda çalışacağını, bu yöntemlerden hangisine nasıl bağlandığı belirler.

Bu ders bir dizgi listesi kuran küçük bir kurucu yazar ve üç şeyi ölçer: seçilmeyen `if` dalındaki ifadenin değerlendirilip değerlendirilmediğini, `for` döngüsünün elemanları nasıl işlediğini ve dalın seçiminin ne zaman yapıldığını. Dönüşümün kendisi derleme sırasında olur ve koşumda görülmez; ölçülen şey, dönüşmüş kodun koşum sırasındaki davranışıdır. Derleme ile koşum arasındaki bu ayrım önceki bir müfredatta ayrıca ele alınmıştı.

```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)")
    }
}
```

```swift
// ListBuilder.swift — dizgi listesi kuran sonuc kurucu
@resultBuilder
struct ListBuilder {
    static func buildExpression(_ line: String) -> [String] {
        [line]
    }

    static func buildBlock(_ parts: [String]...) -> [String] {
        parts.flatMap { $0 }
    }

    static func buildOptional(_ part: [String]?) -> [String] {
        part ?? []
    }

    static func buildEither(first part: [String]) -> [String] {
        part
    }

    static func buildEither(second part: [String]) -> [String] {
        part
    }

    static func buildArray(_ parts: [[String]]) -> [String] {
        parts.flatMap { $0 }
    }
}

func makeList(@ListBuilder _ content: () -> [String]) -> [String] {
    content()
}
```

#### Kurucuyu Yazmak

`ListBuilder` altı statik yöntem taşır. `buildExpression` her tek ifadeyi bir dizgi listesine çevirir. `buildBlock` bir bloktaki parçaları art arda birleştirir. `buildOptional`, `else` dalı olmayan bir `if`'in sonucunu karşılar; dal seçilmediyse boş liste verir. `buildEither(first:)` ve `buildEither(second:)`, `if` ve `else` dallarından hangisi seçildiyse onu karşılar. `buildArray` bir `for` döngüsünün her yinelemesinden gelen parçaları birleştirir. `makeList` kapanışı `ListBuilder` ile işaretlenmiş bir parametre olarak alan yardımcı işlevdir.

#### Seçilmeyen Dal Değerlendirilmez

İlk ölçüm `if`/`else` içinde iki ifade yazar. Biri `chosen` adlı `Probe`'u, öteki `skipped` adlı `Probe`'u çağırır. Koşul her zaman doğrudur, yani yalnız birinci dal seçilir. `render` işlevi iki kez çağrılır.

```swift
// main.swift — secilmeyen dalin ifadesi degerlendirilmez
let chosen = Probe("secilen-dal")
let skipped = Probe("secilmeyen-dal")

do {
    let useFirst = true

    func render() -> [String] {
        makeList {
            "baslik"
            if useFirst {
                chosen.eval("birinci")
            } else {
                skipped.eval("ikinci")
            }
        }
    }

    for _ in 1...2 {
        let lines = chosen.use { skipped.use { render() } }
        print(lines.joined(separator: ","))
    }
}

chosen.report()
skipped.report()
```

```
baslik,birinci
baslik,birinci
secilen-dal: 2 degerlendirme, 2 kullanim -> her-kullanimda
secilmeyen-dal: 0 degerlendirme, 2 kullanim -> hic
```

İki çağrıda da liste `baslik,birinci` olur. `secilen-dal` iki değerlendirme kaydeder, çünkü kurucunun gövdesi her `render` çağrısında yeniden çalışır. `secilmeyen-dal` ise iki kullanım görür ama sıfır değerlendirme: ikinci dalın ifadesi hiçbir zaman çalışmaz. Kural `hic` der. Bu dersin anı **hiç**tir: seçilmeyen dalın ifadesi hiçbir anda değerlendirilmez. İfadenin yalnızca yazılmış olması, onu çağırmaz.

#### Döngü Her Elemanı Değerlendirir

`for` döngüsü `buildArray` ile birleştirilir ve gövdesindeki ifade her eleman için bir kez değerlendirilir. Ölçüm üç elemanlı bir diziyi iki kez işler ve aynı kapanışa `if` içeren bir satır da ekler; bu satır `buildOptional` yoluna girer.

```swift
// main.swift — for her elemani degerlendirir
let items = Probe("dongu-elemani")

do {
    func render(names: [String]) -> [String] {
        makeList {
            for name in names {
                items.eval(name)
            }
            if names.count > 2 {
                "uzun liste"
            }
        }
    }

    for _ in 1...2 {
        let lines = items.use { render(names: ["a", "b", "c"]) }
        print(lines.joined(separator: ","))
    }
}

items.report()
```

```
a,b,c,uzun liste
a,b,c,uzun liste
dongu-elemani: 6 degerlendirme, 2 kullanim -> her-kullanimda
```

Her çağrıda liste `a,b,c,uzun liste` olur. `dongu-elemani` altı değerlendirme kaydeder: iki çağrıda üçer eleman. Elemanlar bir kez önbelleğe alınmadı; kurucunun gövdesi her çağrıda baştan koşar ve döngü her elemanı baştan işler. Anı `her-kullanimda`dır. `if names.count > 2` satırının dizgisi bir `Probe`'dan geçmedi; yalnızca listede göründü.

#### İfade Dönüşümü

`buildExpression`, bloktaki her tek ifadeyi kurucunun parça tipine çevirir. Aynı ada sahip birden çok `buildExpression` yazılırsa, derleyici ifadenin tipine uyanı seçer. Aşağıdaki `MixedBuilder` hem `String` hem `Int` kabul eder; sayıları `sayi 42` biçiminde bir dizgiye çevirir. Ölçüm iki ifadeyi ayrı `Probe`'lardan geçirir ve her çağrıda her ifadenin kaç kez değerlendirildiğine bakar.

```swift
// MixedBuilder.swift — metin ve sayi ifadesi kabul eden sonuc kurucu
@resultBuilder
struct MixedBuilder {
    static func buildExpression(_ line: String) -> [String] {
        [line]
    }

    static func buildExpression(_ number: Int) -> [String] {
        ["sayi \(number)"]
    }

    static func buildBlock(_ parts: [String]...) -> [String] {
        parts.flatMap { $0 }
    }
}

func makeMixed(@MixedBuilder _ content: () -> [String]) -> [String] {
    content()
}
```

```swift
// main.swift — buildExpression ifadeyi cevirir, yeniden degerlendirmez
let texts = Probe("metin-ifadesi")
let numbers = Probe("sayi-ifadesi")
do {
    for _ in 1...2 {
        let lines = texts.use {
            numbers.use {
                makeMixed {
                    texts.eval("baslik")
                    numbers.eval(42)
                }
            }
        }
        print(lines.joined(separator: ","))
    }
}
texts.report()
numbers.report()
```

```
baslik,sayi 42
baslik,sayi 42
metin-ifadesi: 2 degerlendirme, 2 kullanim -> her-kullanimda
sayi-ifadesi: 2 degerlendirme, 2 kullanim -> her-kullanimda
```

Çıktı iki çağrıda da `baslik,sayi 42` verir: dizgi ifadesi olduğu gibi geçti, tam sayı ifadesi `sayi 42` biçimine çevrildi. Çevirinin hangi yoldan yapıldığı ifadenin tipinden anlaşılır; kurucuya hiçbir ayırma yazılmadı. Sayaçlar dönüşümün değerlendirmeyi değiştirmediğini gösterir. `metin-ifadesi` ve `sayi-ifadesi` iki çağrıda ikişer değerlendirme kaydeder ve `her-kullanimda` der. `buildExpression` ifadenin sonucunu alır, ifadeyi yeniden çalıştırmaz. Kurucu kapanışın gövdesini çağrı başına bir kez baştan koşturur ve her ifade tam bir kez değerlendirilir. Bu yüzden dönüşüm yazarken yan etkili bir ifadenin tekrarlanacağından endişe edilmez. Aynı ad altında birden çok `buildExpression` yazmak, kurucunun kabul ettiği ifade tiplerini genişletir; tipi karşılamayan bir ifade ise derleme hatası verir, çalışma zamanında sessizce atlanmaz.

#### İç İçe Koşul

`if` içindeki `if`, kurucuda iç içe iki `buildOptional` yolu açar. Dış koşul tutmazsa iç koşulun kendisi de değerlendirilmez. Ölçüm aynı kapanışı iki kez `visible: false` ile, sonra iki kez `visible: true` ile çalıştırır. İç koşul ve iç satırın ifadesi için her durumda ayrı birer `Probe` vardır.

```swift
// main.swift — dis kosul tutmazsa ic kosul degerlendirilmez
let closedGate = Probe("kapali-ic-kosul")
let closedHit = Probe("kapali-ic-satir")
let openGate = Probe("acik-ic-kosul")
let openHit = Probe("acik-ic-satir")
do {
    func render(visible: Bool, gate: Probe, hit: Probe) -> [String] {
        makeList {
            if visible {
                if gate.eval(true) {
                    hit.eval("ic")
                }
            }
        }
    }

    for _ in 1...2 {
        let shut = closedHit.use { closedGate.use { render(visible: false, gate: closedGate, hit: closedHit) } }
        let shown = openHit.use { openGate.use { render(visible: true, gate: openGate, hit: openHit) } }
        print("kapali \(shut.count), acik \(shown.count)")
    }
}
for probe in [closedGate, closedHit, openGate, openHit] {
    probe.report()
}
```

```
kapali 0, acik 1
kapali 0, acik 1
kapali-ic-kosul: 0 degerlendirme, 2 kullanim -> hic
kapali-ic-satir: 0 degerlendirme, 2 kullanim -> hic
acik-ic-kosul: 2 degerlendirme, 2 kullanim -> her-kullanimda
acik-ic-satir: 2 degerlendirme, 2 kullanim -> her-kullanimda
```

Dış koşul `false` iken ne iç koşul ne de iç satır değerlendirilir. `kapali-ic-kosul` ve `kapali-ic-satir` ikişer kullanıma karşılık sıfır değerlendirme kaydeder ve `hic` der; çıktıda `kapali 0` satırı da listenin boş kaldığını gösterir. Dış koşul `true` iken ise iki iç ifade de her çağrıda bir kez değerlendirilir: `acik-ic-kosul` ve `acik-ic-satir` ikişer değerlendirme kaydeder ve `her-kullanimda` der. `acik 1` satırı iç satırın listeye girdiğini doğrular. Koşulların sırası burada da kısa devre gibi davranır: sıradaki ifade yalnız öncekinin yolu açıksa değerlendirilir. Fark, bunun `&&` operatöründen değil, kurucunun `buildOptional` yolundan gelmesidir. Her `if` kendi `buildOptional` çağrısını üretir; dıştaki çağrının parçası boşsa içtekine hiç inilmez ve içteki ifade bu yüzden `hic` kalır.

#### Boş Döngü

`buildArray`, döngünün her yinelemesinden gelen parçaları alır. Döngü hiç dönmezse parça listesi boştur ve `buildArray` boş bir liste üretir. Soru, boş bir dizi üzerinde kurulan döngünün gövdesindeki ifadenin değerlendirilip değerlendirilmediğidir. Ölçüm aynı kapanışı bir kez boş, bir kez dolu bir dizi ile çalıştırır.

```swift
// main.swift — bos dizi uzerinde dongu govdesi degerlendirilmez
let emptyLoop = Probe("bos-dongu")
let filledLoop = Probe("dolu-dongu")
do {
    func render(names: [String], probe: Probe) -> [String] {
        makeList {
            for name in names {
                probe.eval(name)
            }
        }
    }

    for _ in 1...2 {
        let none = emptyLoop.use { render(names: [], probe: emptyLoop) }
        let some = filledLoop.use { render(names: ["x", "y"], probe: filledLoop) }
        print("bos \(none.count), dolu \(some.count)")
    }
}
emptyLoop.report()
filledLoop.report()
```

```
bos 0, dolu 2
bos 0, dolu 2
bos-dongu: 0 degerlendirme, 2 kullanim -> hic
dolu-dongu: 4 degerlendirme, 2 kullanim -> her-kullanimda
```

Boş dizi üzerindeki döngüde gövdenin ifadesi hiç çalışmaz: `bos-dongu` iki kullanıma karşılık sıfır değerlendirme kaydeder ve `hic` der. `bos 0` satırı sonucun da boş liste olduğunu söyler; `buildArray` boş parça listesini boş bir sonuca çevirir ve hata vermez. Dolu dizide her çağrı iki eleman işler, dolayısıyla `dolu-dongu` iki çağrıda dört değerlendirme kaydeder ve `her-kullanimda` der. Değerlendirme sayısı döngünün dönüş sayısına eşittir. Kurucu gövdesinde döngü kuran biri için bu bir ölçüdür: elemanları yan etkili bir ifadeyle işleyen döngü, her çağrıda eleman sayısı kadar koşar. Koleksiyon çağrılar arasında saklanmaz; yan etkinin tekrarı gerekmiyorsa döngü kurucunun dışına alınır ve sonuç kurucuya hazır verilir.

Bu ölçümlerin ortak dersi şudur: kurucu, gövdedeki her satırı bir çağrıya çevirir ve hangi çağrının yapılacağını yazım biçimi belirler. Bir ifadenin değerlendirilip değerlendirilmeyeceğini okuyarak tahmin etmek yerine, dönüşümü zihinde açmak gerekir: ifade bir `buildExpression` çağrısına, koşul bir `buildOptional` (`else` varsa `buildEither`) çağrısına, döngü bir `buildArray` çağrısına dönüşür. Yan etkisi olan bir ifadeyi gövdeye koymadan önce bu çağrı zincirinde kaç kez ve hangi koşulda çalışacağı sorulur.

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

Dönüşüm derlemede yapılır, ama hangi dalın seçileceği koşumda belirlenir. Önceki iki ölçümde koşul sabitti ve bu fark görünmüyordu. Son ölçüm koşulu bir parametre yapar ve `render`'ı sırayla `true`, `false`, `true` ile çağırır. Her iki dalın ifadesi ayrı birer `Probe`'a yazılır.

```swift
// main.swift — dal secimi kosumda yapilir
let left = Probe("sol-dal")
let right = Probe("sag-dal")

do {
    func render(first: Bool) -> [String] {
        makeList {
            if first {
                left.eval("sol")
            } else {
                right.eval("sag")
            }
        }
    }

    for flag in [true, false, true] {
        let lines = left.use { right.use { render(first: flag) } }
        print(lines.joined(separator: ","))
    }
}

left.report()
right.report()
```

```
sol
sag
sol
sol-dal: 2 degerlendirme, 3 kullanim -> her-kullanimda
sag-dal: 1 degerlendirme, 3 kullanim -> ilk-kullanimda
```

Çağrılar sırasıyla `sol`, `sag`, `sol` yazar. `sol-dal` iki değerlendirme kaydeder: yalnız `true` ile gelen iki çağrıda koşar. `sag-dal` ise tek değerlendirme kaydeder ve bu değerlendirme ikinci çağrıda, yani ilk kullanımdan sonra olduğu için kural `ilk-kullanimda` der. İfade, dalın seçildiği çağrıda değerlendirilir; kodda yazılı olmasının tek başına bir anlamı yoktur. Bu yüzden `hic` anı yalnızca dal hiç seçilmediğinde geçerlidir ve koşula bağlıdır.

**BL5.** Ölçülen kurucu yalnız dizgi listesi kurar ve `buildLimitedAvailability` gibi ek yöntemler tanımlanmadı. İki dallı `if`, üç dallı `if`/`else if` zincirlerinde aynı kuralın geçtiği ölçülmedi. Tek bir kurucu tipinin kullanılması tasarım tercihidir.

#### Özet

- Sonuç kurucu, kapanış gövdesindeki ifadeleri `buildBlock`, `buildExpression`, `buildOptional`, `buildEither` ve `buildArray` yöntemleriyle bir sonuca birleştirir.
- `if`/`else` içinde seçilmeyen dalın ifadesi hiç değerlendirilmez; yazılmış olması onu çağırmaz.
- Kurucunun gövdesi her çağrıda baştan koşar ve `for` döngüsü her elemanı her çağrıda değerlendirir.
- Dönüşüm derlemede yapılır, ama hangi dalın seçileceği koşumda belirlenir; `hic` anı yalnız dal hiç seçilmezse geçerlidir.
- Bu dersin anı: **hiç**. Seçilmeyen dalın ifadesi hiçbir anda değerlendirilmez.

#### Sonraki Adım

Bu konunun ilk dersleri tek bir akışta çalışan koddaki bellek ve değerlendirme anlarını ölçtü. Birden çok iş aynı anda aynı belleğe dokunduğunda yeni sorular çıkar: bir değer kimin kontrolünde, kim ona ne zaman erişir. Sonraki ders aktörü, aktarılabilir tip anlamına gelen `Sendable`'ı ve `@MainActor`'ı anlatır; aktörün tembel özelliğinin ilk erişimde nasıl davrandığını ve `async let` ile başlatılan işi ölçer.

### Eşzamanlılık Güvenliği

Source: https://academia.sh/tr/kurslar/swift/eszamanlilik-guvenligi

Aktörün tembel özelliğinin eşzamanlı okumalarda bir kez kurulduğunu ve sonucu hiç beklenmeyen async let'in yine de değerlendirildiğini ölçer; Sendable ve @MainActor sınırlarını anlatır, kurs kapanışını yapar.

Önceki dersler tek akışta çalışan kodun değerlendirme anlarını ölçtü. Birden çok iş aynı anda aynı belleğe dokunduğunda başka bir soru doğar: bir değere aynı anda iki yerden yazılırsa ne olur. Buna **veri yarışı (data race)** denir ve sonucu tahmin edilemez. Swift 6 bu soruyu çalışma zamanına bırakmaz; **katı eşzamanlılık denetimi (strict concurrency checking)** yarışa yol açabilecek kodu derlemede reddeder.

Bu ders üç araç anlatır ve iki şey ölçer. Araçlar aktör yalıtımı, `Sendable` ve `@MainActor`'dır. Ölçümler bir aktörün tembel özelliğinin ne zaman kurulduğu ve `async let` ile başlatılan işin ne zaman koştuğudur. Aktörün içindeki sayaçlar düz `Int`'tir: `Probe` bir sınıftır, `Sendable` değildir ve aktör sınırını geçemez. Görevlerin koşma sırası belirsiz olduğundan sonuçlar toplanıp sıralanarak basılır; yalnız sayılar basılır.

```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)")
    }
}
```

#### Aktör Yalıtımı

**Aktör (actor)** durumunu kendi içinde saklayan ve bu duruma aynı anda tek bir görevin dokunmasına izin veren bir tiptir. Sınıfa benzer, ama değişebilir durumuna ve yalıtımlı yöntemlerine dışarıdan `await` yazmadan erişilemez; değişmez `let` özellikler ve `nonisolated` üyeler bunun dışındadır. Dışarıdaki bir görev aktörün yöntemini çağırırken `await` yazar: çağrı aktörün sırasına girer, aktör o an meşgulse görev bekler. Bu bekleme noktasına **askıya alma noktası (suspension point)** denir ve aktörün değişebilir durumuna `await` yazılmadan ulaşılamaz.

Aşağıdaki aktör iki sayaç tutar ve `total` adlı tembel bir özellik taşır. Özelliğin ifadesi bir yöntemi çağırır; yöntem kaç kez koştuğunu `builds` içinde sayar.

```swift
// Ledger.swift — tembel ozelligi olan aktor
actor Ledger {
    private(set) var builds = 0
    private(set) var reads = 0
    lazy var total: Int = makeTotal()

    private func makeTotal() -> Int {
        builds += 1
        return 42
    }

    func read() -> Int {
        reads += 1
        return total
    }
}
```

Ölçüm önce özelliğe hiç dokunmadan sayaçlara bakar, sonra dört görevi aynı anda `read` çağırmak üzere başlatır.

```swift
// main.swift — aktordeki lazy ozellik eszamanli okumalarda bir kez kurulur
let ledger = Ledger()
print("onceki kurulum \(await ledger.builds)")

let values = await withTaskGroup(of: Int.self) { group in
    for _ in 1...4 {
        group.addTask { await ledger.read() }
    }
    var all: [Int] = []
    for await value in group {
        all.append(value)
    }
    return all
}

print(values.sorted())
print("okuma \(await ledger.reads), kurulum \(await ledger.builds)")
```

```
onceki kurulum 0
[42, 42, 42, 42]
okuma 4, kurulum 1
```

Dört görev aynı özelliği okudu ama kurulum bir kez yapıldı. Özelliğe dokunulmadan önce kurulum sıfırdı: tembel ifade tanım satırında değil, ilk okumada çalıştı. Aktör yalıtımı burada iki işi birden yapar. Okumaları sıraya dizer ve ilk okumanın kurulumu bitmeden ikincisinin başlamasına izin vermez. Bu yüzden iki görevin aynı anda tembel ifadeyi çalıştırması ve sayacın bozulması olanaksızdır. Bu dersin anı **ilk kullanımda**dır.

Bu ölçümün ne söylemediğini de belirtmek gerekir. Aktör, işleri aynı anda yürütmeyi engellemez; yalnız aktörün durumuna dokunan kısmı sıraya dizer. Dört görev aynı anda başlar, ama `read` yöntemine tek tek girer. Tembel özelliğin tek kez kurulması bu sıralamanın doğrudan sonucudur: ikinci görev girdiğinde özellik zaten kuruludur.

Askıya alma noktasının bir bedeli vardır. Bir aktör yöntemi `await` ile başka bir işi beklerken aktör boşalır ve sıradaki görev içeri girebilir. Bu yüzden `await` öncesinde okunan bir değerin sonrasında aynı kaldığı varsayılmaz. Yalıtım iki görevin aynı anda içeride olmasını engeller; beklerken durumun değişmeyeceğini garanti etmez. Bu dersin ölçümlerinde yöntemlerin içinde `await` bulunmaz, bu yüzden bu durum ölçülmedi.

#### Aktarılabilir Tip ve Ana Aktör

Bir değer görevler arasında gidip geliyorsa bir yalıtım sınırını geçiyordur. Sınırı geçebilen tiplere **`Sendable`** denir; iş adıyla aktarılabilir tip. Sayılar, dizgiler ve yalnız aktarılabilir tipleri saklayan, genel (public) olmayan değer tipleri bu özelliği kendiliğinden taşır; genel tiplerde uyum açıkça yazılır. Aktörler de taşır, çünkü kendi içlerini zaten korurlar. Değiştirilebilir durum tutan sıradan bir sınıf ise taşımaz: iki görev aynı örneğe aynı anda yazabilir.

`Probe` bu yüzden aktör sınırını geçemez. Aşağıdaki parça yalnız kaynaktır ve çalıştırılmaz: bir `Probe`, başka bir görevin kapanışına yakalanır. Swift 6 bunu derlemede reddeder; hata metni burada basılmaz.

```swift
// Kaynak parca: calistirilmaz
let moved = Probe("tasinan")
let work = Task.detached { moved.eval(1) }
print(await work.value)
```

Neden reddedildiği bellidir: `Task.detached` ile başlayan iş ana aktörden bağımsız koşar, `moved` ise ana aktörün durumudur. Aynı örneğe iki yerden aynı anda dokunulabilirdi ve bu tam olarak veri yarışının tanımıdır. Derleyici örneğin hangi satırda iki yalıtım bölgesine sızdığını gösterir. Çözümlerden biri değeri taşımamak ya da onu yalıtım altına almaktır: sayacı bir aktörün içine koymak, bu dersteki ölçümlerin yaptığı gibi. Derleyici sorunu çalıştırmadan önce yakaladığı için hata, programın bozuk çalıştığı bir gün yerine yazıldığı gün görülür.

**Ana aktör (`@MainActor`)** önceden tanımlı, tek bir yalıtım bölgesidir. `@MainActor` ile işaretlenen bir tipin durumuna yalnız ana aktörde koşan kod dokunabilir; başka bir yerden gelen çağrı `await` ister. Bir betiğin üst düzey kodu da ana aktörde koşar. Aşağıdaki sınıf bu işareti taşır ve bir sayaç tutar.

```swift
// Screen.swift — ana aktore baglanan sinif
@MainActor
final class Screen {
    private(set) var taps = 0

    func tap() {
        taps += 1
    }
}
```

Ölçüm beş görevi arka planda başlatır; her biri `tap` yöntemini `await` ile çağırır.

```swift
// main.swift — ana aktore bagli sinifa baska gorevlerden await ile erisilir
let screen = Screen()

await withTaskGroup(of: Void.self) { group in
    for _ in 1...5 {
        group.addTask { await screen.tap() }
    }
}

print("dokunus \(screen.taps)")
```

```
dokunus 5
```

Beş çağrı beş artış verdi, kayıp yok. Çağrılar ana aktörün sırasına girdi ve tek tek işlendi. `screen` bir sınıf olduğu halde görevlere yakalanabildi: ana aktöre bağlı sınıflar aktarılabilir sayılır, çünkü durumlarına yalnız tek bir yalıtım bölgesinden dokunulur.

#### Görev Grupları

Yukarıdaki ölçümler **görev grubu (task group)** kullandı. Grup, sayısı çalışma zamanında belirlenebilen alt görevleri başlatır ve hepsinin bitmesini bekler; `for await` ile sonuçlar bittikçe toplanır. Sonuçların geliş sırası belirsizdir, bu yüzden dersin ölçümleri onları sıralayarak basar. Sayısı derleme anında bilinen birkaç iş için ise `async let` vardır; her bağlama tek bir alt görevdir; sonucu bağlamanın adıyla `await` edilerek okunur. Bir `async let` bağlaması sağ tarafındaki ifadeyi bir alt görev olarak başlatır; sonuç `await` ile okunur.

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

Soru şudur: `async let` ne zaman başlar. Akla gelen yanıt, sonucun istendiği satırdır, yani `await` yazılan yer. Bunu sınamak için bir bağlama sonucu okunur, ikincisi hiç okunmaz. Aktör iki bağlamanın da `bump` yöntemini çağırıp çağırmadığını sayar.

```swift
// Counter.swift — cagri sayisini tutan aktor
actor Counter {
    private(set) var calls = 0

    func bump() -> Int {
        calls += 1
        return calls
    }
}
```

```swift
// main.swift — async let kuruldugu satirda baslar, beklenmese de
let counter = Counter()

do {
    async let waited = counter.bump()
    async let ignored = counter.bump()
    _ = await waited
}

print("cagri \(await counter.calls)")
```

```
cagri 2
```

İki çağrı sayıldı. `ignored` hiç `await` edilmedi, ama kapsam bitmeden iş yine koştu: sonucun beklenmemesi işin koşmasına engel olmadı. Kapsam çıkışında Swift beklenmeyen alt görevi iptal eder ve bitmesini bekler; `bump` iptale bakmadığı için çalışmasını tamamlar. Bu ölçüm bir sınırı gösterir. Sıradan bir çağrıda ifade, çağrının yazıldığı yerde ve çağıran beklenerek değerlendirilir; `async let` ise beklemeden bir alt görev başlatır. İki kural farklı alanlara aittir: `async let` bir ifade değil bir görevdir, ve yan etkisi sonucun okunup okunmamasından bağımsızdır. Bu yüzden `async let` için değerlendirme kullanımı beklemez.

**BL6.** Askıya alma noktası içeren aktör yöntemleri ölçülmedi. Ölçümler tek bir aktör ve dört ya da beş eşzamanlı görevle yapıldı; çok daha kalabalık gruplarda aynı kuralın geçtiği gösterilmedi. Görevlerin hangi sırayla koştuğu ölçülmedi, yalnız sayılar okundu. İptal edilen bir görevin iptale bakan bir yöntemde yarıda kaldığı durum ölçülmedi. Ölçüm yalnız işin beklenmese de koştuğunu gösterir; işin bağlama satırında mı yoksa daha sonra mı başladığı bu ölçümle ayrılamaz.

#### Özet

- Aktör, durumunu kendi içinde korur ve dışarıdan erişimi `await` ile sıraya dizer; aktörün değişebilir durumuna `await` yazmadan erişmek mümkün değildir.
- Aktördeki `lazy` özellik birden çok eşzamanlı görevden okunsa da bir kez değerlendirilir ve ilk okumada kurulur.
- `Sendable` olmayan bir değeri görevler arasında taşımak derlemede reddedilir; çözümlerden biri, bu derste ölçülen, değeri yalıtım altına almaktır.
- `async let` bağlaması bir alt görev başlatır; sonucu hiç beklenmese de iş kapsam bitmeden koşar.
- Bu dersin anı: **ilk kullanımda**. Aktörün tembel özelliği eşzamanlı okumalarda yalnız ilk okumada, bir kez kurulur.

#### Kurs Kapanışı

Bu kursun yirmi dört dersi tek bir soruyu yanıtladı: bir ifade ne zaman değerlendirilir. Her ders bir ifadeyi ölçtü ve anını dört addan biriyle söyledi. Tabloda ders, ölçülen ifade, an ve o dersin sınırı yan yana durur.

| Ders | Ölçülen ifade | An | Sınır |
|---|---|---|---|
| Swift'in Tasarım Hedefleri | `let` sabitinin sağ tarafı | `hemen` | Aynı ifade tek `Probe`'a iki kez bağlanırsa kural `her-kullanimda` der |
| Sabitler ve Değişkenler | Yerel sabitin ilk değeri | `hemen` | Başka dosyadaki küresel sabit ilk erişimde kurulur |
| Temel Tipler | `let` satırındaki tip dönüşümü | `hemen` | Okunmayan demet elemanı da değerlendirilir |
| Dizgi İşlemleri | İfade yerleştirmedeki `description` çağrısı | `her-kullanimda` | Bir `let` içinde saklanan dizgi bir kez kurulur |
| İşleçler | Sol taraf doğruyken `\|\|` işlecinin sağı | `hic` | Sağ tarafı `@autoclosure` ile almayan fonksiyon onu çağrıdan önce değerlendirir |
| Denetim Akışı | Eşleşmeden sonraki `where` koşulu | `hic` | Döngüde koşul her turda, `while` çıkışta bir kez daha sorulur |
| Fonksiyonlar | Varsayılan argüman ifadesi | `her-kullanimda` | Verilen argüman gövde onu okumasa da çağrıdan önce değerlendirilir |
| Kapanışlar | Kapanış gövdesi | `her-kullanimda` | Yakalama listesindeki ifade kurulurken bir kez değerlendirilir |
| İsteğe Bağlı Değerler | Boş kutuda `map` dönüştürücüsü | `hic` | Dış katmanı dolu `.some(nil)` kutusunda dönüştürücü koşar |
| İsteğe Bağlı Açma | `if let` sağ tarafı | `hemen` | Zincirin boş halkasından sonrası atlanır, aynı satırdaki bağımsız ifade atlanmaz |
| Yapılar ve Sınıflar | Yazarken kopyalanan belgenin kopyası | `ilk-kullanimda` | Tek sahipli ya da sınıf alanındaki belgede kopya hiç oluşmaz |
| Numaralandırmalar | İlişkili değerin ifadesi | `hemen` | Numaralandırmanın hesaplanan özelliği her okunuşta koşar |
| Özellikler | `lazy` özelliğin ilk değeri | `ilk-kullanimda` | Yapıda her kopya kendi `lazy` değerini ayrıca kurar |
| Protokoller | Protokol uzantısındaki varsayılan gerçekleştirim | `her-kullanimda` | Anı protokol değil, gereksinimi karşılayan tip belirler |
| Uzantılar | Uzantıyla eklenen hesaplanan özellik ve yöntem | `her-kullanimda` | Uzantıdaki `static let` ilk erişimde bir kez kurulur |
| Genellikler | `lazy.map` dönüştürücüsü | `her-kullanimda` | Tembel dizinin okunmayan elemanı hiç dönüştürülmez |
| Hata Yönetimi | `throw` sonrasındaki ifade | `hic` | `defer` bloğu her çıkışta koşar |
| Erişim Denetimi | `private lazy` önbellek | `ilk-kullanimda` | Anı erişim düzeyi değil, özelliğin çeşidi belirler |
| Otomatik Referans Sayımı | `deinit` gövdesi | `her-kullanimda` | İkinci referans varken bırakma `deinit`'i koşturmaz |
| Güçlü, Zayıf ve Sahipsiz Referanslar | Tutma döngüsündeki `deinit` gövdesi | `hic` | Gitmiş örneğe `unowned` okuma ölçülemez, programı durdurur |
| Kapanışlarda Yakalama | Yakalama listesindeki ifade | `hemen` | Saklanmayan kapanış döngü kurmaz |
| Özellik Sarmalayıcıları | `wrappedValue` okuyucusu | `her-kullanimda` | `@autoclosure` alan sarmalayıcı anı `ilk-kullanimda`'ya çevirir |
| Sonuç Kurucular | Seçilmeyen dalın ifadesi | `hic` | Hangi dalın seçileceği koşumda belirlenir |
| Eşzamanlılık Güvenliği | Aktördeki `lazy` özellik | `ilk-kullanimda` | Sonucu beklenmeyen `async let` yine koşar |

Yirmi dört dersin dağılımı şöyledir: altısı `hemen`, dördü `ilk-kullanimda`, sekizi `her-kullanimda`, altısı `hic`. Bu sayım tablonun an sütunundan çıkar. Kursun çekirdeğindeki sekiz ifadenin dağılımı ise 2, 1, 3 ve 2'dir. İkisi aynı şey değildir: çekirdek sekiz ifadeyi, tablo yirmi dört dersi sayar. Bir dersin anı o dersin ölçtüğü ifadeye aittir.

Kursun bitirdiği iş, ifadeyi kuralıyla birlikte okumayı öğretmektir. Aynı ifade yazılışına göre dört andan her birini verebilir: yerel sabit `hemen`, hesaplanan özellik `her-kullanimda`, `lazy` özellik `ilk-kullanimda`, seçilmeyen dal `hic`. Kursun bıraktığı alışkanlık, bir ifadeye bakınca onun yazılışından anını tahmin etmeye çalışmak yerine hangi kuralın altında durduğunu sormaktır. Bu kurs dilin kendisini ölçtü.

