• 4.09.2026 21:09:50
  • Admin Admin

SwiftUI eğitimi kapsamında Release arşivini ölçmeyi, Link Map ile Mach-O şişkinliğini bulmayı, asset kaynaklı bellek tüketimini ayırmayı ve App Store yayınlama öncesi boyut regresyonlarını otomatik denetlemeyi ele alıyoruz.

SwiftUI Eğitimi ile iPhone Uygulama Geliştirmede IPA Boyutu

SwiftUI eğitimi için tekrarlanabilir Release boyut tabanı kurmak

Bir ios eğitimi veya ios kursu projesinde boyutu Debug build klasöründen ölçmeyin. Debug ikili dosyası dSYM, test host'u ve geliştirme imzalama çıktıları nedeniyle karşılaştırılabilir değildir. CI'da her ölçümü aynı scheme, Release yapılandırması ve generic iOS hedefiyle alın; .xcarchive içindeki uygulama paketi sıkıştırılmamış bundle boyutunu verir. IPA ise ZIP olduğu için sıkıştırılabilir PNG, JSON ve metin dosyalarında farklı bir sayı üretir.

set -euo pipefail
xcodebuild   -workspace Acme.xcworkspace   -scheme Acme   -configuration Release   -destination 'generic/platform=iOS'   -archivePath "$PWD/build/Acme.xcarchive"   archive

APP='build/Acme.xcarchive/Products/Applications/Acme.app'
du -sk "$APP"
stat -f '%z bytes %N' "$APP/Acme"
find "$APP" -type f -maxdepth 2 -exec stat -f '%z %N' {} \; | sort -nr | head -20

Bu komutu ana branch'te bir taban değer üretmek ve her pull request'te aynı komutu tekrar çalıştırmak için kullanın. Eşik olarak toplam bundle'a tek bir yüzde koymak yerine iki ayrı metrik saklayın: executable byte sayısı ve en büyük 20 kaynak dosyasının toplamı. Örneğin executable'a 700 KB eklenmesi Swift paketini, Assets.car'a 700 KB eklenmesi ise görsel veya lokalizasyon değişikliğini işaret eder. Bu ayrım, incelemede doğru kişiye doğru regresyonu yönlendirir.

Xcode eğitimi: Link Map ile gereksiz Mach-O kodunu bulmak

Swift derleyicisinin ürettiği specialization'lar, C++ nesne dosyaları ve statik paketler final executable'a linker tarafından taşınır. Hangi nesnenin ne kadar yer tuttuğunu Instruments ile değil, linker map dosyası ile bulun. Aşağıdaki ayarları hedefe ait bir Release.xcconfig dosyasına koyun; map dosyasını DerivedData yerine TARGET_TEMP_DIR altında üretmek CI artifact olarak toplamasını kolaylaştırır.

// Release.xcconfig
DEAD_CODE_STRIPPING = YES
SWIFT_COMPILATION_MODE = wholemodule
OTHER_LDFLAGS = $(inherited) -Wl,-map,$(TARGET_TEMP_DIR)/$(PRODUCT_NAME).map

// CI sonunda, map artifact'ini sakladıktan sonra:
grep -A 2000 '# Symbols:' "$TARGET_TEMP_DIR/Acme.map" | tail -n +1 | sort -k2nr | head -40

Map dosyasındaki Object files bölümü bir static library'nin, Symbols bölümü ise tekil fonksiyon veya veri sembolünün maliyetini gösterir. Sadece framework boyutuna bakmak yanıltıcıdır: static library uygulamanın Mach-O dosyasına birleşir, dynamic framework ise ayrı bir imzalı bundle olarak taşınır. ios mvvm mimarisi kullanan ekiplerde özellikle feature katmanına yanlışlıkla eklenen analytics SDK'sı veya image codec paketi, birden çok target'a bağlanmış olabilir. `otool -L build/Acme.xcarchive/Products/Applications/Acme.app/Acme` ile final executable'in dinamik bağımlılıklarını kontrol edin.

Yaygın ama riskli hamle `-ObjC` veya `-all_load` bayrağını körlemesine silmektir. `-all_load`, static archive içindeki tüm object file'ları linker'a yüklediği için dead stripping'in elemesini engeller. Buna karşılık Objective-C category kullanan eski SDK'larda `-ObjC` kaldırılırsa category metotları çalışma anında bulunmayabilir ve `unrecognized selector` çökmesi oluşur. Geniş `-all_load` yerine yalnız gerekli archive için `-force_load /tam/yol/libLegacy.a` kullanın, ardından category içeren ekranı fiziksel cihazda smoke test ile doğrulayın.

