---
title: 'Temsilci Kalıbı'
source: 'https://academia.sh/tr/kurslar/uikit/temsilci-kalibi'
course: 'UIKit ve Köprüleme'
language: tr
updated: '2026-10-21T05:03:15+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Temsilcinin bir nesnenin işini başka bir nesneye devrettiği bir protokol ilişkisi olduğunu ayırır'
    - 'Zayıf referansın nesne serbest kalınca nil olduğunu ve çağrının boşa düştüğünü gerçekten ölçer'
    - 'Bağlı olmayan temsilcinin bıraktığı ekranı bayat olarak sayar'
    - 'Bağlanmamayla yöntemin hiçbir şey yazmamasının aynı bayat sonucu verdiğini doğrular'
---

# Temsilci Kalıbı

Zayıf tutulan temsilcinin bağlanmadığında ya da serbest bırakıldığında çağrının boşa düştüğünün ve ekranın bayat kaldığının, bağlanmamayla yöntemin yazmamasının aynı sonucu verdiğinin ölçümü.

Önceki derste, Koleksiyon Görünümleri, bir görünümün veriyi ve davranışı kendisi tutmayıp başka bir nesneye danıştığını gördük. O danışma ilişkisinin adı **temsilci** (delegate) kalıbıdır. Bir nesne, işin bir kısmını bir **protokole** uyan başka bir nesneye devreder: seçim yapıldığında, kayıt bittiğinde ya da bir satıra dokunulduğunda temsilcinin yöntemi çağrılır. Temsilci bağlı değilse çağrı hiçbir yere gitmez, ve bu sessiz bir kopmadır.

Ölçüm çekirdek dosyayla yapılır.

```swift
// Sync.swift — ekranda gosterilen her alani modelin o anki degeriyle karsilastirir
final class Screen {
    let name: String
    private(set) var shown: [String: String] = [:]

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

    func set(_ key: String, _ value: String) { shown[key] = value }
    func clear(_ key: String) { shown[key] = nil }
}

func verdict(model: String?, shown: String?) -> String {
    switch (model, shown) {
    case (nil, nil): return "olcum-disi"
    case (nil, _?): return "artik"
    case (_?, nil): return "eksik"
    case let (wanted?, displayed?): return wanted == displayed ? "uyumlu" : "bayat"
    }
}

func check(_ label: String, model: [String: String], screen: Screen, keys: [String]) -> [String] {
    keys.map { key in
        let result = verdict(model: model[key], shown: screen.shown[key])
        print("\(label) | \(screen.name).\(key): model=\(model[key] ?? "-") ekran=\(screen.shown[key] ?? "-") -> \(result)")
        return result
    }
}

func countVerdicts(_ verdicts: [String]) {
    for name in ["uyumlu", "bayat", "eksik", "artik"] {
        print("\(name): \(verdicts.filter { $0 == name }.count)")
    }
}
```

Çekirdek, ekranda gösterilen her alanı modelin o anki değeriyle karşılaştırır. Bu derste ayrıca, bir seçici ile onu dinleyen temsilcilerden oluşan saf Swift kodu ölçülür. Bu kod platform çerçevesi içermez, o yüzden gerçekten koşar. Model, kodun ekrana ne yazdığını kurar; çerçevenin kendisi değildir.

## Zayıf Bağlanan Temsilci

Bir ödeme ekranında teslimat adresini bir seçici verir. Seçici adres seçildiğinde temsilciye haber verir; ekranın görevi bu adresi göstermektir. Temsilci seçicide **zayıf referansla** (weak) tutulur: seçici, ekrana sahip çıkarsa ekran da seçiciye sahip çıkar ve ikisi birbirini serbest bırakmayan bir **tutma döngüsüne** (retain cycle) girer. Zayıf referans bunu engeller, ama bedeli vardır: temsilci serbest kalırsa referans `nil` olur.

