---
title: Anahtarlık
source: 'https://academia.sh/tr/kurslar/ios-veri-ve-ag/anahtarlik'
course: 'Veri, Ağ ve Eşzamanlılık'
language: tr
updated: '2026-10-21T05:03:10+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Gizli değerlerin tercih deposuna değil anahtarlığa ait olduğunu ayırır'
    - 'Aynı öğeyi ikinci kez eklemenin durum kodunu değiştirdiğini ölçer'
    - 'Güncelle yoksa ekle işleminin iki farklı değerde sıraya duyarlı olduğunu doğrular'
    - 'Durum kodunun karşılaştırılan duruma dahil edilip edilmemesinin sınıfı değiştirdiğini gösterir'
---

# Anahtarlık

Gizli değerlerin saklandığı yerde aynı öğeyi ikinci kez eklemenin durum kodunu değiştirmesinin, güncelle yoksa ekle işleminin ise sıraya duyarlı olmasının ölçümü.

Önceki derste tercih deposunun değerleri açık durduğunu gördük. Parola, oturum belirteci ya da bir uygulama anahtarı gibi gizli değerler için platformun ayrı bir deposu vardır: **anahtarlık**. Anahtarlık değerleri şifreli saklar, uygulama silinip yeniden kurulsa bile bazı öğeleri korur ve erişimi öğe başına denetler. Karşılığında bir bedel öder: işlemler bir sonuç kodu döndürür ve bu kod, bir öğenin zaten var olup olmadığını söyler. Kursun sorusu aynı kalır: **bu işlem yinelenirse ya da sırası değişirse sonuç değişir mi?** Bu derste kod da sonucun parçası olur.

```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. Aşağıdaki model anahtarlığın saf Swift karşılığıdır: bir öğe, bir hesap adıyla bir değerden ibarettir. Model platformun kendisi değildir; yalnız işlemin durumda bıraktığı sonucu kurar.

## Ekleme ve Durum Kodu

Anahtarlığa öğe eklemenin platform çağrısı şöyle okunur. Parça KAYNAK'tır; gerçek bir anahtarlık gerektirdiği için burada koşmaz.

```swift
import Security

let query: [String: Any] = [
    kSecClass as String: kSecClassGenericPassword,
    kSecAttrAccount as String: "token",
    kSecValueData as String: Data("a1".utf8)
]

let first = SecItemAdd(query as CFDictionary, nil)   // errSecSuccess
let second = SecItemAdd(query as CFDictionary, nil)  // errSecDuplicateItem
```

İkinci çağrı aynı sınıf ve aynı hesap adıyla geldiği için yeni bir öğe oluşturmaz; `errSecDuplicateItem` döner. Mevcut değer olduğu gibi kalır. Yani ikinci eklemenin etkisi yoktur ama sonucu vardır, ve bu sonuç bir hata kodudur. Varolan bir öğeyi değiştirmek için ayrı bir çağrı gerekir: `SecItemUpdate`. Öğe yoksa o çağrı da `errSecItemNotFound` verir. Uygulamalar bu iki çağrıyı **güncelle yoksa ekle** biçiminde birleştirir: önce güncellemeyi dener, öğe yoksa ekler.

## Anahtarlığın Modeli

Model, hesap adından değere bir sözlük ve son işlemin durum kodunu tutar. Durum kodu karşılaştırmaya girer; `includesStatus` bunu kapatabilir ve sınırlayıcı ölçüm bundan yararlanır.

```swift
// KeychainModel.swift — anahtarligin saf Swift modeli
struct KeychainBox: Equatable, CustomStringConvertible {
    static let success = 0
    static let duplicateItem = -25299
    static let itemNotFound = -25300

    private(set) var items: [String: String]
    private(set) var status: Int
    let includesStatus: Bool

    init(includesStatus: Bool = true, items: [String: String] = [:]) {
        self.includesStatus = includesStatus
        self.items = items
        self.status = KeychainBox.success
    }

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

    static func == (lhs: KeychainBox, rhs: KeychainBox) -> Bool {
        lhs.description == rhs.description
    }

