---
title: 'Basit Tercih Saklama'
source: 'https://academia.sh/tr/kurslar/ios-veri-ve-ag/basit-tercih-saklama'
course: 'Veri, Ağ ve Eşzamanlılık'
language: tr
updated: '2026-10-21T05:03:09+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Küçük ve gizli olmayan değerlerin tercih deposuna uygun düştüğünü ayırır'
    - 'İki ekranın aynı anahtara yazma sırasının sonucu değiştirdiğini gerçekten ölçer'
    - 'Açılış sayacını artırmanın yinelenince sonucu değiştirdiğini ölçer'
    - 'Varsayılan değerin aynı yazma çiftini farklı duyarlılık sınıfına taşıyabildiğini doğrular'
---

# Basit Tercih Saklama

Küçük bir değerin kalıcı saklanmasında iki ekranın aynı anahtara yazma sırasının, açılış sayacının yinelenmesinin ve varsayılan değerin sonucu nasıl değiştirdiğinin ölçümü.

Önceki derste, Tepkisel Akışlar, bir değerin zaman içinde nasıl aktığını gördük. Akış bitince değer de gider. Uygulama kapanıp yeniden açıldığında yaşaması gereken küçük değerler için ise başka bir yer gerekir: tema seçimi, son açılan sekme, bir ipucunun gösterilip gösterilmediği. Bu konu o yeri anlatır; adı **kalıcılaştırma**dır. Kursun sorusu burada da değişmez: **bu işlem yinelenirse ya da sırası değişirse sonuç değişir mi?** Dört yanıt vardır ve kurs boyunca adları sabittir: **sağlam**, **yinelemeye duyarlı**, **sıraya duyarlı** ve **çift duyarlı**. Üst ad **duyarlılık**tır ve kural bunu etiketten değil, işlemin durumda bıraktığı sonuçtan okur.

Ölçüm çekirdek dosyayla yapılır. Her ders onu aynen verir.

```swift
// Replay.swift — bir islemin yinelemeye ve siraya duyarliligini siniflandirir
struct Trial<State: Equatable & CustomStringConvertible> {
    let label: String
    let once: State
    let twice: State
    let forward: State
    let backward: State

    var sensitivity: String {
        switch (once != twice, forward != backward) {
        case (false, false): return "saglam"
        case (true, false): return "yinelemeye-duyarli"
        case (false, true): return "siraya-duyarli"
        case (true, true): return "cift-duyarli"
        }
    }

    func report() -> String {
        print("\(label) | bir=\(once) iki=\(twice) ab=\(forward) ba=\(backward) -> \(sensitivity)")
        return sensitivity
    }
}

func replay<State>(_ label: String, from start: State, _ a: (State) -> State, then b: (State) -> State) -> Trial<State> {
    Trial(label: label, once: a(start), twice: a(a(start)), forward: b(a(start)), backward: a(b(start)))
}

func countSensitivities(_ results: [String]) {
    for name in ["saglam", "yinelemeye-duyarli", "siraya-duyarli", "cift-duyarli"] {
        print("\(name): \(results.filter { $0 == name }.count)")
    }
}
```

`bir` işlemin başlangıç durumuna bir kez, `iki` iki kez uygulanmasıdır; `ab` önce `a` sonra `b`, `ba` ise tersi sıradır. `bir` ile `iki` ayrışırsa işlem yinelemeye duyarlıdır; `ab` ile `ba` ayrışırsa sıraya duyarlıdır. Bu derste ölçülen işlemler bir tercih deposunun saf Swift modeline uygulanır. Model, işlemin duruma ne yaptığını kurar; deponun ya da diskin kendisi değildir.

## Tercih Deposu Ne İçindir