```swift
// AddressPicker.swift — adres secici ve onu dinleyen temsilciler
protocol AddressPickerDelegate: AnyObject {
    func picker(_ picker: AddressPicker, didSelect address: String)
}

final class AddressPicker {
    weak var delegate: AddressPickerDelegate?

    func choose(_ address: String) {
        delegate?.picker(self, didSelect: address)
    }
}

final class CheckoutScreen: AddressPickerDelegate {
    let screen: Screen

    init(_ screen: Screen) { self.screen = screen }

    func picker(_ picker: AddressPicker, didSelect address: String) {
        screen.set("address", address)
    }
}

final class SilentCheckout: AddressPickerDelegate {
    func picker(_ picker: AddressPicker, didSelect address: String) {}
}
```

`choose(_:)` temsilciyi isteğe bağlı zincirle çağırır: temsilci `nil` ise hiçbir şey olmaz ve hata da çıkmaz. `CheckoutScreen` seçilen adresi ekrana yazar. `SilentCheckout` ise protokole uyar ama yöntemi boştur. İlk ölçümde seçici önce bağlanmadan kullanılır, sonra bağlanır.

```swift
// main.swift — baglanmayan temsilci ekrani bayat birakir, baglaninca uyumlu olur
let screen = Screen("Checkout")
screen.set("address", "Ankara")
var model = ["address": "Ankara"]

let picker = AddressPicker()
let checkout = CheckoutScreen(screen)
let start = check("baslangic", model: model, screen: screen, keys: ["address"])

model["address"] = "Izmir"
picker.choose("Izmir")
let unwired = check("temsilci-yok", model: model, screen: screen, keys: ["address"])

picker.delegate = checkout
picker.choose("Izmir")
let wired = check("temsilci-bagli", model: model, screen: screen, keys: ["address"])

countVerdicts(start + unwired + wired)
```

```
baslangic | Checkout.address: model=Ankara ekran=Ankara -> uyumlu
temsilci-yok | Checkout.address: model=Izmir ekran=Ankara -> bayat
temsilci-bagli | Checkout.address: model=Izmir ekran=Izmir -> uyumlu
uyumlu: 2
bayat: 1
eksik: 0
artik: 0
```

`baslangic` adımında uyumlu. `temsilci-yok` adımında model `Izmir`, ekran `Ankara` ve sonuç `bayat`: seçici adresi seçti, ama kimseye iletmedi. `temsilci-bagli` adımında aynı çağrı bu kez ekrana ulaştı ve satır uyumlu. Sayım iki uyumlu, bir bayat.

**DZ1.** Bu ölçüm temsilcinin yönteminin eşzamanlı çağrıldığını varsayar. Çağrının başka bir kuyrukta mı koştuğu ölçülmedi.

## Serbest Kalan Temsilci

Bağlanmamış bir temsilci akla ilk gelen kopmadır. Daha sinsi olanı, bağlanmış temsilcinin serbest kalmasıdır. Ekran kapanır ya da başka bir nesne onu tutmayı bırakır; zayıf referans `nil` olur ve seçici, bağlıymış gibi çağrı yapmaya devam eder. Bu derste `nil` olma bir tahmin değil, gerçekten ölçülür: referans aşağıda iki kez okunur.

```swift
// main.swift — zayif referans temsilci serbest kalinca nil olur, cagri bosa duser
let screen = Screen("Checkout")
screen.set("address", "Ankara")
let model = ["address": "Bursa"]

let picker = AddressPicker()
var handler: CheckoutScreen? = CheckoutScreen(screen)
picker.delegate = handler
print("temsilci bagli: \(picker.delegate != nil)")

handler = nil
print("temsilci bagli: \(picker.delegate != nil)")

picker.choose("Bursa")
let released = check("temsilci-birakildi", model: model, screen: screen, keys: ["address"])

countVerdicts(released)
```

