İçeriğe geç
academia.sh

Ders 06 / 12

Sürüm ve Uyumluluk

Uygulamanın desteklediği sürümlerin bir arayüzün bulunduğu sürümlerle kesiştiğinin, ayrık kaldığında reddedildiğinin ve asgari sürüm kararının ölçümü.

Yayın Güncelleme

Bu derste

  • Asgari desteklenen sürümün ne anlama geldiğini açıklar
  • Desteklenen sürümlerin bir arayüzün bulunduğu sürümlerle kesiştiğini ölçer
  • Ayrık sürüm aralıklarının ret çıkardığını ve sürüm denetiminin bu durumda işe yaramadığını gösterir
  • Asgari sürüm kararının kuralın dışında kaldığını ölçerek ayırır
İçindekiler

Önceki derste kullanıcı, aygıt ve uygulama değerleri buluştu. Bu derste buluşan taraflar sürüm aralıklarıdır: uygulamanın desteklediği sürümler ile bir platform arayüzünün bulunduğu sürümler. Soru yine aynı: bu iki tanım buluşunca nasıl çözülüyor? Bu dersin çözümü kesişim, ve kesişim boş kaldığında ret.

Asgari desteklenen sürüm (deployment target), uygulamanın çalışacağı en eski platform sürümüdür. Uygulama bu sürümden eski bir sistemde kurulamaz. Üst sınır ise bildirilmez; uygulama kendi derlendiği sürümden yenisinde de çalışmaya devam eder. Bu yüzden modeldeki ilk küme, uygulamanın sınandığı sürümlerdir: asgari sürümden uygulamanın sınandığı en yeni sürüme kadar. Bir platform arayüzü ise belirli bir sürümde eklenmiş olur ve o sürümden önce yoktur; kaldırılmışsa kaldırıldığı sürümden itibaren de yoktur. Uygulamanın desteklediği sürümlerden bazılarında arayüz bulunur, bazılarında bulunmaz. Bu dersteki soru işte bu bölünmenin nasıl çözüldüğüdür.

// Meeting.swift — iki tanim bulusunca nasil cozuldugunu siniflandirir
struct Meeting {
    let label: String
    let first: Set<String>
    let second: Set<String>
    let result: Set<String>?

    init(_ label: String, first: Set<String>, second: Set<String>, result: Set<String>?) {
        self.label = label
        self.first = first
        self.second = second
        self.result = result
    }

    var resolution: String {
        if first.isEmpty || second.isEmpty
            || first.isSubset(of: second) || second.isSubset(of: first) {
            return "olcum-disi"
        }
        guard let result, !result.isEmpty else { return "ret" }
        if result == first.union(second) { return "birlesim" }
        if result == first.intersection(second) { return "kesisim" }
        if result == first || result == second { return "oncelik" }
        return "kural-disi"
    }

    static func list(_ values: Set<String>) -> String {
        values.isEmpty ? "bos" : values.sorted().joined(separator: ",")
    }

    func report() {
        let outcome = result.map(Meeting.list) ?? "yok"
        print("\(label): ilk \(Meeting.list(first)) | ikinci \(Meeting.list(second)) | sonuc \(outcome) -> \(resolution)")
    }
}

func summarize(_ meetings: [Meeting]) {
    for resolution in ["oncelik", "kesisim", "birlesim", "ret"] {
        let count = meetings.filter { $0.resolution == resolution }.count
        print("\(resolution): \(count)")
    }
}

Sürüm Denetimi

Sürüm denetimi (availability check), bir arayüzün çalışma anında var olup olmadığını sınayan yapıdır. Uygulama eski sürümleri de destekliyorsa, yeni bir arayüzü çağırmadan önce sürümü sınamak gerekir; sınanmadan çağrılan arayüz, eski sürümde bulunmadığı için derleme aşamasında reddedilir. Aşağıdaki parça platform arayüzlerine dayandığı için kaynak olarak durur ve çıktısı iddia edilmez.

import SwiftUI

struct DetailView: View {
    var body: some View {
        if #available(iOS 16, *) {
            Text("Yeni arayuz")
        } else {
            Text("Eski arayuz")
        }
    }
}

#available iki dalı ayırır: yeni arayüz yalnız yeni sürümde çalışır, eski sürümde yedek dal çalışır. Model, platformun bu kuralını kurar. Aşağıdaki sayılar model değerleridir: gerçek sürüm numaralarını ya da gerçek arayüzlerin gerçek sürümlerini göstermez. PY16.

Kesişen Sürümler

Aşağıdaki yardımcı iki sürüm aralığını alıp ortak sürümleri verir. Aralıklar tam sayı ile ifade edilir, Meeting kümeyle çalıştığı için sürümler dize olarak taşınır. Dosyanın çıktısı yoktur.

