---
title: 'Gözlemlenebilir Nesneler'
source: 'https://academia.sh/tr/kurslar/swiftui/gozlemlenebilir-nesneler'
course: 'SwiftUI ile Arayüz'
language: tr
updated: '2026-10-21T05:03:14+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Gözlemlenebilir nesnenin özelliğini okuyan görünümlerin gözlem tetiğiyle yeniden hesaplandığını ölçer'
    - 'Gerçek bir gözlem izlemesiyle yalnız okunan özelliğin değişiminin bildirim ürettiğini gösterir'
    - 'Ekranın kendi durumunun nesne özelliğinden ayrı, yerel kaldığını ayırır'
    - 'Nesne düzeyinde bildirimin özellik düzeyinden farkını modelde kurar'
---

# Gözlemlenebilir Nesneler

Gözlemlenebilir bir nesnenin yalnız okunan özelliğinin değişimde yeniden hesaplama ürettiğinin, gerçek gözlem izlemesiyle doğrulanmasının ve nesne düzeyinde bildirimin neyi değiştirdiğinin ölçümü.

Önceki derste çocuk görünüm bir değeri bağlamayla okudu; değer yine bir görünümde yaşıyordu. Bu derste değer bir görünümün içinde değil, ayrı bir nesnede yaşar. Ekranın verisi, ağın ya da diskin sonucu, birden çok ekranın paylaştığı bir liste: bunlar bir görünümün alanı olmak yerine bir **gözlemlenebilir nesne** (observable object) içinde durur. Görünümler nesneyi tutar, özelliklerini okur ve okudukları özellik değişince yeniden hesaplanır. Bu derste bakılan tetik **gözlem**.

Ölçüm çekirdek dosyayla yapılır ve bu derste ilk kez çekirdeğin dışında, derleyicinin kendi gözlem izlemesi de ölçülür.

```swift
// Invalidation.swift — bir degisikligin hangi gorunumu hangi kaynak yoluyla yeniden hesaplattigini siniflandirir
enum Storage {
    case state(owner: String)
    case model(object: String)
    case environment
}

struct ViewNode {
    let name: String
    let parent: String?
    let reads: Set<String>

    init(_ name: String, parent: String?, reads: Set<String>) {
        self.name = name
        self.parent = parent
        self.reads = reads
    }
}

struct Recompute {
    let view: String
    let cause: String
}

struct ViewTree {
    let nodes: [ViewNode]
    let storage: [String: Storage]

    func ancestors(of name: String) -> [String] {
        var found: [String] = []
        var current = nodes.first { $0.name == name }?.parent
        while let next = current {
            found.append(next)
            current = nodes.first { $0.name == next }?.parent
        }
        return found
    }

    func cause(of node: ViewNode, key: String) -> String {
        guard let kind = storage[key] else { return "olcum-disi" }
        switch kind {
        case .environment:
            return "ortam"
        case .model:
            return "gozlem"
        case .state(let owner):
            if owner == node.name { return "yerel" }
            if ancestors(of: node.name).contains(owner) { return "ebeveyn" }
            return "kural-disi"
        }
    }

    func change(_ label: String, key: String) -> [Recompute] {
        let hits = nodes.filter { $0.reads.contains(key) }
            .map { Recompute(view: $0.name, cause: cause(of: $0, key: key)) }
        let skipped = nodes.filter { !$0.reads.contains(key) }.map(\.name)
        let recomputed = hits.isEmpty ? "yok" : hits.map { "\($0.view) \($0.cause)" }.joined(separator: ", ")
        let rest = skipped.isEmpty ? "yok" : skipped.joined(separator: ",")
        print("\(label): \(key) degisti | yeniden \(recomputed) | atlanan \(rest)")
        return hits
    }
}

func countCauses(_ recomputes: [Recompute]) {
    for cause in ["yerel", "ebeveyn", "gozlem", "ortam"] {
        print("\(cause): \(recomputes.filter { $0.cause == cause }.count)")
    }
}
```