Platformun tercih deposu anahtar-değer çiftleri saklar. Değerler dizgi, sayı, mantıksal değer, tarih ve bunlardan kurulu diziler ile sözlükler gibi basit tiplerdir. Bir `UserDefaults.standard` nesnesi uygulamanın tamamı için ortaktır: bir ekranın yazdığı değeri başka bir ekran aynı süreçte hemen okur. Bu kolaylık ölçümün asıl konusudur, çünkü ortak bir anahtara yazan iki ekran birbirinin değerini fark etmeden ezebilir.

Depo küçük ve gizli olmayan değerler içindir. Büyük veriler dosya sistemine ya da veritabanına gider; parola ve belirteç gibi gizli değerler bir sonraki dersin konusu olan anahtarlığa gider. Tercih deposuna yazılan bir değer okunabilir biçimde durur.

```swift
import Foundation

UserDefaults.standard.register(defaults: ["theme": "sistem", "launchCount": 0])
UserDefaults.standard.set("koyu", forKey: "theme")

let count = UserDefaults.standard.integer(forKey: "launchCount")
UserDefaults.standard.set(count + 1, forKey: "launchCount")

UserDefaults.standard.removeObject(forKey: "theme")
```

Bu parça KAYNAK'tır; platformun kendi sınıfını çağırdığı için burada koşmaz. Üç şey okunur: `set(_:forKey:)` değeri yazar, `integer(forKey:)` anahtar yoksa `0` döner, `removeObject(forKey:)` yazılmış değeri siler. Aşağıdaki model bu adları aynen taşır.

## Tercih Deposunun Modeli

Model iki ayrı sözlük tutar: açıkça yazılan değerler ve kaydedilmiş varsayılan değerler. Okuma önce yazılanı, yoksa varsayılanı verir. Durumun karşılaştırılması ve basılması da bu **çözülmüş** değere göre yapılır.

```swift
// PreferenceModel.swift — tercih deposunun saf Swift modeli
struct PreferenceBox: Equatable, CustomStringConvertible {
    private(set) var stored: [String: String] = [:]
    private(set) var registered: [String: String] = [:]

    var resolved: [String: String] {
        registered.merging(stored) { _, explicit in explicit }
    }

    var description: String {
        let pairs = resolved.keys.sorted().map { "\($0)=\(resolved[$0] ?? "-")" }
        return pairs.isEmpty ? "-" : pairs.joined(separator: ",")
    }

    static func == (lhs: PreferenceBox, rhs: PreferenceBox) -> Bool {
        lhs.resolved == rhs.resolved
    }

    mutating func register(defaults: [String: String]) {
        registered.merge(defaults) { _, new in new }
    }

    mutating func set(_ value: String, forKey key: String) { stored[key] = value }
    mutating func set(_ value: Int, forKey key: String) { stored[key] = String(value) }
    mutating func removeObject(forKey key: String) { stored[key] = nil }

    func string(forKey key: String) -> String? { resolved[key] }
    func integer(forKey key: String) -> Int { Int(resolved[key] ?? "") ?? 0 }
}

func registeredBox(_ defaults: [String: String]) -> PreferenceBox {
    var box = PreferenceBox()
    box.register(defaults: defaults)
    return box
}
```

Sözlüklerin sırası belirsiz olduğu için `description` anahtarları sıralayarak basar. Boş durum `-` olarak görünür. Bu da bir seçimdir: kural çözülmüş değerleri karşılaştırır, deponun içinde yazılmış mı yoksa varsayılan mı olduğuna bakmaz.

**KL1.** Model, bir yazmanın aynı süreçteki sonraki okumalarda hemen görüldüğünü varsayar. Değerin diske ne zaman yazıldığı ölçülmedi.

**KL2.** Model iki ekranın tek bir depoyu paylaştığını varsayar. Her ekranın ayrı bir depo nesnesi kullandığı durum kurulmadı.

## Üç İşlemin Ölçümü

Üç işlem kurulur. Birincisi iki ekranın aynı `theme` anahtarına farklı değer yazmasıdır. İkincisi iki ekranın farklı anahtarlara yazmasıdır. Üçüncüsü uygulama her açıldığında sayacı bir artırmaktır.