```
temsilci bagli: true
temsilci bagli: false
temsilci-birakildi | Checkout.address: model=Bursa ekran=Ankara -> bayat
uyumlu: 0
bayat: 1
eksik: 0
artik: 0
```

İlk satır `true`, ikinci satır `false`. Temsilci serbest bırakıldığında zayıf referans kendiliğinden `nil` oldu; sonraki `choose` çağrısı hiçbir yere gitmedi. Ekran `Ankara` olarak kaldı, model `Bursa` ve sonuç `bayat`. Sayım bir bayat.

İlk iki satır modelin bir varsayımı değil, Swift'in zayıf referans davranışının gerçek sonucudur. Üçüncü satırdaki bayat ise bu iki satırın sonucudur: çağrı boşa düştüğü için ekrana yazılan bir şey olmadı.

**DZ2.** Bu ölçüm yalnız `handler` değişkenini `nil` yapmanın serbest bırakma olduğunu varsayar. Başka bir güçlü referansın nesneyi tutmaya devam ettiği durum ölçülmedi.

KAYNAK tarafta aynı bağ, bir görünüm denetleyicisinin bir tablo görünümünü kurarken kendini temsilci yapmasıyla kurulur.

```swift
import UIKit

final class CheckoutController: UIViewController, AddressPickerDelegate {
    let picker = AddressPicker()

    override func viewDidLoad() {
        super.viewDidLoad()
        picker.delegate = self
    }
}
```

`picker.delegate = self` satırı unutulursa derleyici uyarmaz; yalnız ekran bayat kalır.

## Protokol Tasarımı

Temsilci kalıbında bir nesne, kendi adına karar vermesi gereken bir noktada başka bir nesneye sorar ya da haber verir. Soran nesne **temsilcinin** (delegate) kim olduğunu bilmez; yalnız bir **protokole** uyduğunu bilir. Bu sayede sorunun sahibi ile cevabın sahibi birbirinden ayrılır. Hazır görünümlerin çoğu bu kalıpla çalışır: tablo görünümü, seçim denetleyicileri ve metin alanları kendi davranışlarını temsilcilerine bırakır.

İyi bir temsilci protokolü üç özellik taşır. Birincisi, yordam adı olayı bir fiil kalıbıyla anlatır: bir şey olacak (`will`), olmakta (`did`) ya da onay gerekiyor (`should`). İkincisi, ilk parametre olayı gönderen nesnedir; böylece tek bir temsilci birden çok nesneye hizmet edebilir ve hangisinden geldiğini ayırt eder. Üçüncüsü, protokol küçüktür: her yordam bir karar ya da bildirim taşır, protokolün kendisi bir sınıfın bütün davranışını tarif etmez.

```swift
import UIKit

protocol AddressPickerDelegate: AnyObject {
    func addressPicker(_ picker: AddressPicker, didSelect address: String)
    func addressPickerDidCancel(_ picker: AddressPicker)
}
```

Protokol `AnyObject` ile sınırlanmıştır, yani yalnız sınıflar uyabilir. Bunun nedeni bir sonraki başlıktadır.

## Zayıf Referans

Temsilci tutan nesne, temsilciyi **zayıf referansla** (weak reference) saklar. Nedeni bellek döngüsüdür: ekran seçiciyi tutar, seçici de ekranı temsilci olarak tutarsa iki nesne birbirini bırakmaz ve ikisi de hiç serbest kalmaz. Zayıf referans nesneyi ayakta tutmaz; temsilci başka yerde yok olursa referans kendiliğinden `nil` olur.

```swift
import UIKit

final class AddressPicker {
    weak var delegate: AddressPickerDelegate?

    func choose(_ address: String) {
        delegate?.addressPicker(self, didSelect: address)
    }
}
```

`weak` yalnız sınıf türleri için geçerlidir; protokolün `AnyObject` ile sınırlanması bu yüzden gereklidir. Çağrı, temsilci `nil` ise sessizce atlanır; bu da bu dersin asıl tuzağıdır. Temsilci bağlanmadıysa seçici çalışır, hata vermez ve hiçbir şey olmaz.

