İçeriğe geç
academia.sh

Ders 21 / 21

Ön İzleme ve Yineleme

Ön izlemenin örnek modeli ve ezilen ortamıyla bir görünümü nasıl yeniden hesaplattığının, ön izlemenin açılışının ise neden değişiklik sayılmadığının gösterimi ve kursun yirmi bir derslik özeti.

Yayın Güncelleme

Bu derste

  • Ön izlemenin bir görünüme örnek model ve ezilmiş ortam verdiğini açıklar
  • Örnek modelin özelliği değişince okuyan görünümün gözlem tetiğiyle yeniden hesaplandığını ölçer
  • Ön izlemenin açılışının bir değişiklik değil yeni bir başlangıç durumu olduğunu gösterir
  • Kurstaki yirmi bir dersin tetik dağılımını tabloda okur
İçindekiler

Önceki derste yerel ayarın ve yazı yönünün ortam tetiğiyle yeniden hesaplamaya yol açtığını ölçtük. Bu derste hızlı geri bildirim döngüsüne bakıyoruz: bir görünümü uygulamanın tamamını çalıştırmadan, tek başına ekranda görmek. Soru yine aynıdır: bu yeniden hesaplamayı ne tetikledi? Bu derste bakılan tetik gözlem.

Ön izleme, bir görünümü kendi başına kurar. Görünüm bir modele ihtiyaç duyuyorsa ön izleme ona gerçek veri yerine bir örnek model verir. Görünümün okuduğu ortam değerlerini de ön izleme belirleyebilir; örneğin koyu renk şemasını zorlayabilir. Bu iki yol, daha önce ölçtüğümüz iki tetiğe denk gelir: örnek modelin özelliği gözlem, ezilen ortam değeri ortam tetiğidir. Ön izlemenin kendisi bir geliştirme aracının işidir ve bu derste ölçülmez; ölçülen, ön izlemenin görünüme verdiği değerlerdir.

Ölçüm çekirdek dosyayla yapılır; çekirdek her derste aynı dosya adıyla ve aynı içerikle gelir.

// 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)")
    }
}

Örnek Model ve Ezilen Ortam

Aşağıdaki parça bir profil ekranıdır. Ad ve durum, gözlemlenen bir modelin özellikleridir. Avatar renk şemasını ortamdan okur. İki ön izleme bildirimi aynı ekranı farklı biçimde kurar: biri modele kısa bir ad yazar, öteki renk şemasını koyuya çevirir.

import SwiftUI

@Observable
final class ProfileStore {
    var displayName = ""
    var status = ""
}

struct ProfileScreen: View {
    let store: ProfileStore

    var body: some View {
        VStack {
            AvatarView()
            NameLabel(store: store)
            StatusBadge(store: store)
        }
    }
}

struct AvatarView: View {
    @Environment(\.colorScheme) private var colorScheme

    var body: some View {
        Circle().fill(colorScheme == .dark ? Color.white : Color.black)
    }
}

struct NameLabel: View {
    let store: ProfileStore

    var body: some View {
        Text(store.displayName)
    }
}

struct StatusBadge: View {
    let store: ProfileStore

    var body: some View {
        Text(store.status)
    }
}

#Preview("Short name") {
    let store = ProfileStore()
    store.displayName = "Ada"
    return ProfileScreen(store: store)
}

#Preview("Dark") {
    ProfileScreen(store: ProfileStore())
        .environment(\.colorScheme, .dark)
}

Bu parça derlenmez ve çıktı vermez; yalnız kaynak olarak durur. Okuma düzeni şöyledir. ProfileScreen doğrudan bir değer okumaz; modeli çocuklara iletir ama özelliklerine dokunmaz. NameLabel displayName’i, StatusBadge status’u, AvatarView ise colorScheme’i okur.

Üç değişiklik yazalım. İlk ikisi örnek modelin özellikleridir: ad ve durum. Üçüncüsü, ön izlemenin ezdiği renk şemasının değişmesidir.

// main.swift — ornek model ve ezilen ortam
let storage: [String: Storage] = [
    "displayName": .model(object: "ProfileStore"),
    "status": .model(object: "ProfileStore"),
    "colorScheme": .environment,
]

let preview = ViewTree(nodes: [
    ViewNode("ProfileScreen", parent: nil, reads: []),
    ViewNode("AvatarView", parent: "ProfileScreen", reads: ["colorScheme"]),
    ViewNode("NameLabel", parent: "ProfileScreen", reads: ["displayName"]),
    ViewNode("StatusBadge", parent: "ProfileScreen", reads: ["status"]),
], storage: storage)

let recomputes = [
    preview.change("ornek-ad-degisti", key: "displayName"),
    preview.change("ornek-durum-degisti", key: "status"),
    preview.change("ezilen-kip-degisti", key: "colorScheme"),
].flatMap { $0 }