```swift
// main.swift — iki ekran ve bir acilis sayaci
func writeValue(_ value: String, forKey key: String) -> (PreferenceBox) -> PreferenceBox {
    { box in
        var next = box
        next.set(value, forKey: key)
        return next
    }
}

func incrementLaunchCount(_ box: PreferenceBox) -> PreferenceBox {
    var next = box
    next.set(next.integer(forKey: "launchCount") + 1, forKey: "launchCount")
    return next
}

let sameKey = replay("iki-ekran-ayni-anahtar", from: PreferenceBox(), writeValue("koyu", forKey: "theme"), then: writeValue("acik", forKey: "theme")).report()
let otherKeys = replay("iki-ekran-ayri-anahtar", from: PreferenceBox(), writeValue("koyu", forKey: "theme"), then: writeValue("tr", forKey: "language")).report()
let launches = replay("acilis-sayaci", from: PreferenceBox(), incrementLaunchCount, then: incrementLaunchCount).report()

countSensitivities([sameKey, otherKeys, launches])
```

```
iki-ekran-ayni-anahtar | bir=theme=koyu iki=theme=koyu ab=theme=acik ba=theme=koyu -> siraya-duyarli
iki-ekran-ayri-anahtar | bir=theme=koyu iki=theme=koyu ab=language=tr,theme=koyu ba=language=tr,theme=koyu -> saglam
acilis-sayaci | bir=launchCount=1 iki=launchCount=2 ab=launchCount=2 ba=launchCount=2 -> yinelemeye-duyarli
saglam: 1
yinelemeye-duyarli: 1
siraya-duyarli: 1
cift-duyarli: 0
```

İlk satırda aynı değeri iki kez yazmak sonucu değiştirmedi: `bir` ile `iki` aynı. Ama iki farklı değerin sırası belirleyici oldu: `ab` `acik`, `ba` `koyu`. Son yazan kazanır; kazananı yazma sırası belirler. Bu yüzden sınıf **sıraya duyarlı**dır.

İkinci satırda iki ekran farklı anahtarlara yazıyor. Yazma sırası sonucu değiştirmiyor ve aynı yazmayı yinelemek de değiştirmiyor. Sınıf **sağlam**dır. Bu satır, sorunun yazmanın kendisinde değil, iki yazmanın aynı anahtarı paylaşmasında olduğunu gösterir.

Üçüncü satırda sayaç bir kez uygulandığında `1`, iki kez uygulandığında `2` oldu. Sıra bu işlem için anlamsızdır, çünkü iki işlem aynıdır; yineleme ise sonucu değiştirir. Sınıf **yinelemeye duyarlı**dır. Sayacın artması bir okumayı ve ardından bir yazmayı gerektirir; yazılan değer okunan değere bağlıdır. Aynı anahtara sabit bir değer yazan işlem ise yinelendiğinde değişmez. Fark budur: `set` sabit değerle sağlamdır, okuyup artıran işlem değildir.

Sayım bir sağlam, bir yinelemeye duyarlı, bir sıraya duyarlıdır.

## Varsayılan Değerler

Bir anahtar hiç yazılmadıysa okuma `nil` ya da `0` verir. Her yerde bu durumu ayrı ayrı karşılamak yerine uygulama başlarken varsayılan değerleri kaydeder: `register(defaults:)`. Kaydedilen varsayılanlar yazılmış değer sayılmaz; yazılı değer yoksa devreye giren geri dönüş değeridir. Kayıt her açılışta yeniden yapılır, çünkü varsayılan değerler kalıcı olarak saklanmaz.

Varsayılan değer, anahtar silinince de okunan değerdir. `removeObject(forKey:)` yazılmış değeri kaldırır ve okuma varsayılana düşer. Bu iki işlemin birlikte nasıl davrandığı, varsayılan değere göre değişir ve bu dersin sınırlayıcı ölçümünün konusudur.