## Bağlanmayan Temsilci

Temsilcinin hiç atanmaması, kalıbın en yaygın hatasıdır. Çağrı iyimser zincirleme ile yapıldığı için çökme yoktur, uyarı yoktur; ekran yalnızca tepki vermez. Bu dersin birinci ve ikinci ölçümleri bunu gösterir. Temsilci atanmadan yapılan seçim ekrana ulaşmaz ve satır bayat kalır. Atandıktan sonra aynı seçim uyumlu sonuç verir. İkinci ölçüm ayrıca zayıf referansı gerçekten sınar: ekran serbest kaldığında seçicinin temsilci bağı `nil` olur ve sonraki seçimler yine kimseye ulaşmaz.

## Temsilci mi Kapanış mı

Aynı iş çoğu zaman bir kapanışla da yapılabilir. Seçici, seçim tamamlandığında çağıracağı bir kapanışı özellik olarak alabilir. Tek bir olay için bu daha kısadır ve protokol yazmayı gerektirmez. Birden çok ilişkili olay varsa, örneğin seçim, iptal ve doğrulama sorusu, protokol daha okunaklıdır ve gönderen nesne parametre olarak geçtiği için tek temsilci birden çok nesneye hizmet edebilir.

Kapanışlar da bellek döngüsü doğurabilir: kapanış ekranı, ekran seçiciyi tutuyorsa döngü kurulur ve zayıf referans kapanışın içinde ayrıca yazılmalıdır. Temsilci kalıbında bu risk tek bir yerde, `weak` anahtar sözcüğünde toplanır.

## Sık Yapılan Hatalar

- Temsilciyi güçlü referansla tutmak. Döngü oluşur ve iki nesne de hiç serbest kalmaz.
- Temsilciyi atamayı unutmak. Çağrı sessizce atlanır; çıktı yoktur.
- Protokolü `AnyObject` ile sınırlamamak. Zayıf referans yazılamaz ve derleyici hata verir.
- Gönderen nesneyi parametre olarak geçmemek. Tek temsilci birden çok nesneye hizmet verdiğinde hangisinin çağırdığı bilinmez.
- Temsilcide ağır iş yapmak. Çağrı seçicinin akışında koşar ve uzun işler ekranı bloke eder.
- Temsilciye veri sormak için kullanılan protokol ile bildirim için kullanılan protokolü tek yerde toplamak. Birincisi cevap bekler, ikincisi beklemez; bu ayrım isimlendirmede korunur.

## Ölçülen Sonucun Yorumu

İlk ölçümde başlangıçta ekran modelle uyumluydu. Model `Izmir` olunca, temsilci bağlı olmadığı için ekran `Ankara` kaldı ve satır bayat çıktı. Temsilci bağlanınca aynı seçim ekrana ulaştı ve satır uyumlu oldu. İkinci ölçümde zayıf referans gerçekten ölçüldü: bağ önce `true`, ekran serbest kalınca `false` basıldı ve sonraki seçim kimseye ulaşmadığı için satır bayat çıktı. Üçüncü ölçümde iki farklı ekran aynı sonuca vardı: biri temsilci bağlanmadığı için, öbürü temsilci bağlı olduğu hâlde seçimi ekrana yazmadığı için bayat kaldı. Çıktı ikisini ayırt etmez.

Sonuç şudur: temsilci kalıbı hata üretmeden başarısız olabilir. Bağın kopması da, bağlı temsilcinin işini yapmaması da aynı sessiz görüntüyü verir ve bunu görmenin tek yolu çıktıyı modelle karşılaştırmaktır.