    mutating func add(_ value: String, account: String) {
        if items[account] != nil {
            status = KeychainBox.duplicateItem
        } else {
            items[account] = value
            status = KeychainBox.success
        }
    }

    mutating func update(_ value: String, account: String) {
        if items[account] != nil {
            items[account] = value
            status = KeychainBox.success
        } else {
            status = KeychainBox.itemNotFound
        }
    }

    mutating func addOrUpdate(_ value: String, account: String) {
        update(value, account: account)
        if status == KeychainBox.itemNotFound {
            add(value, account: account)
        }
    }
}

func adding(_ value: String, account: String = "token") -> (KeychainBox) -> KeychainBox {
    { box in
        var next = box
        next.add(value, account: account)
        return next
    }
}

func upserting(_ value: String, account: String = "token") -> (KeychainBox) -> KeychainBox {
    { box in
        var next = box
        next.addOrUpdate(value, account: account)
        return next
    }
}
```

Durum kodu sayıları platformun sabitleriyle aynı tutulmuştur: başarı `0`, tekrar eden öğe `-25299`, öğe bulunamadı `-25300`. Karşılaştırma ve basım aynı `description` üzerinden yürür; böylece bir sınıflandırmada görünen şey ile eşitlikte sayılan şey ayrışmaz.

**KL4.** Model, öğenin kimliğini yalnız hesap adı sayar. Platformda kimlik sınıf, servis ve hesap gibi niteliklerin birleşimidir; tek hesap adıyla modellemek ölçümü sadeleştirir.

**KL5.** Model, durum kodlarını yukarıdaki sabitlerle üretir. Platformun gerçek geri dönüşü, erişim denetimi ve aygıt kilidi ölçülmedi.

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

Üç işlem kurulur. Birincisi aynı değeri iki kez eklemektir. İkincisi güncelle yoksa ekle işleminin iki farklı değerle koşmasıdır. Üçüncüsü iki farklı değeri doğrudan eklemektir.

```swift
// main.swift — ekleme ve guncelle yoksa ekle
let sameValue = replay("ekle-ayni-deger", from: KeychainBox(), adding("a1"), then: adding("a1")).report()
let upsert = replay("guncelle-yoksa-ekle", from: KeychainBox(), upserting("a1"), then: upserting("b2")).report()
let twoValues = replay("ekle-iki-deger", from: KeychainBox(), adding("a1"), then: adding("b2")).report()

