---
title: 'Özellik Sarmalayıcıları'
source: 'https://academia.sh/tr/kurslar/swift/ozellik-sarmalayicilari'
course: 'Swift Dili'
language: tr
updated: '2026-10-21T05:03:12+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Bir özellik sarmalayıcısını wrappedValue ve projectedValue ile yazar ve $ önekiyle yansıtılan değere erişir'
    - 'wrappedValue okuyucusunun her erişimde çalıştığını ölçüm çıktısından okur'
    - 'Sarmalayıcıya verilen başlangıç değerinin özellik kurulurken bir kez değerlendirildiğini ölçer'
    - 'Önbellekli bir sarmalayıcıda değerlendirme anının sarmalayıcının gövdesinde yazıldığını gösterir'
---

# Özellik Sarmalayıcıları

Ö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.