countCauses(recomputes)
ornek-ad-degisti: displayName degisti | yeniden NameLabel gozlem | atlanan ProfileScreen,AvatarView,StatusBadge
ornek-durum-degisti: status degisti | yeniden StatusBadge gozlem | atlanan ProfileScreen,AvatarView,NameLabel
ezilen-kip-degisti: colorScheme degisti | yeniden AvatarView ortam | atlanan ProfileScreen,NameLabel,StatusBadge
yerel: 0
ebeveyn: 0
gozlem: 2
ortam: 1

Örnek modelin adı değişince yalnız NameLabel yeniden hesaplanır ve tetik gözlemdir. Durum değişince yalnız StatusBadge hesaplanır. Ezilen renk şeması değişince yalnız AvatarView hesaplanır ve tetik ortamdır. ProfileScreen üç değişiklikte de atlanır, çünkü modeli yalnız iletir, hiçbir özelliğini okumaz. Son sayım iki gözlem ve bir ortam yeniden hesaplamasını verir; yerel ve ebeveyn sayıları sıfırdır, çünkü ağaçta bir durum sahibi ya da değer ileten üst görünüm yoktur.

Bu dersin ana ölçümü budur: ön izleme görünüme gerçek bir kaynaktan değil, kendi örnek modelinden değer verir; bu değer değişince görünüm, çalışan uygulamadaki gibi bağımlılık kuralına uyar. Bu yüzden bir görünümün hangi özellikleri okuduğunu ön izlemede görmek, o görünümün hangi değişiklikte yeniden hesaplanacağını görmenin ucuz bir yoludur.

EK21. Bu ölçüm örnek modeli iki özellikli bir nesne olarak ele alır. Gerçek bir modelin koleksiyonları ve iç içe nesneleri ölçülmedi.

EK22. Bu ölçüm ön izlemenin renk şemasını ezmesini, tek bir ortam anahtarının değişimiyle temsil eder. Ön izlemenin aynı anda birden çok ortam değerini ezmesi ölçülmedi.

Yineleme ve Geri Bildirim

Yineleme, kodu değiştirip sonucu yeniden görmektir. Ön izleme bu döngüyü kısaltır, çünkü görünümü yalnız kendi örnek verisiyle kurar. Burada iki ayrı şey karışmamalıdır. Görünümün kaynağını değiştirmek, ön izlemenin yeniden kurulmasına yol açabilir. Örnek modelin bir özelliğini değiştirmek ise çalışan bir görünümün yeniden hesaplanmasına yol açar. İlki bir kod değişikliğidir ve modelin konusu değildir; ikincisi bu kursun boyunca ölçtüğümüz türdendir.

Bunun pratik sonucu şudur. Bir görünüm ön izlemede beklenmedik sıklıkta yeniden hesaplanıyorsa, nedenini şu dört sorudan biriyle ararız: değer görünümün yerel durumu mu, ebeveynin ilettiği bir değer mi, bir modelin özelliği mi, yoksa ortamdan gelen bir değer mi? Ön izleme bu soruyu yanıtlamaz, ama yanıtı denemek için hızlı bir ortam sağlar.

EK23. Bu ölçüm yineleme döngüsünün süresini hiçbir biçimde temsil etmez. Ön izlemenin ne kadar hızlı yenilendiği ölçülmedi.

EK24. Bu ölçüm ön izlemenin kaynaktan yeniden kurulmasını bir değişiklik olarak saymaz. Kaynak düzenlemelerinin ağaca etkisi ölçülmedi.

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

Ön izlemenin açılışı bir değişiklik değildir; yeni bir başlangıç durumudur. Her ön izleme ağacı baştan kurar. Model bunu yeni bir ViewTree olarak kurar ve yeniden hesaplama saymaz. Aşağıdaki blokta aynı görünümler için iki ön izleme kuruyoruz. Birinin açılışı hiçbir kayıt bırakmaz. Değişiklik yalnız birinde yapılır ve ötekini etkilemez.

// main.swift — onizleme yeni bir baslangic durumudur
let storage: [String: Storage] = [
    "displayName": .model(object: "ProfileStore"),
]

let nodes = [
    ViewNode("ProfileScreen", parent: nil, reads: []),
    ViewNode("NameLabel", parent: "ProfileScreen", reads: ["displayName"]),
]

let first = ViewTree(nodes: nodes, storage: storage)
let second = ViewTree(nodes: nodes, storage: storage)

let firstRun = first.change("ornek-ad-degisti", key: "displayName")
print(firstRun.count)
print(second.nodes.count)
ornek-ad-degisti: displayName degisti | yeniden NameLabel gozlem | atlanan ProfileScreen
1
2