## Sık Yapılan Hatalar

- Aynı anahtarı iki ekranda farklı amaçlarla kullanmak. Son yazan kazanır ve birbirinin değerini sessizce ezer.
- Sayacı okuyup artırmak ve yinelemeyi hesaba katmamak. Düğmeye iki kez basılması ya da açılış kodunun iki kez koşması sayacı iki artırır.
- Gizli bir değeri tercih deposuna yazmak. Depo bir gizlilik sınırı sağlamaz; gizli değerler anahtarlığa aittir.
- Varsayılan değeri her okuma noktasında ayrı yazmak. Değer iki yerde ayrışır ve hangisinin geçerli olduğu belirsizleşir.
- Büyük bir veriyi tercih deposuna koymak. Depo küçük değerler için tasarlanmıştır.
- Anahtar adını dizgi olarak çoğaltmak. Yazım hatası yeni bir anahtar açar ve eski değer okunmaya devam eder.

## Ölçülen Sonucun Yorumu

İki ekran aynı anahtara yazınca sonucu yazma sırası belirledi; aynı değeri yinelemek zarar vermedi. Farklı anahtarlara yazınca iki ekran birbirini etkilemedi. Açılış sayacı ise yinelendiğinde değişti. Üç satır, tercih deposunun tek bir davranışı olmadığını gösterir: aynı `set` çağrısı, anahtarın kim tarafından paylaşıldığına ve değerin sabit mi hesaplanmış mı olduğuna göre başka bir sınıfa düşer.

Pratikte iki alışkanlık yeterlidir. Birincisi her anahtarın tek bir sahibi olmasıdır: değeri yazan ekran bilinir ve başka bir ekran aynı anahtara yazmaz. İkincisi sayaç ve benzeri hesaplanmış değerlerin yinelemeye karşı korunmasıdır: işlem bir kez koşmuş sayılıyorsa bunun ayrıca kaydı tutulur. Bu iki alışkanlık yukarıdaki ilk ve üçüncü satırın kaynağını yazım aşamasında ortadan kaldırır. Ölçüm bunun gerekli olduğunu gösterir, ama iki ekranın gerçekten ne zaman yazacağını göstermez.

**KL3.** Bu ölçüm artırmayı bir okuma ve ardından bir yazma olarak, kesintisiz kurar. İki artırmanın aynı anda koştuğu durum kurulmadı.

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

Kural tek bir başlangıç durumundan ölçer. Aynı işlem çifti farklı başlangıçta farklı sınıfa düşebilir. Burada işlem çifti şudur: `a` anahtarı siler, `b` aynı anahtara `koyu` yazar. Başlangıç, varsayılan değerin ne olduğuna göre üç biçimde kurulur.

```swift
// main.swift — ayni islem cifti uc farkli baslangicta
func removeValue(forKey key: String) -> (PreferenceBox) -> PreferenceBox {
    { box in
        var next = box
        next.removeObject(forKey: key)
        return next
    }
}

func writeDark(_ box: PreferenceBox) -> PreferenceBox {
    writeValue("koyu", forKey: "theme")(box)
}

func writeValue(_ value: String, forKey key: String) -> (PreferenceBox) -> PreferenceBox {
    { box in
        var next = box
        next.set(value, forKey: key)
        return next
    }
}

let noDefault = replay("varsayilan-yok", from: PreferenceBox(), removeValue(forKey: "theme"), then: writeDark).report()
let sameDefault = replay("varsayilan-koyu", from: registeredBox(["theme": "koyu"]), removeValue(forKey: "theme"), then: writeDark).report()
let otherDefault = replay("varsayilan-sistem", from: registeredBox(["theme": "sistem"]), removeValue(forKey: "theme"), then: writeDark).report()

countSensitivities([noDefault, sameDefault, otherDefault])
```