`Storage.model(object:)` bir değerin bir görünümde değil, bir nesnede saklandığını söyler. `cause` işlevi bu durumda sahiplik sorusuna girmez: bir nesne özelliğini okuyan her görünüm, ağaçta nerede durursa dursun, gözlem tetiğini alır. Model, çerçevenin bağımlılık izleme kuralını kurar; çerçevenin kendisi değildir.

## Özellik Düzeyinde Gözlem

Aşağıdaki nesne bir kütüphanenin kitap listesini ve ödünç sayısını tutar. `@Observable` makrosu sınıfın her saklanan özelliğini izlenebilir yapar. Ekran nesneyi çocuklarına verir; her çocuk yalnız ilgilendiği özelliği okur.

```swift
import SwiftUI
import Observation

@Observable
final class LibraryStore {
    var books: [String] = []
    var borrowedCount = 0
}

struct LibraryScreen: View {
    let store: LibraryStore
    @State private var searchText = ""

    var body: some View {
        VStack {
            BookList(store: store, query: searchText)
            BorrowBadge(store: store)
        }
    }
}

struct BookList: View {
    let store: LibraryStore
    let query: String

    var body: some View {
        List(store.books.filter { $0.contains(query) }, id: \.self) { title in
            Text(title)
        }
    }
}

struct BorrowBadge: View {
    let store: LibraryStore

    var body: some View {
        Text("Odunc: \(store.borrowedCount)")
    }
}
```

`BookList` yalnız `books` özelliğini, `BorrowBadge` yalnız `borrowedCount` özelliğini okur. `searchText` ise ekranın kendi durumudur ve nesnede değildir. Ağaç bu okumaları birebir taşır. Saklama tablosunda iki nesne özelliği `LibraryStore` nesnesine, arama metni ise `LibraryScreen` görünümüne bağlanır. Üç değişiklik verilir: bir kitap eklenir, bir ödünç sayılır, bir arama yazılır.

```swift
// main.swift — nesne ozelligi gozlem, ekranin kendi durumu yerel tetik uretir
let storage: [String: Storage] = [
    "books": .model(object: "LibraryStore"),
    "borrowedCount": .model(object: "LibraryStore"),
    "searchText": .state(owner: "LibraryScreen"),
]

let screen = ViewTree(nodes: [
    ViewNode("LibraryScreen", parent: nil, reads: ["searchText"]),
    ViewNode("BookList", parent: "LibraryScreen", reads: ["books", "searchText"]),
    ViewNode("BorrowBadge", parent: "LibraryScreen", reads: ["borrowedCount"]),
], storage: storage)

let recomputes = [
    screen.change("kitap-eklendi", key: "books"),
    screen.change("odunc-verildi", key: "borrowedCount"),
    screen.change("arama-yazildi", key: "searchText"),
].flatMap { $0 }

countCauses(recomputes)
```

```
kitap-eklendi: books degisti | yeniden BookList gozlem | atlanan LibraryScreen,BorrowBadge
odunc-verildi: borrowedCount degisti | yeniden BorrowBadge gozlem | atlanan LibraryScreen,BookList
arama-yazildi: searchText degisti | yeniden LibraryScreen yerel, BookList ebeveyn | atlanan BorrowBadge
yerel: 1
ebeveyn: 1
gozlem: 2
ortam: 0
```

`kitap-eklendi` yalnız `BookList`'i, `odunc-verildi` yalnız `BorrowBadge`'i yeniden hesaplattı; ikisi de gözlem. İki görünüm aynı nesneyi tutuyor, ama her biri nesnenin farklı bir özelliğini okuyor ve birinin değişimi ötekini etkilemiyor. `arama-yazildi` satırı gözlem değil: değer nesnede değil ekranda yaşıyor, bu yüzden `LibraryScreen` yerel, `BookList` ebeveyn tetikle yeniden hesaplandı. Sayılar: yerel bir, ebeveyn bir, gözlem iki.