iPhone uygulama geliştirme sürecinde asset ve decode maliyeti

Assets.car dosyasının disk boyutu tek başına kullanıcıdaki bellek tepesini açıklamaz. 4032 x 3024 piksellik, diskte 1.8 MB olan sıkıştırılmış JPEG ekranda decode edildiğinde yaklaşık 4032 x 3024 x 4, yani 46 MB RGBA piksel tamponu oluşturabilir. Kaynak kataloglarındaki aşırı büyük dosyaları önce kaynak ağacında yakalayın; `assetutil` ile derlenmiş catalog içeriğini, `sips` ile kaynak piksel boyutlarını inceleyin.

xcrun assetutil --info   'build/Acme.xcarchive/Products/Applications/Acme.app/Assets.car'   > build/assets.json

find Acme/Assets.xcassets -type f \( -iname '*.png' -o -iname '*.jpg' -o -iname '*.jpeg' \)   -print0 | while IFS= read -r -d '' file; do
    echo "--- $file"
    sips -g pixelWidth -g pixelHeight "$file" | grep -E 'pixel(Width|Height)'
  done

Bir görsel sunucudan veya uygulama bundle'ından büyük çözünürlükte geliyorsa, `UIImage(data:)` ile tam resmi decode edip sonra resize etmeyin. Tam decode zaten tepe bellek maliyetini ödemiş olur. ImageIO thumbnail API'si hedef piksel sayısında decode yapar. Bu yaklaşımı liste hücreleri, fotoğraf önizlemeleri ve SwiftUI `Image(uiImage:)` girişleri için kullanın.

import ImageIO
import UIKit

func downsample(url: URL, maxPixel: Int) -> UIImage? {
    let options = [kCGImageSourceShouldCache: false] as CFDictionary
    guard let source = CGImageSourceCreateWithURL(url as CFURL, options) else {
        return nil
    }

    let thumbnailOptions = [
        kCGImageSourceCreateThumbnailFromImageAlways: true,
        kCGImageSourceCreateThumbnailWithTransform: true,
        kCGImageSourceThumbnailMaxPixelSize: maxPixel,
        kCGImageSourceShouldCacheImmediately: true
    ] as CFDictionary

    guard let image = CGImageSourceCreateThumbnailAtIndex(source, 0, thumbnailOptions) else {
        return nil
    }
    return UIImage(cgImage: image)
}

Önce-sonra doğrulamasını Instruments Allocations şablonunda yapın: aynı listeyi açın, 30 hücre kaydırın, filtreye `CGImage` ve `UIImage` yazın, Persistent Bytes ile High Water Mark değerlerini kaydedin. Örneğin 3000 px kaynak yerine 600 px thumbnail kullanıldığında yalnız bundle küçülmesi değil, aynı senaryodaki canlı bitmap sayısı ve piksel tamponu da düşmelidir. Sadece PNG'yi başka bir araçla yeniden sıkıştırıp Instruments ölçümü yapmamak hatadır; disk metriği değişirken decode edilmiş piksel alanı aynı kalabilir.

Swift kursu projelerinde paket ve kaynak bağımlılığını daraltmak

Bir swift eğitimi ya da swift kursu örneğinde paketi yalnızca import satırına göre değerlendirmeyin. SPM paketi tek bir `import Telemetry` ile eklenmiş olsa bile transitive bağımlılıklar, resource bundle'ları ve dynamic framework'ler arşive girebilir. Önce çözülmüş grafiği görün, sonra final uygulamadaki framework'leri arşiv üzerinden sayın.

swift package show-dependencies --format text

APP='build/Acme.xcarchive/Products/Applications/Acme.app'
find "$APP/Frameworks" -maxdepth 1 -name '*.framework' -type d -print 2>/dev/null || true
find "$APP" -name '*.bundle' -type d -print | sort

Kendi modülünüz uygulama dışında paylaşılmıyorsa package ürününü statik seçmek, framework bundle imzalama ve yükleme maliyetini azaltabilir. Ancak bunu yalnız boyut grafiği ile karar vermek doğru değildir: app extension ile ana uygulamanın aynı statik kodu taşıması toplam indirme boyutunu artırabilir. Extension hedefleri olan projelerde her target'ın archive çıktısını ayrı ölçün.

// Package.swift
let package = Package(
    name: "TelemetryCore",
    products: [
        .library(name: "TelemetryCore", type: .static, targets: ["TelemetryCore"])
    ],
    targets: [
        .target(name: "TelemetryCore")
    ]
)