Pratikte iki yoklama yeterlidir. Birincisi, temsilcinin atandığı yerin tek ve açık olmasıdır: ekran kurulurken seçici oluşturulur ve temsilci hemen atanır, böylece atama arada kalmaz. İkincisi, temsilci çağrısının arkasına sessiz bir yol bırakmamaktır. Çağrının sonucu önemliyse, temsilci yoksa uyarı veren bir koşul eklenir ya da geliştirme sırasında bir kontrol ile durdurulur. Sessiz atlama, kalıbın rahat yanıdır ama ölçümün gösterdiği gibi bir hatanın görünmesini de engeller. Bu iki alışkanlık, ölçümde görülen bayat satırların büyük bölümünü daha yazım aşamasında önler; çünkü bağın kurulduğu yer tek olduğunda, bağın kopması ya da hiç kurulmaması da tek bir yerde aranır ve her ekranda ayrı ayrı izlenmez.

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

Bayat sonucunun iki ayrı sebebi olabilir: temsilci hiç bağlanmamış olabilir ya da bağlıdır ama yöntem bir şey yazmıyordur. Biri bağlantıyı unutmaktır, öteki yöntemin içini boş bırakmaktır. **Kural kimin unuttuğunu okumaz.** Model yalnız ekranda ne yazıldığına bakar.

```swift
// main.swift — baglanmama ile yontemin yazmamasi ayni bayat sonucunu verir
let model = ["address": "Bursa"]
let unwiredScreen = Screen("UnwiredCheckout")
let silentScreen = Screen("SilentCheckout")
unwiredScreen.set("address", "Ankara")
silentScreen.set("address", "Ankara")

let unwiredPicker = AddressPicker()
unwiredPicker.choose("Bursa")

let silentPicker = AddressPicker()
let silentDelegate = SilentCheckout()
silentPicker.delegate = silentDelegate
silentPicker.choose("Bursa")

let unwiredResult = check("baglanmadi", model: model, screen: unwiredScreen, keys: ["address"])
let silentResult = check("yontem-yazmadi", model: model, screen: silentScreen, keys: ["address"])

countVerdicts(unwiredResult + silentResult)
```

```
baglanmadi | UnwiredCheckout.address: model=Bursa ekran=Ankara -> bayat
yontem-yazmadi | SilentCheckout.address: model=Bursa ekran=Ankara -> bayat
uyumlu: 0
bayat: 2
eksik: 0
artik: 0
```

İki satır da `bayat`, sayım iki bayat. Biri hiç çağrılmadı, öteki çağrıldı ve hiçbir şey yazmadı; çıktı ikisini ayırt etmiyor. Bu bir sınırdır: sonucun düzeltilmesi için çağrının nerede koptuğunun ayrıca izlenmesi gerekir, model bunu söylemez.

**DZ3.** Bu ölçüm iki sebebi aynı model değeriyle karşılaştırır. Temsilcinin yöntemi çağrıldığında geçirilen argümanın doğruluğu ayrıca ölçülmedi.

## Özet

- Temsilci, bir nesnenin işinin bir kısmını bir protokole uyan başka bir nesneye devrettiği kalıptır.
- Temsilci zayıf tutulur ki tutma döngüsü oluşmasın; temsilci serbest kalınca zayıf referans `nil` olur ve çağrı boşa düşer.
- Bağlanmamış ya da serbest bırakılmış temsilci ekranı bayat bırakır; bağlanınca ekran uyumlu olur.
- Zayıf referansın `nil` olması bu derste gerçekten ölçüldü.
- Bağlanmamayla yöntemin hiçbir şey yazmaması aynı bayat sonucunu verir; kural kimin unuttuğunu okumaz.
- Bu dersin uyumu: **bayat**.

## Sonraki Adım

Temsilci nesneler arası bağı kurar; çizim ise bir görünümün kendi yüzeyine ne yazdığını belirler. Sıradaki derste `draw(_:)` ile çizilen bir görünümün, yeniden çizim istenmediğinde eski değeri göstermeye devam ettiği ölçülecek.