Burada tetiği belirleyen nesnenin kendisi değil, okunan özelliktir. Aynı `BookList` hem `books` için gözlem hem `searchText` için ebeveyn tetikle yeniden hesaplandı. Bu, ikinci dersin sonucunun nesneler için de geçerli olduğunu gösterir: tetik görünümün türüne değil, okunan değerin yaşadığı yere bağlıdır.

**DR7.** Bu ölçüm üç değişiklik ve üç anahtar için. Nesne özelliğinin görünümün gövdesinde yalnız dolaylı okunduğu durum, örneğin bir hesaplanmış özellik üzerinden okuma, ölçülmedi; modelde her görünüm okuduğu anahtarı doğrudan `reads` içinde taşır.

## Gerçek İzleme: Yalnız Okunan Özellik

Yukarıdaki ölçüm bir modeldir; ağaç kuralı bildiğimiz biçimde kurar. Kuralın kendisi, yani `@Observable` bir nesnede yalnız okunan özelliğin değişiminin bildirim üretmesi, derleyicinin ve Observation modülünün davranışıdır ve doğrudan ölçülebilir. `withObservationTracking`, ilk kapanışta okunan özellikleri izler ve bunlardan biri değişince ikinci kapanışı çağırır.

```swift
// main.swift — gercek gozlem izleme: yalniz okunan ozellik bildirim uretir
import Observation

@Observable
final class LibraryStore {
    var books: [String] = []
    var borrowedCount = 0
}

let library = LibraryStore()
withObservationTracking {
    _ = library.borrowedCount
} onChange: {
    print("izlenen ozellik degisti")
}
library.books.append("algoritmalar")
print("books degisti")
library.borrowedCount += 1
print("borrowedCount degisti 1")
library.borrowedCount += 1
print("borrowedCount degisti 2")
```

```
books degisti
izlenen ozellik degisti
borrowedCount degisti 1
borrowedCount degisti 2
```

İzleme yalnız `borrowedCount` özelliğini okudu. `books` değiştiğinde geri çağrı çalışmadı: çıktıda `books degisti` satırından önce bir bildirim yok. `borrowedCount` değiştiğinde geri çağrı çalıştı ve `izlenen ozellik degisti` satırı, atamanın hemen ardından, `borrowedCount degisti 1` satırından önce basıldı. Yani bildirim atamanın içinde ve eşzamanlı geldi. İkinci artışta ise bildirim gelmedi: izleme **bir kezliktir**. Bir değişiklik bildirildikten sonra izleme biter ve sonraki değişikliği görmek için yeniden kurulması gerekir. Görünümlerde bu yeniden kurma çerçevenin işidir; bu ders onu ölçmez.

Bu iki gözlem, ağaçtaki iki satırı destekler. Okunmayan özelliğin değişimi hiçbir şeyi yeniden hesaplatmaz, okunan özelliğin değişimi yalnız o okumayı kuran tarafı bilgilendirir. Ağaçta `BorrowBadge`'in `books` değişiminde atlanması bu davranışın modele yansımasıdır.

**DR8.** Bu ölçüm `Observation` modülünün kendisi içindir; çerçevenin bu izlemeyi görünüm gövdesine nasıl bağladığı ve gövdenin kaç kez çağrıldığı ölçülmedi. Geri çağrının atanan değer yazılmadan önce mi sonra mı çalıştığı da ölçülmedi; çıktı yalnız atamanın içinde çalıştığını gösterir.

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

Her gözlem mekanizması özellik düzeyinde değildir. Daha eski bir yol, `ObservableObject` ve `@Published`, nesne düzeyinde bildirim yapar: nesnenin herhangi bir yayımlanan özelliği değişince nesneyi izleyen her görünüm yeniden hesaplanır. Bu yol Combine çerçevesine bağlıdır; ölçüm ortamında derlenmediği için kaynağı yalnız gösterilebilir.