```
varsayilan-yok | bir=- iki=- ab=theme=koyu ba=- -> siraya-duyarli
varsayilan-koyu | bir=theme=koyu iki=theme=koyu ab=theme=koyu ba=theme=koyu -> saglam
varsayilan-sistem | bir=theme=sistem iki=theme=sistem ab=theme=koyu ba=theme=sistem -> siraya-duyarli
saglam: 1
yinelemeye-duyarli: 0
siraya-duyarli: 2
cift-duyarli: 0
```

Aynı iki işlem üç başlangıçta iki sınıf verdi. Varsayılan değer yokken ve varsayılan `sistem` iken sıra belirleyicidir: sil sonra yaz `koyu`, yaz sonra sil ise varsayılana ya da boşa döner. Varsayılan `koyu` iken iki sıra da `koyu` verir, çünkü silmenin düşeceği değer zaten yazılacak değerdir. Sonuç sağlam çıkar. Bu, işlemin her durumda sağlam olduğunu göstermez; yalnız o başlangıçta sıranın görünmediğini gösterir. Varsayılan değeri değiştirmek, hiçbir işlem koduna dokunmadan sınıfı değiştirdi.

Bu ölçümün de sınırı vardır. Kural, değerin deponun içinde yazılmış mı yoksa varsayılandan mı geldiğine bakmaz; yalnız çözülmüş değeri karşılaştırır. Yazılmış `koyu` ile varsayılan `koyu` aynı sayılır.

## Gerçek Uygulamada Nerede Görülür

Ölçülen sıra duyarlılığı yapay bir laboratuvar durumu değildir. Bir uygulamada tema seçimini hem ayarlar ekranı hem de ana ekrandaki hızlı anahtar değiştirebilir. İki ekran aynı anahtara yazdığında kalan değer, hangisinin sonra yazdığına bağlıdır. Kullanıcı ayarlar ekranını açıkken ana ekrandan temayı değiştirirse, ayarlar ekranı kapanırken kendi eski kopyasını geri yazabilir. Sonuç, kullanıcının az önce yaptığı değişikliğin sessizce kaybolmasıdır. Hata raporunda bunun izi yoktur: hiçbir çağrı başarısız olmamıştır, her yazma kendi başına doğru çalışmıştır.

Açılış sayacı ise yineleme duyarlılığının tipik örneğidir. Sayacı bir artıran kod, kullanıcı uygulamayı her açtığında bir kez çalışsın diye yazılmıştır. Aynı kod ekranın yeniden görünmesinde, arka plandan dönüşte ya da bir yeniden denemede de çalışırsa sayaç gerçek açılış sayısından büyük çıkar. Sayaca bağlı bir karşılama ya da bir değerlendirme isteği gibi kararlar da bu yüzden yanlış zamanda tetiklenir. Ölçümün `iki` sütunu tam olarak bu durumu gösterir: işlem yinelendiğinde değer bir daha artmıştır.

Varsayılan değer meselesi daha da sinsidir. Hiç yazılmamış bir anahtarın okunması, uygulamanın yeni kurulduğu ve eski sürümden güncellendiği iki farklı durumda aynı görünür. Sınırlayıcı ölçüm bunu gösterdi: aynı işlem çifti, başlangıç durumuna göre farklı sınıfa düşebilir. Bu yüzden bir tercihin güvenli olup olmadığı yalnız işleme bakılarak söylenemez; işlemin hangi başlangıçtan koştuğu da sorulur.

## Yanlış Kurulum ve Düzeltmesi

En yaygın yanlış kurulum, her ekranın tercihi kendi içinde bir kopya olarak tutmasıdır. Ekran açılırken değeri okur, kullanıcı değiştirdikçe kopyayı günceller ve ekran kapanırken bütün kopyayı depoya yazar. Bu düzende iki ekranın ömürleri çakıştığında sondaki yazma, öteki ekranın değişikliğini ezer.