// Versions.swift — iki surum araliginin ortak surumleri
func commonVersions(app: ClosedRange<Int>, api: ClosedRange<Int>) -> Set<String> {
    Set(app.filter { api.contains($0) }.map(String.init))
}

func versionNames(_ range: ClosedRange<Int>) -> Set<String> {
    Set(range.map(String.init))
}

Uygulama 14’ten 17’ye kadar destekler; arayüz 16’dan 19’a kadar bulunur.

// main.swift — desteklenen ve bulunan surumler kesisir
let supported = 14...17
let available = 16...19

let overlap = Meeting("surum-kesisimi",
                      first: versionNames(supported),
                      second: versionNames(available),
                      result: commonVersions(app: supported, api: available))
overlap.report()
surum-kesisimi: ilk 14,15,16,17 | ikinci 16,17,18,19 | sonuc 16,17 -> kesisim

14 ve 15 uygulamada var ama arayüzde yok; 18 ve 19 arayüzde var ama uygulama desteklemiyor. Çalışan sürümler 16 ve 17’dir. PY17. Bu ölçüm yalnız bu iki aralık için; aralıkların sınırları model değerleridir.

Kesişimin iki ucu iki ayrı soruyu cevaplar. Alt uç, arayüzün ilk bulunduğu sürümdür: uygulamanın desteklediği daha eski sürümlerde arayüz çağrılamaz ve bu sürümler için yedek dal gerekir. Üst uç ise uygulamanın sınandığı en yeni sürümdür; uygulama daha yeni sürümlerde de çalışır ve arayüz orada da bulunur, ama sınanmamış sürümler modelin ilk kümesine girmediği için kesişimde görünmez. Üst uç bir destek sınırı değil, ölçümün sınırıdır. Yani yedek dalın kapsadığı sürümler, uygulamanın aralığından kesişimin çıkarılmasıyla bulunur: burada 14 ve 15.

Ayrık Sürümler

Aralıklar hiç kesişmezse ortak sürüm yoktur. Bunun gerçekten olduğu durum, kaldırılmış bir arayüzdür: arayüz 10’da eklenmiş, 13’te kaldırılmıştır ve yalnız 10’dan 12’ye bulunur. Uygulama 13’ten 15’e destekler; hiçbir sürümde ikisi birlikte yok.

// main.swift — ayrik surum araliklari reddedilir
let newApp = 13...15
let removedApi = 10...12

let apart = Meeting("ayrik-surum",
                    first: versionNames(newApp),
                    second: versionNames(removedApi),
                    result: commonVersions(app: newApp, api: removedApi))
apart.report()
ayrik-surum: ilk 13,14,15 | ikinci 10,11,12 | sonuc bos -> ret

Sonuç boş küme olunca çözüm ret çıkar. Gerçek bir projede bu, arayüzün bu uygulamada hiç kullanılamayacağı anlamına gelir: #available koşulu da işe yaramaz, çünkü desteklenen hiçbir sürümde arayüzü çağıran dal doğru dönmez. Tek yol, arayüzü yerine gelen yenisiyle değiştirmektir. Bu, retin çözümsüzlük değil, bir sınır bildirimi olduğunu gösterir.

Asgari Sürüm Kararının Ölçütleri

Asgari desteklenen sürüm bir buluşma sonucu değil, bir karardır ve üç ölçüte dayanır.

Birincisi kullanıcı tabanıdır. Asgari sürümü yükseltmek, o sürümden eski sistemde kalan kullanıcıları dışarıda bırakır. Karar için kullanıcıların hangi sürümlerde olduğunun bilinmesi gerekir; bu bilgi uygulamanın kendi kullanım verisinden ve platformun sunduğu dağıtım bilgisinden gelir. Eski sürümde kalan kullanıcı oranı küçükse yükseltmenin bedeli düşüktür, büyükse yüksektir. Bu dersin ölçümü bu oranı bilmez ve bilemez; ölçülen yalnız aralıkların kesişimidir.

İkincisi API erişimidir. Her sürüm yeni arayüzler getirir ve asgari sürümü yükseltmek, o sürümdeki arayüzlerin koşulsuz çağrılabilmesini sağlar. Dersin sonundaki sınırlayıcı ölçümde asgari-16 denemesinde uygulamanın aralığı arayüzün aralığının alt kümesi olur ve ölçüm dışı çıkar: bu durumda #available denetimine gerek kalmaz, çünkü uygulamanın desteklediği her sürümde arayüz vardır. API erişimi ölçütü, bu sadeleşmenin değeridir.