```swift
import SwiftUI
import Combine

final class LibraryStore: ObservableObject {
    @Published var books: [String] = []
    @Published var borrowedCount = 0
}

struct BookList: View {
    @ObservedObject var store: LibraryStore

    var body: some View {
        List(store.books, id: \.self) { title in
            Text(title)
        }
    }
}

struct BorrowBadge: View {
    @ObservedObject var store: LibraryStore

    var body: some View {
        Text("Odunc: \(store.borrowedCount)")
    }
}
```

Aşağıdaki ölçüm bu kuralın modelini kurar; çerçevenin kendisini ölçmez. Nesne düzeyindeki bildirimde saklama tablosunda tek bir anahtar vardır ve anahtar nesnenin adıdır. İzleyen her görünüm bu anahtarı okur, çünkü nesnedeki hangi özelliğin değiştiği görünüm tarafından ayırt edilmez.

```swift
// main.swift — nesne duzeyinde bildirimde anahtar nesnenin adidir
let storage: [String: Storage] = [
    "LibraryStore": .model(object: "LibraryStore"),
]

let screen = ViewTree(nodes: [
    ViewNode("LibraryScreen", parent: nil, reads: []),
    ViewNode("BookList", parent: "LibraryScreen", reads: ["LibraryStore"]),
    ViewNode("BorrowBadge", parent: "LibraryScreen", reads: ["LibraryStore"]),
], storage: storage)

let recomputes = [
    screen.change("kitap-eklendi", key: "LibraryStore"),
    screen.change("odunc-verildi", key: "LibraryStore"),
].flatMap { $0 }

countCauses(recomputes)
```

```
kitap-eklendi: LibraryStore degisti | yeniden BookList gozlem, BorrowBadge gozlem | atlanan LibraryScreen
odunc-verildi: LibraryStore degisti | yeniden BookList gozlem, BorrowBadge gozlem | atlanan LibraryScreen
yerel: 0
ebeveyn: 0
gozlem: 4
ortam: 0
```

İki değişiklik iki görünümü ikişer kez yeniden hesaplattı; gözlem sayısı dört. Özellik düzeyindeki ilk ölçümde aynı iki değişiklik iki yeniden hesaplama üretmişti. Fark, kuralın ayrıntısından gelir: nesne düzeyinde bildirimde anahtar nesnenin adı olduğu için, `kitap-eklendi` değişikliği `BorrowBadge`'i de yeniden hesaplattı. Bu satırlar çerçevenin ölçümü değildir, kuralın modelidir. Ders bu modeli, özellik düzeyindeki gözlemin neyi kazandırdığını görmek için kurar: değişen özelliği okumayan görünüm atlanır.

**DR9.** Bu ölçüm nesne düzeyinde bildirimin kuralını model olarak kurar. Gerçek `ObservableObject` nesnesinin yayımlama biçimi ve görünümün aynı değerle gelen yayında yeniden hesaplanıp hesaplanmadığı ölçülmedi.

## Özet

- Gözlemlenebilir nesnenin özelliğini okuyan görünüm, özellik değişince gözlem tetiğiyle yeniden hesaplanır.
- Gözlem özellik düzeyindedir: yalnız okunan özelliğin değişimi bildirim üretir, okunmayan özellik üretmez.
- Gerçek izleme bir kezliktir; ikinci değişikliği görmek için izleme yeniden kurulur.
- Ekranın kendi durumu nesne özelliğinden ayrıdır ve yerel ya da ebeveyn tetik üretir.
- Nesne düzeyinde bildirim, nesneyi izleyen her görünümü her değişiklikte yeniden hesaplatır; özellik düzeyinde gözlem bunu ayırır.
- Bu dersin tetiği: **gözlem**.

## Sonraki Adım

Bu derste nesneyi çocuklara parametre olarak verdik; ekran nesneyi tuttu, çocuk onu aldı. Nesneyi ağacın her görünümüne tek tek taşımak, aradaki görünümleri yalnızca iletmek için nesneyi tanımaya zorlar. Sıradaki derste değer ağaç boyunca bir kez konur ve altındaki her görünüm onu doğrudan okur. Tetik bu kez ortamdır.