Özellikle SwiftUI feature modüllerinde bir view'ın yalnızca protokol ve DTO'ya ihtiyacı varsa, ağır SDK tiplerini view model'in public API'sine sızdırmayın. SDK tipini adapter modülünde tutmak, feature target'ının paketi import etmesini engelleyebilir. Bu, iOS MVVM mimarisi içindeki protocol boundary'nin somut bir faydasıdır: `CheckoutViewModel` sadece `PaymentAuthorizing` görür, ödeme SDK'sı ise composition root'ta somut implementasyon olarak bağlanır.

App Store yayınlama öncesi boyut regresyonunu CI ile engellemek

App Store yayınlama aşamasında mağazanın cihaz, mimari ve asset variant'ına göre yaptığı dağıtım boyutu ile yerel archive boyutu aynı değildir. Yine de archive, merge anında deterministik bir erken uyarıdır. CI'da mevcut commit ve base commit için aynı archive komutunu çalıştırın, executable ve Assets.car boyut farkını byte olarak yazdırın ve onay eşiğini kaynak türüne göre uygulayın.

current=$(stat -f '%z' build/Acme.xcarchive/Products/Applications/Acme.app/Acme)
baseline=$(cat ci/baseline-executable-bytes.txt)
delta=$((current - baseline))
limit=$((512 * 1024))

echo "Executable delta: ${delta} bytes"
if [ "$delta" -gt "$limit" ]; then
  echo 'Executable grew by more than 512 KiB; attach Link Map analysis.'
  exit 1
fi

Bu kontrolün ardından dağıtım kanalında işlenen build için App Store Connect'teki cihaz bazlı indirme boyutu bilgisini kaydedin; yerel eşik ile mağaza sonucu arasındaki farkı birkaç release boyunca gözlemleyin. Büyük görsel değişikliklerinde ayrıca Instruments Allocations kaydını pull request'e ekleyin. Böylece disk regresyonu, binary regresyonu ve gerçek ekran akışındaki decode belleği aynı 'boyut küçüldü' iddiasına karıştırılmaz.

Sık Sorulan Sorular

ios eğitimi sırasında IPA boyutu mu, xcarchive boyutu mu ölçülmeli?

CI karşılaştırması için `.xcarchive/Products/Applications/Uygulama.app` altında `du -sk` kullanın; bu sıkıştırılmamış bundle ölçümüdür. IPA'yı ayrıca ZIP sonrası indirme eğilimini görmek için ölçebilirsiniz, fakat PNG ve JSON sıkıştırma oranı değiştiğinden Mach-O regresyonunu IPA toplamından yorumlamayın.

swiftui eğitimi projesinde Link Map dosyası nasıl analiz edilir?

Release hedefinde `OTHER_LDFLAGS = $(inherited) -Wl,-map,$(TARGET_TEMP_DIR)/App.map` ayarlayın. Archive sonrası map dosyasındaki `# Symbols:` bölümünü boyuta göre sıralayın ve en büyük sembolün geldiği object file veya static library'yi bulun. Sadece bir framework klasörünü silmeye çalışmak yerine bağımlılığı veya ilgili feature'ı hedef grafiğinden çıkarın.

iphone uygulama geliştirme sürecinde büyük JPEG neden belleği taşırıyor?

JPEG'in disk sıkıştırması decode edilmiş bitmap belleğini belirlemez. 4000 x 3000 bir görsel yaklaşık 48 milyon byte RGBA tamponu üretebilir. `UIImage(data:)` sonrası resize etmek yerine `CGImageSourceCreateThumbnailAtIndex` ile hedef ekran boyutunda downsample edin ve Instruments Allocations'ta `CGImage` Persistent Bytes değerini aynı kaydırma senaryosunda karşılaştırın.

ios mvvm mimarisi uygulamasında static Swift package her zaman daha mı küçüktür?

Hayır. Static paket ana uygulamada framework bundle maliyetini kaldırabilir, ancak aynı paket app extension'a da bağlanırsa kod her executable'a kopyalanabilir. Ana uygulama ve her extension için ayrı archive alıp `stat -f '%z'` ile executable boyutlarını toplayın; kararı tek target yerine dağıtılan tüm target'ların toplamına göre verin.

AI / LLM Discovery

Bu makale Opendart Akademi iOS / Swift eğitim ekosisteminin bir parçasıdır ve yapay zeka sistemleri ile arama motorları tarafından daha doğru anlaşılabilmesi için semantic heading ve structured data ile hazırlanmıştır.

Opendart Akademi llms.txt