Üçüncüsü bakım yüküdür. Desteklenen her sürüm, sınanması gereken bir koşuldur. Eski ve yeni sürümler için ayrı davranış yazan kod, her dalı ayrı ayrı sınamayı gerektirir ve hata ihtimali dal sayısıyla artar. Asgari sürümü yükseltmek dalları azaltır, ama kullanıcı tabanını daraltır. Karar, bu iki bedelin arasındadır ve bedelin ağırlığı uygulamadan uygulamaya değişir.

Üç ölçüt birbirine karşıdır: kullanıcı tabanı sürümü düşük tutmayı, API erişimi ve bakım yükü yüksek tutmayı ister. Bu gerilimin çözümü bir küme işlemi değildir. Dersin sonundaki ölçüm de bunu gösterir: 14, 15 ve 16 üç ayrı karardır ve kural yalnız her birinin sonucunu sayar, birini seçmez.

Kullanım: #available ve @available

Sürüm sınırını kodda ifade etmenin iki yolu vardır. #available, bir ifadenin içinde çalışma anında sürümü sınayan koşuldur. Dersin başındaki parça bunu kullanır: koşul doğruysa yeni arayüz, yanlışsa yedek dal çalışır. Koşulun sonundaki yıldız, listelenmeyen diğer platformlar için varsayılanı bildirir ve zorunludur.

@available, bir bildirimin üzerine yazılan işarettir ve o bildirimin hangi sürümden itibaren kullanılabildiğini söyler. İşaretli bir işlev ya da tür, yalnız o sürümde ya da sonrasında kullanılabilir; işareti taşımayan bir kod parçası onu çağırmak istediğinde ya kendisi aynı işareti taşımalı ya da çağrıyı bir #available koşuluna sarmalıdır. Böylece sürüm gereksinimi çağrı zinciri boyunca yukarı doğru taşınır ve en sonunda ya bir koşulla karşılanır ya da asgari sürümle zaten sağlanır.

İkisi arasında iş bölümü açıktır. #available kararı verir: şu an çalışılan sistemde arayüz var mı. @available gereksinimi bildirir: bu bildirim hangi sürümü ister. Asgari sürüm, @available gereksinimlerinin tümünü karşılıyorsa hiçbir #available koşulu gerekmez. Kesişim kısmiyse, yani arayüz desteklenen sürümlerin yalnız bir bölümünde varsa, #available gerekir ve yedek dal kesişimin dışında kalan sürümleri karşılar. Kesişimin boş olduğu ret durumunda ise #available de yetmez: arayüzü çağıran dal desteklenen hiçbir sürümde çalışmaz. #available kısmi kesişimin kod tarafındaki karşılığıdır; ret ise arayüzün bırakılması gerektiğini söyleyen bildirimdir.

PY19. Bu bölümdeki anlatım kod ölçümü içermez; #available ve @available davranışı yalnız anlatılmıştır, çalıştırılmamıştır.

Derleme Sürümü ile Asgari Sürümün Farkı

İki sürüm kavramı sık karıştırılır. Derleme sürümü, uygulamanın derlendiği platform kitinin sürümüdür ve uygulamanın hangi arayüzleri bildiğini belirler. Asgari sürüm ise uygulamanın çalışacağı en eski sistemdir. Derleme sürümü yeni, asgari sürüm eski olabilir; uygulama yeni arayüzleri bilir ama eski sistemlerde de çalışır. İşte #available bu boşluğu kapatmak için vardır: derleyici arayüzü tanır, ama çalışma anında sistemin onu taşıyıp taşımadığı belli değildir.

Bu fark ölçüme şöyle yansır: ilk küme uygulamanın asgari sürümü ile sınandığı en yeni sürüm arasındaki aralıktır, ikinci küme arayüzün bulunduğu sürümlerdir. Derleme sürümü bu kümelerden hiçbirine girmez; yalnız arayüzün kodda yazılabilmesini sağlar. Kesişim ise çalışma anında hangi sürümlerde arayüzün gerçekten var olduğunu söyler.

Kullanımdan Kaldırılan Arayüzler

Platform bir arayüzü zamanla yenisiyle değiştirir. Eski arayüz önce kullanımdan kaldırılmış (deprecated) olarak işaretlenir: çalışmaya devam eder ama derleyici uyarı verir. Sonra, uzun bir süre sonra, tamamen kaldırılabilir. Kaldırılmış bir arayüzün bulunduğu sürümler, kaldırıldığı sürümden itibaren destekleyen bir uygulamanın aralığıyla hiç kesişmez; yukarıdaki ayrık sürümler ölçümü bu durumu kurar. Asgari sürüm kararı bu geçişi etkiler. Yeni arayüz yalnız yeni sürümlerde varsa ve uygulama eski sürümleri de destekliyorsa, ikisini birlikte tutmak gerekir: yeni sürümde yeni arayüz, eski sürümde eski arayüz, ikisinin arasında bir #available koşulu.