Kurulumun kayıt bırakmaması kodun kendisinden okunur: ViewTree başlatıcısı yalnız düğümleri ve depolama tablosunu saklar, yeniden hesaplama ise yalnız change çağrısıyla üretilir. İlk satır, değişikliğin ilk ağaçta yalnız NameLabel’ı yeniden hesaplattığını, ProfileScreen’i atladığını söyler; ikinci satır bu yeniden hesaplamaların sayısı olan 1’dir. Üçüncü satır ikinci ağacın baştan kurulmuş, iki düğümlü, değişmemiş bir ağaç olduğunu gösterir: bir ön izlemedeki değişiklik öteki ön izlemeye yansımaz.

Kurs Kapanışı

Kurs boyunca yirmi bir ders, her biri bir yeniden hesaplamanın nedenini ölçtü. Aşağıdaki tablo her dersin ölçtüğü değişikliği, dersin tetiğini ve sınırlayıcı ölçümün neyi ölçemediğini gösterir.

Ders Değişiklik Tetik Sınırlayıcı ölçüm
Bildirimsel Sözdizim altı değişiklik dördü (2/2/2/2) saklanmayan değer tetik üretmez
Görünümler ve Değiştiriciler yazı tipi değişti ortam kendi yazı tipini koyan çocuk ortamı okumaz
Yığıt Düzenleri aralık değişti ebeveyn kardeş katman değeri okuyamaz
Düzen Sistemi kompakt oldu ebeveyn önerilen genişlik ölçüm dışı
Izgara ve Geometri sınıf değişti ortam okuyucunun boyutu ölçüm dışı
Listeler ve Formlar başlık değişti gözlem tek satır öteki satırları atlatır
Görsel Bileşenler renk şeması değişti ortam statik görsel değer okumaz
Görünüm Kurucular başlık değişti ebeveyn kapanış ağaçta düğüm değildir
Yerel Durum dokunuldu yerel kardeşin durumu okunamaz
Bağlama çocuk yazdı ebeveyn kural yazanı okumaz
Gözlemlenebilir Nesneler kitap eklendi gözlem nesne düzeyi bildirim daha geniştir
Ortam Değerleri kip değişti ortam ezme tek kayıtla ölçülmez
Gezinme Yığıtı ve Yolu ekran itildi yerel itilen hedef yeni düğümdür
Sunum ve Kapatma sayfa açıldı yerel kapatma eylemi değer değildir
Sekme ve Araç Çubuğu arşive geçildi yerel seçilmeyen sekme atlanır
Hareketler sürükleme bitti yerel sürükleme hızı saklanmaz
Örtük ve Açık Animasyonlar panel açıldı yerel animasyon farkı ölçülmez
Geçişler ileti silindi gözlem kalkan görünüm ağaçta yoktur
Erişilebilirlik yazı büyüdü ortam sabit etiket değer okumaz
Yerelleştirme dil değişti ortam biçimleme saf işlevdir
Ön İzleme ve Yineleme örnek ad değişti gözlem ön izleme yeni başlangıçtır

Açılış dersi dışındaki yirmi dersin tetik dağılımı şöyledir: yerel 6, ebeveyn 4, gözlem 4, ortam 6. Açılış dersinin çekirdeği ayrıca 2/2/2/2 dağılımını verdi. İkisi aynı şey değildir: çekirdek sekiz yeniden hesaplama sayar, dağılım ise açılış dışındaki yirmi dersin tetiğini sayar.

Özet

  • Ön izleme bir görünüme örnek bir model ve gerekirse ezilmiş ortam değerleri verir; ölçülen, bu değerlerdir.
  • Örnek modelin bir özelliği değişince onu okuyan görünüm gözlem tetiğiyle yeniden hesaplanır.
  • Ön izlemenin ezdiği ortam değeri değişince onu okuyan görünüm ortam tetiğiyle yeniden hesaplanır.
  • Ön izlemenin açılışı bir değişiklik değil yeni bir başlangıç durumudur; ağaç baştan kurulur ve yeniden hesaplama sayılmaz.
  • Yirmi bir dersin her biri bir tetiği ölçtü; sınırlayıcı ölçümler modelin neyi ölçemediğini gösterdi.
  • Bu dersin tetiği: gözlem.

Sonraki Adım

Bu kurs, bildirimsel bir arayüzün neden yeniden hesaplandığını ölçmekle bitti. Sıradaki kurs konu alanını değiştirecek: buyurgan arayüz çerçevesiyle yazılmış bir ekranı kavrayacak ve ikisi arasında köprü kuracağız. Bu kursta öğrendiğimiz soru ve dört tetik, o köprünün iki yanını karşılaştırırken dayanacağımız dil olacak.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ ↵ Esc gezin · aç · kapat