countSensitivities([sameValue, upsert, twoValues])
```

```
ekle-ayni-deger | bir=token=a1 durum=0 iki=token=a1 durum=-25299 ab=token=a1 durum=-25299 ba=token=a1 durum=-25299 -> yinelemeye-duyarli
guncelle-yoksa-ekle | bir=token=a1 durum=0 iki=token=a1 durum=0 ab=token=b2 durum=0 ba=token=a1 durum=0 -> siraya-duyarli
ekle-iki-deger | bir=token=a1 durum=0 iki=token=a1 durum=-25299 ab=token=a1 durum=-25299 ba=token=b2 durum=-25299 -> cift-duyarli
saglam: 0
yinelemeye-duyarli: 1
siraya-duyarli: 1
cift-duyarli: 1
```

İlk satırda öğe iki durumda da `a1`'dir; ayrışan yalnız durum kodudur: `0` ve `-25299`. Ekleme etkisiz kalmış, ama sonuç değişmiş. Sıra anlamsızdır, çünkü iki işlem aynıdır. Sınıf **yinelemeye duyarlı**dır. Bu, bir önceki dersin sayacından farklı bir yinelemedir: değer değil, işlemin bildirdiği sonuç değişir.

İkinci satırda güncelle yoksa ekle işlemi yinelendiğinde sonuç değişmez: öğe ve kod aynı kalır. Etkisizlik yinelemeyi çözmüştür. Ama iki farklı değerde sırayı çözmez: `ab` `b2`, `ba` `a1`. Son yazan kazanır ve kazananı sıra belirler. Sınıf **sıraya duyarlı**dır. Kural eşitsizliği görür; nedeni, yani ikinci çağrının güncelleme yolundan geçtiğini görmez.

Üçüncü satır iki etkiyi birleştirir. İkinci ekleme başarısız olur, bu yüzden hem yineleme hem sıra ayrışır. Öğe ilk gelenin değeriyle kalır ve ilk gelen sıraya bağlıdır. Sınıf **çift duyarlı**dır.

Sayım bir yinelemeye duyarlı, bir sıraya duyarlı, bir çift duyarlıdır; sağlam yoktur.

## Neden Güncelle Yoksa Ekle

Doğrudan ekleme, öğenin önceden olmadığını varsayar. Uygulama her açılışta belirteci eklerse ikinci açılıştan itibaren çağrı hata döner ve eski değer kalır; yeni değer sessizce yazılmaz. Bu, belirteç yenilendiğinde eski belirteçle çalışmaya devam etmenin yaygın bir nedenidir. Güncelle yoksa ekle bu hatayı ortadan kaldırır: yineleme artık hata üretmez. Bedeli yukarıdaki ikinci satırdır. İki ayrı kaynak aynı hesaba farklı değer yazarsa kazananı sıra belirler.

Pratikte iki karar yeterlidir. Eklemenin sonucu denetlenir: `errSecDuplicateItem` bir arıza değil, öğenin zaten var olduğunun bilgisidir. Birden çok kaynağın yazabileceği bir hesap varsa, hangi kaynağın son sözü söyleyeceği tasarımda yazılır.

## Sık Yapılan Hatalar

- Gizli bir değeri tercih deposuna yazmak. Anahtarlık bunun için vardır.
- Ekleme sonucunu denetlememek. Hata dönse bile uygulama başarılı sanır ve eski değerle devam eder.
- Her açılışta doğrudan eklemek. İlk açılışta çalışır, sonrakilerde sessizce başarısız olur.
- Güncelle yoksa ekle yolunu iki ayrı yerden çağırmak. Son yazanı sıra belirler ve hata ayıklama güçleşir.
- Silme ve eklemeyi tek adımmış gibi düşünmek. İki çağrı arasında uygulama kesilirse öğe kaybolur.
- Hesap adını dizgi olarak çoğaltmak. Yazım hatası yeni bir öğe açar.

**KL6.** Model, çağrıları kesintisiz ve tek iş parçacığında koşturur. İki çağrının aynı anda koştuğu durum kurulmadı.

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

Birinci sınır, durum kodunun karşılaştırılan duruma girip girmediğidir. Kural eşitsizliği görür; durum kodu duruma dahil değilse ikinci ekleme görünmez. Aynı üç eklemeyi `includesStatus: false` ile koşalım. Üçüncü satırda ise aynı işlem çiftini, öğenin baştan var olduğu bir başlangıçta ölçelim.

```swift
// main.swift — durum kodu olmadan ve dolu baslangicta
let plain = KeychainBox(includesStatus: false)
let withoutStatus = replay("durumsuz-ayni-deger", from: plain, adding("a1"), then: adding("a1")).report()
let withoutStatusTwo = replay("durumsuz-iki-deger", from: plain, adding("a1"), then: adding("b2")).report()
let seeded = replay("durumlu-dolu-baslangic", from: KeychainBox(items: ["token": "a1"]), adding("a1"), then: adding("a1")).report()