Düzeltme iki adımdır. Birincisi, yalnız kullanıcının değiştirdiği anahtarı, değiştirdiği anda yazmaktır; ekran kapanırken tüm kopyayı yazmak yerine. Böylece ayrı anahtarlara yazan ekranlar birbirini etkilemez, ki ölçümdeki ayrı anahtar satırı bunu sağlam olarak gösterdi. İkincisi, aynı anahtarı birden çok yerden değiştirmek gerekiyorsa değişikliği tek bir yerden geçirmek ve ekranların depoyu değil o tek yeri dinlemesini sağlamaktır. Bu, sıra sorununu çözmez ama sırayı tek bir yerde görünür kılar; sıraya duyarlı bir işlemin güvenli hâle gelmesinin yolu çoğu zaman sıranın artık değişememesidir.

Sayaç için düzeltme farklıdır. Sayacı artırmak yerine "bu açılış sayıldı mı" bilgisini saklamak, işlemi yinelemeye karşı korur. Aynı oturumda ikinci kez gelen çağrı, işaretin dolu olduğunu görüp bir şey yapmaz. Ölçüm olarak bu, işlemin yinelemeye duyarlı olmaktan çıkıp sağlam olması demektir; ama bunu bu derste ayrıca ölçmedik, yalnız yönünü söylüyoruz.

## KAYNAK Parçayı Okumak

Dersin başındaki adsız parça derlenmez, çünkü gerçek bir tercih deposu platforma ve uygulamanın yaşam döngüsüne bağlıdır. Parça yine de okunmaya değerdir. Okuma ve yazma çağrıları, ölçtüğümüz modeldeki iki işlemin birebir karşılığıdır: modelde bir anahtarı okuyup yazdığımız yerde, gerçek parça aynı anahtarı okuyup yazar. Aradaki fark, gerçek depoda yazmanın hemen diske gitmemesi ve okumaların başka bir süreçten gelen değişikliği görmesinin garanti edilmemesidir. Bu fark KL1 ve KL2 olarak kayıtlıdır ve ölçülen sınıfı değiştirmez; yalnız gerçek davranışın modele ne kadar yakın olduğunu söyler.

Parçadaki varsayılan değer verme biçimi de önemlidir. Okuma çağrısı anahtar yoksa bir değer döndürür ve bu değer, kodun başka bir yerinde ayrıca tanımlı bir varsayılanla çelişebilir. İki yerde tanımlı varsayılan, ölçümdeki iki farklı başlangıç durumunun gerçek hayattaki karşılığıdır. Varsayılanı tek bir yerde tanımlamak, sınırlayıcı ölçümün gösterdiği sınıf kaymasını baştan engeller.

## Özet

- Tercih deposu küçük ve gizli olmayan değerleri anahtar-değer olarak saklar; gizli değerler anahtarlığa aittir.
- İki ekran aynı anahtara farklı değer yazınca sonucu yazma sırası belirler (`ab` `acik`, `ba` `koyu`); farklı anahtarlar birbirini etkilemez.
- Okuyup artıran bir işlem, açılış sayacı gibi, yinelendiğinde sonucu değiştirir (`1`, sonra `2`); sabit değer yazmak değiştirmez.
- Varsayılan değer, aynı sil ve yaz çiftini sağlamdan sıraya duyarlıya taşıyabilir; sonuç tek başlangıçtan ölçülür.
- Üç işlemin sayımı bir sağlam, bir yinelemeye duyarlı, bir sıraya duyarlıdır; sınırlayıcı ölçüm bir sağlam, iki sıraya duyarlı verir.
- Bu dersin duyarlılığı: **sıraya duyarlı**.

## Sonraki Adım

Tercih deposu değerlerin açık durduğu bir yerdir. Sıradaki derste, Anahtarlık, gizli değerlerin saklandığı depoya geçilecek ve aynı öğeyi ikinci kez eklemenin durum kodunu nasıl değiştirdiği ölçülecek.