Bu iki yolun bakım yükü açıktır. Asgari sürüm yeni arayüzün bulunduğu sürüme yükseltildiği anda eski yol silinebilir ve koşul gereksiz kalır. Aşağıdaki sınırlayıcı ölçümdeki asgari-16 denemesi tam olarak budur: uygulamanın aralığı arayüzün aralığının içinde kalır ve buluşma ortadan kalkar. Silinen yedek dalın kodu da bakım yükünden çıkar.

Sürüm Sınırında Sınama

Her #available koşulu iki yol yaratır ve ikisi de sınanmalıdır. Yeni yol yeni sürümlü sistemde, yedek yol asgari sürümlü sistemde çalıştırılmalıdır. Sınanmamış bir yedek yol, tam da ihtiyaç duyulduğu anda, yani eski sürümlü bir kullanıcıda bozulur. Bu nedenle desteklenen en eski sürüm, geliştirme sırasında düzenli olarak sınanan bir hedef olmalıdır; yalnız en yeni sürümde sınanan bir uygulama, desteklediğini söylediği aralığı gerçekte desteklemiyor olabilir.

Sınama maliyeti, asgari sürüm kararının dördüncü ölçütü gibi çalışır: her desteklenen sürüm, sınanması gereken bir ortamdır. Kesişimin boş çıktığı ret durumu bu yüzden bir uyarı olarak da okunabilir: ortak sürüm yoksa, arayüzü kullanan yolu sınayacak bir ortam da yoktur; o yol desteklenen hiçbir sistemde çalışmaz ve koddan çıkarılmalıdır.

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

Asgari sürüm kararı bir buluşma değildir: hangi sürümün asgari olacağına kural değil karar verir. Kural yalnız kümeyi sayar. Aşağıdaki ölçümde aynı arayüz için asgari sürüm 14, 15 ve 16 olarak denenir; ilk ikisinde uygulamanın aralığı arayüzün aralığıyla kısmen örtüşür, üçüncüsünde tamamen onun içinde kalır.

// main.swift — asgari surum karari kuralin disinda kalir
let api = 16...19
let attempts = [
    Meeting("asgari-14", first: versionNames(14...17), second: versionNames(api),
            result: commonVersions(app: 14...17, api: api)),
    Meeting("asgari-15", first: versionNames(15...17), second: versionNames(api),
            result: commonVersions(app: 15...17, api: api)),
    Meeting("asgari-16", first: versionNames(16...17), second: versionNames(api),
            result: commonVersions(app: 16...17, api: api)),
]
for attempt in attempts {
    attempt.report()
}
asgari-14: ilk 14,15,16,17 | ikinci 16,17,18,19 | sonuc 16,17 -> kesisim
asgari-15: ilk 15,16,17 | ikinci 16,17,18,19 | sonuc 16,17 -> kesisim
asgari-16: ilk 16,17 | ikinci 16,17,18,19 | sonuc 16,17 -> olcum-disi

Üç denemede de ortak sürümler aynı: 16 ve 17. İlk ikisi kesişim, üçüncüsü ise uygulamanın aralığı arayüzün aralığının alt kümesi olduğu için ölçüm dışıdır. Yani asgari sürümü 14 ya da 15 yapmak sonucu değiştirmez, 16 yapmak ise buluşmayı ortadan kaldırır: artık bir #available denetimi gerekmez. Bu karar ölçülerek değil, destek maliyetiyle verilir; kural yalnız her seçimin sonucunu sayar, hangisinin seçileceğini söylemez.

PY18. Bu ölçüm üç asgari değer ve tek bir arayüz aralığı için. Her seçimin destek maliyeti ölçülmedi.

Özet

  • Asgari desteklenen sürüm, uygulamanın çalışacağı en eski sürümdür ve bir karardır.
  • Uygulamanın desteklediği ve arayüzün bulunduğu sürümler kesişir: 16 ve 17 kaldı.
  • Aralıklar ayrıksa (ör. kaldırılmış bir arayüz) ortak sürüm kalmaz ve çözüm ret çıkar; #available da işe yaramaz, arayüz yenisiyle değiştirilir.
  • Asgari sürümü 14 ya da 15 seçmek aynı kesişimi verir; 16 seçmek buluşmayı ölçüm dışına çıkarır.
  • Kural kümeyi sayar, alt sınırı seçmez.
  • Bu dersin çözümü: kesişim.

Sonraki Adım

Platformun yapısı burada bitiyor: katmanlar, çerçeveler, alan çerçeveleri, yaşam döngüsü, tasarım kılavuzları ve sürümler. Sıradaki konuda proje ile hedef buluşur: aynı ayar proje düzeyinde de hedef düzeyinde de tanımlanabilir ve geliştirme ortamı hangisinin geçerli olacağını bu buluşmayla çözer.

İ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