countSensitivities([withoutStatus, withoutStatusTwo, seeded])
```

```
durumsuz-ayni-deger | bir=token=a1 iki=token=a1 ab=token=a1 ba=token=a1 -> saglam
durumsuz-iki-deger | bir=token=a1 iki=token=a1 ab=token=a1 ba=token=b2 -> siraya-duyarli
durumlu-dolu-baslangic | bir=token=a1 durum=-25299 iki=token=a1 durum=-25299 ab=token=a1 durum=-25299 ba=token=a1 durum=-25299 -> saglam
saglam: 2
yinelemeye-duyarli: 0
siraya-duyarli: 1
cift-duyarli: 0
```

Durum kodu çıkarılınca aynı ekleme çifti **sağlam**a döndü; iki farklı değerde ise çift duyarlı sınıf sıraya duyarlıya indi. İşlem değişmedi, yalnız neye baktığımız değişti. Kodu duruma almak yinelemeyi görünür kılar; almamak onu gizler.

İkinci sınır başlangıç durumudur. Aynı iki ekleme boş başlangıçta yinelemeye duyarlıydı. Öğenin zaten bulunduğu başlangıçta ilk ekleme de hata verir ve durum `-25299`'da sabitlenir: sınıf sağlam. Sağlam çıkan işlem her durumda sağlam değildir; yalnız bu başlangıçta yinelemeyi göstermez.

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

Ekleme işleminin ikinci çağrıda başarısız olması, anahtarlığın en sık karşılaşılan davranışlarından biridir. Bir uygulama oturum anahtarını ilk girişte kaydeder; kullanıcı çıkış yapıp yeniden girdiğinde aynı kod çalışır ve ekleme, öğe zaten var diye reddedilir. Geliştirici bunu çoğunlukla yalnız bir hata kodu olarak görür ve kullanıcının yeni anahtarı hiç saklanmamış kalır: eski anahtar yerinde durur, uygulama ise yenisini kaydettiğini sanır. Ölçümdeki `-25299` durumu bu çakışmanın karşılığıdır ve `ekle-ayni-deger` satırı, aynı değerin ikinci kez eklenmesinde bile durumun değiştiğini gösterir.

Bu ayrım önemlidir, çünkü sonuç değeri aynı kalsa da durum kodu değişmiştir. Kodu okuyan çağıran, ikinci eklemeyi başarısızlık sayabilir ve bir hata ekranı gösterebilir. Durum kodunu hesaba katmadığımız sınırlayıcı satırlarda ise aynı işlem çifti sağlam görünür. Aradaki fark, ölçüde neyin eşitliğe dâhil edildiğidir; kural eşitsizliği görür, ama neyin eşitlik sayıldığını biz seçeriz.

Çıkış ve yeniden giriş dışında, anahtarlık çağrıları uygulamanın yeniden açılışında, bir yedekten geri yüklemede ve iki ayrı uzantının aynı öğeyi paylaştığı durumlarda da yinelenir. Paylaşılan bir öğeyi iki yerin yazması, ölçümdeki iki değerli ekleme satırının gerçek karşılığıdır: hangi değerin kaldığı, hangisinin önce çalıştığına bağlıdır.

## Yanlış Kurulum ve Düzeltmesi

Yaygın yanlış kurulum, önce silip sonra eklemektir. Mantık şöyledir: öğe varsa sil, sonra ekle; böylece çakışma olmaz. Bu düzen iki çağrı arasında öğenin hiç bulunmadığı bir aralık bırakır. Uygulama bu aralıkta kesilirse ya da başka bir yer öğeyi tam o anda okursa, kullanıcı anahtarsız kalır. Ayrıca silme ve ekleme iki ayrı işlemdir; her birinin ayrı bir durum kodu vardır ve ikisini de denetlemek gerekir.

Düzeltme, ölçtüğümüz "güncelle, yoksa ekle" kalıbıdır. Önce güncellemeyi dener, öğe yoksa ekleme yapar. Güncelleme yinelemeye karşı sağlamdır; aynı değerle ikinci güncelleme bir şeyi değiştirmez. Kalıp sırayı ise çözmez: iki farklı değer iki sırayla gelirse son yazılan kalır. Bu yüzden `guncelle-yoksa-ekle` satırı yinelemeye karşı korumalı ama sıraya duyarlı çıktı. Kalıbın sağladığı şey, ilk çakışmanın hataya dönmemesidir; hangi değerin kalacağı sorusunu çözmez.

İkinci yaygın yanlış, durum kodunu yok saymaktır. Ekleme çağrısının sonucunu kontrol etmeyen kod, başarısız bir yazmayı başarılı sanır. Düzeltme basittir ve sıkıcıdır: her çağrının durumunu okumak ve yalnız beklenen kodlarda ilerlemek. Beklenmeyen bir kodda uygulama, kullanıcıya anlaşılır bir sonuç sunmalıdır.

## KAYNAK Parçayı Okumak

Dersin başındaki parça, bir öğenin sorgu sözlüğüyle tanımlandığını gösterir. Sözlükteki sınıf, servis ve hesap alanları öğenin kimliğini oluşturur; değer ise ayrı bir alandır. Çağrı bir durum kodu döndürür ve kod başarıyı ya da hangi çakışmanın olduğunu bildirir. Modelimiz kimliği yalnız hesap adına indirgedi; bu KL4 olarak kayıtlıdır. Gerçekte aynı hesap adı iki farklı serviste iki ayrı öğe olabilir, ve modelde birbirine çakışan iki yazma gerçekte hiç çakışmayabilir.

Parçadaki erişim ayarı da modelin dışında kaldı. Öğenin ne zaman okunabileceği, aygıt kilitliyken erişilip erişilemeyeceği gibi kararlar durum kodlarını doğrudan etkiler. Bu yüzden KL5 durum kodlarının gerçek geri dönüşünü ve erişim denetimini ölçülmemiş olarak kaydeder. Bu derste ölçülen şey kuralın kendisidir: hangi işlem çifti, hangi başlangıçta hangi sınıfa düşer.

## Ölçümden Tasarıma

Ölçümün üç satırı, anahtarlığa yazan her kod için bir kontrol listesine çevrilebilir. İlk soru, işlemin yinelenmesinin ne yaptığıdır. Ekleme çağrısı ikinci kez çalıştığında durum kodu değişir; bu, çağıranın başarısızlığı bir olay sanıp sanmayacağını belirler. İkinci soru, iki farklı değerin gelmesidir: hangisinin kalacağı kural olarak yazılmadıysa sonuç rastlantıya bırakılmıştır. Üçüncü soru, işlemin hangi başlangıçtan koştuğudur. Durumlu satırların dolu başlangıçta sağlam çıkması, öğenin zaten var olduğu bir uygulamada ekleme çağrısının tutarlı biçimde aynı hatayı verdiğini söyler; bu tutarlılık bir güvence değil, yalnız bir gözlemdir.

Pratikte bunun anlamı şudur: giriş akışında anahtarı yazan kod, yazmadan önce öğenin var olup olmadığını varsaymamalıdır. Güncelle-yoksa-ekle kalıbı bu varsayımı kaldırır. Çıkış akışında ise öğeyi silen kod, silinecek öğe yoksa da hata vermemelidir; silmenin yinelemeye karşı sağlam olması, çıkış düğmesine iki kez basılan bir kullanıcıyı korur. Bu derste silmeyi ölçmedik, ama aynı soruyu ona da sormak gerekir ve sorunun cevabı, ölçmeden varsaymak yerine ölçmekle bulunur.

Son olarak, anahtarlıktaki değerin sır olduğunu hatırlamak gerekir. Bu dersin çıktıları örnek değerler içerir; gerçek bir anahtarın günlüğe yazılması ya da ekranda gösterilmesi ayrı bir hatadır. Ölçüm kodu sır taşımaz, ama sizin ölçüm kodunuz da taşımamalıdır.

## Özet

- Gizli değerler anahtarlığa aittir; işlemler bir durum kodu döndürür ve bu kod sonucun parçasıdır.
- Aynı öğeyi ikinci kez eklemek öğeyi değiştirmez ama durumu `0`'dan `-25299`'a çevirir; sınıf yinelemeye duyarlıdır.
- Güncelle yoksa ekle yinelemeyi çözer ama iki farklı değerde sırayı çözmez: `ab` `b2`, `ba` `a1`.
- İki farklı değeri doğrudan eklemek hem yinelemeyi hem sırayı ayrıştırır; sınıf çift duyarlıdır.
- Durum kodu karşılaştırmadan çıkarılınca aynı ekleme çifti sağlam görünür; başlangıçta öğe varsa da sağlam görünür.
- Bu dersin duyarlılığı: **yinelemeye duyarlı**.

## Sonraki Adım

Anahtarlık küçük gizli değerleri saklar. Sıradaki derste, Dosya Sistemi, daha büyük verilerin dosyalara yazıldığı yere geçilecek ve bir dosyaya sona eklemenin hem yinelemeye hem sıraya duyarlı olduğu ölçülecek.
