iOS Runtime: nedir, iPhone'da uygulama çalışma zamanı ortamı

Yazar: IT Sectr Yayınlanma: 2026-05-17 Okuma süresi: 12 dk

iOS Runtime, Apple iOS işletim sistemindeki uygulama çalışma zamanı ortamıdır ve Objective-C Runtime, Swift Runtime, Cocoa Touch framework'leri ve Automatic Reference Counting (ARC) aracılığıyla bellek yönetimi mekanizmalarını içerir. iOS Runtime, dinamik metot bağlama (message passing), sınıf yükleme, bellek yönetimi ve iOS framework'leri aracılığıyla donanımla etkileşimden sorumludur. Apple Geliştirici Dokümantasyonu'na göre, performans optimizasyonu, hata ayıklama ve kararlı iOS uygulamaları geliştirmek için runtime'ı anlamak gereklidir.

Anahtar Noktalar

  • iOS Runtime — Objective-C Runtime, Swift Runtime ve Cocoa Touch içeren uygulama çalışma zamanı ortamı
  • Objective-C Runtime — message passing (objc_msgSend) aracılığıyla dinamik metot bağlama
  • Swift Runtime — value types ve generics aracılığıyla optimizasyonlarla statik dağıtım
  • ARC (Automatic Reference Counting) — derleme zamanında otomatik bellek yönetimi
  • dyld — uygulama başlatılırken framework'leri ve kütüphaneleri yükleyen dinamik yükleyici

iOS Runtime Nedir?

iOS Runtime, iOS çalıştıran Apple cihazlarında uygulama yürütmeyi sağlayan bir sistem bileşenleri koleksiyonudur. Objective-C Runtime (libobjc.A.dylib kütüphanesi), Swift Runtime (libswiftCore.dylib), Core Foundation, Cocoa Touch framework'leri (UIKit, Foundation), dinamik yükleyici dyld ve bellek yönetimi, iş parçacıkları ve süreçler arası iletişim için çalışma zamanı ortamını içerir.

Mimari olarak, iOS Runtime üç seviyede çalışır. En alt seviyede — Mach-O ikili biçimi ve yürütülebilir dosyayı ve kütüphaneleri yükleyen dyld. Orta seviyede — metot dağıtımı ve nesne yönetiminden sorumlu Objective-C Runtime ve Swift Runtime. Üst seviyede — geliştiriciye API sağlayan Cocoa Touch framework'leri (UIKit, Foundation, Core Data, Metal).

iOS Runtime'ı anlamak, geliştiricinin karmaşık sorunları çözmesini sağlar: A/B testi ve analitik için metot swizzling (Method Swizzling), dinamik sınıf yükleme, ARC'yi anlayarak bellek optimizasyonu, retain cycle'ları ve bellek sızıntılarını hata ayıklama, dyld aracılığıyla uygulama başlatma süresini optimize etme. Runtime bilgisi olmadan, sistem düzeyinde profil oluşturma ve optimizasyon imkansızdır.

iOS Runtime Bileşenleri

BileşenKütüphaneAmaç
Objective-C Runtimelibobjc.A.dylibMessage passing, dinamik sınıflar, swizzling
Swift RuntimelibswiftCore.dylibValue types, generics, protocol witnesses
Core FoundationCoreFoundation.frameworkCFType, toll-free bridging
dylddyld (usr/lib/dyld)Mach-O yükleme, kütüphane bağlama
libSystemlibSystem.B.dylibPOSIX threads, libc, libdispatch (GCD)

Mach-O Biçimi

iOS için uygulamalar Mach-O biçiminde (Mach Object) derlenir. Bir Mach-O dosyası başlık, yükleme komutları ve segmentler içerir: __TEXT (kod, sabitler), __DATA (global değişkenler, Objective-C meta verileri), __LINKEDIT (semboller, yeniden konumlandırma tabloları). dyld, ilk talimatı yürütmeden önce Mach-O'yu ayrıştırır ve bağımlılıkları yükler.

Objective-C Runtime: Message Passing ve Dinamik Dağıtım

Objective-C Runtime, iOS Runtime'ın en güçlü parçasıdır. Erken bağlama (early binding) kullanan C++'ın aksine Objective-C, message passing aracılığıyla geç bağlama (late binding) kullanır. [receiver message] metot çağrısı, doğrudan bir işlev çağrısı olarak değil, nesnenin sınıfında metot uygulamasını dinamik olarak bulan objc_msgSend(receiver, @selector(message)) olarak derlenir.

Her Objective-C nesnesi, sınıfına bir isa işaretçisi depolar. Sınıf, bir metot listesi, metot önbelleği ve bir üst sınıf işaretçisi içerir. objc_msgSend kalıtım zincirini dolaşır: sınıf önbelleğini kontrol eder, ardından metot listesini, ardından üst sınıfa geçer. Metot bulunamazsa, yönlendirme tetiklenir: resolveInstanceMethod, forwardingTargetForSelector ve forwardInvocation.

Method Swizzling, çalışma zamanında IMP (uygulama işaretçisi) değişimi yoluyla metot uygulamalarını anında değiştirme tekniğidir. A/B testi, analitik (otomatik ekran izleme) ve izleme için kullanılır. Kritik bir ihtiyaç olmadan üretim için önerilmez, çünkü işletim sistemi güncellemeleriyle çakışabilir.

Örnek: Objective-C'de Method Swizzling

objective-c
// viewDidLoad izlemesi için Method Swizzling
#import 

@implementation UIViewController (Tracking)

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];

        SEL originalSelector = @selector(viewDidLoad);
        SEL swizzledSelector = @selector(swizzled_viewDidLoad);

        Method originalMethod = class_getInstanceMethod(
            class, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(
            class, swizzledSelector);

        BOOL didAddMethod = class_addMethod(
            class,
            originalSelector,
            method_getImplementation(swizzledMethod),
            method_getTypeEncoding(swizzledMethod)
        );

        if (didAddMethod) {
            class_replaceMethod(
                class,
                swizzledSelector,
                method_getImplementation(originalMethod),
                method_getTypeEncoding(originalMethod)
            );
        } else {
            method_exchangeImplementations(
                originalMethod, swizzledMethod);
        }
    });
}

- (void)swizzled_viewDidLoad {
    // Olay izleme
    NSLog(@"View Did Load: %@", self.class);
    // Orijinal uygulamayı çağırma
    [self swizzled_viewDidLoad];
}

@end

UIViewController (Tracking) kategorisi, uygulamadaki tüm UIViewControllers'ta viewDidLoad'ı swizzled_viewDidLoad ile değiştirir. dispatch_once, tek seferlik swizzling'i garanti eder. class_addMethod, çift swizzling'i ve üst sınıflarla çakışmaları önler. Denetleyici kaynak kodunu değiştirmeden analitikte otomatik ekran görünümü izleme için kullanılır.

isa İşaretçisi ve Etiketli İşaretçiler

Modern iOS'ta (arm64), Apple isa işaretçisini optimize etti: bu sadece bir sınıf adresi değil, bellek yönetimi bayrakları ve sınıf bilgisi içeren bir bit alanıdır (non-pointer isa). Etiketli işaretçiler (tagged pointers) başka bir optimizasyondur: küçük NSNumber, NSDate ve NSString değerleri, yığında nesne olarak değil, doğrudan işaretçide depolanır ve malloc ile retain/release yükünü ortadan kaldırır. Etiketli bir işaretçi, isa'nın en az anlamlı bitiyle tanınır.

Swift Runtime: Statik Dağıtım ve Optimizasyon

Swift Runtime, Objective-C Runtime'dan temel olarak farklıdır: Swift varsayılan olarak sınıf metotları için vtable ve value types ile genişletme metotları için direct call aracılığıyla statik dağıtım (static dispatch) kullanır. Dinamik dağıtım (dynamic dispatch) yalnızca @objc veya dynamic ile işaretlenmiş metotlar için kullanılır. Bu, Objective-C'ye kıyasla %40'a kadar performans iyileştirmesi sağlar.

Value types (struct, enum) Swift'te Objective-C'den önemli bir farktır. Yığında (stack) veya başka bir nesnenin içinde depolanırlar, retain/release kullanmazlar ve referans sayımı için ARC'ye katılmazlar. Struct'ın isa işaretçisi yoktur ve objc_msgSend aracılığıyla gönderilemez. Protocol witnesses, existential containers için dinamik dağıtım sağlayan protokoller için vtable'ın bir benzeridir.

Swift Runtime ayrıca, somutlaştırma (mangled symbols aracılığıyla reified generics) ile generics ve string, array, dictionary, set'i optimize etmek için COW (Copy-on-Write) içerir. Bir koleksiyon kopyalanırken, gerçek kopyalama yalnızca kopyalardan biri değiştirildiğinde gerçekleşir. Bu, koleksiyonların işlevler arasında iletilmesi sırasında yükü en aza indirir.

Swift vs Objective-C Dağıtım

swift
import Foundation

// Swift: statik dağıtım (sınıf için vtable)
class Animal {
    func makeSound() { print("...") }  // vtable
}

class Dog: Animal {
    override func makeSound() { print("Woof") }  // vtable override
}

// @objc dynamic: Objective-C Runtime dağıtımı
class Cat: Animal {
    @objc dynamic override func makeSound() {
        print("Meow")
    }  // objc_msgSend
}

// Struct — runtime dağıtımı yok
struct Cow {
    func makeSound() { print("Moo") }  // direct call
}

// Protocol with protocol witness
protocol SoundMaker {
    func makeSound()
}

struct Duck: SoundMaker {
    func makeSound() { print("Quack") }
}

// Existential container kullanımı
let soundMakers: [SoundMaker] = [Dog(), Cow(), Duck()]
for maker in soundMakers {
    maker.makeSound()  // protocol witness dispatch
}

// Performans testi
func testDispatch() {
    let dog = Dog()
    let cat = Cat()
    var cow = Cow()

    let start = CFAbsoluteTimeGetCurrent()
    for _ in 0..<1000000 {
        dog.makeSound()  // vtable: ~3ns
        cat.makeSound()  // objc_msgSend: ~15ns
        cow.makeSound()  // direct: ~1ns
    }
    let elapsed = CFAbsoluteTimeGetCurrent() - start
    print("Elapsed: (elapsed) sec")
}

Örnek, Swift'te üç tür dağıtımı gösterir: class (Dog) için vtable, @objc dynamic (Cat) için objc_msgSend ve struct (Cow) için direct call. Existential containers ([SoundMaker]) içindeki protocol witnesses yük ekler. Pratikte Swift, mümkün olan her yerde statik dağıtımı seçer ve C'ye yakın performans sağlar.

Swift Runtime ve Objective-C Köprüsü

Swift Runtime, Objective-C Runtime ile tam uyumluluk için tasarlanmıştır. NSObject'ten miras alan herhangi bir Swift sınıfı otomatik olarak Objective-C Runtime'da kaydedilir ve objc_msgSend aracılığıyla çağrılabilir. @objc özelliği, bir Swift metodunu Objective-C'den erişilebilir kılar. String köprüsü: Swift String, Objective-C API'sine (toll-free bridging) iletildiğinde otomatik olarak NSString'e köprülenir.

ARC: Otomatik Referans Sayma ve Bellek Yönetimi

ARC (Automatic Reference Counting), derleme zamanında çalışan iOS'ta bir bellek yönetim sistemidir. Derleyici (Clang), nesne yaşam sürelerini analiz eder ve otomatik olarak retain/release/autorelease çağrıları ekler. Geliştiricinin bunları manuel olarak çağırması gerekmez — iOS 5'ten önceki Manual Retain-Release'in (MRR) aksine. ARC, Objective-C ve Swift nesneleri düzeyinde çalışır, ancak value types (struct, enum) için çalışmaz.

Her Objective-C ve Swift sınıfı nesnesinin, non-pointer isa içindeki extra_rc alanında depolanan bir referans sayısı (retain count) vardır. Bir nesne oluşturulduğunda, retain count = 1'dir. retain'de sayaç artar, release'de azalır. Sayaç 0'a ulaştığında, nesne dealloc (Objective-C) veya deinit (Swift) aracılığıyla serbest bırakılır. ARC iş parçacığı güvenlidir: retain/release atomik işlemler (OSAtomicIncrement32/OSAtomicDecrement32) kullanır.

Retain cycle'lar ARC'nin ana sorunudur. A nesnesinin B'ye strong referansı varsa ve B'nin A'ya strong referansı varsa, her iki nesne de asla serbest bırakılmaz çünkü referans sayıları asla sıfıra ulaşmaz. Çözüm, weak referanslar (Objective-C'de __weak, Swift'te weak) veya unowned referanslardır. Weak referanslar retain count'u artırmaz ve nesne serbest bırakıldığında otomatik olarak sıfırlanır (nil).

Instruments ile Retain Cycle'larında Hata Ayıklama

swift
import Foundation

// Retain cycle örneği
class Parent {
    var child: Child?
    deinit { print("Parent deallocated") }
}

class Child {
    var parent: Parent?  // strong — retain cycle oluşturur!
    deinit { print("Child deallocated") }
}

var parent: Parent? = Parent()
var child: Child? = Child()
parent?.child = child
child?.parent = parent  // döngü: Parent -> Child -> Parent
parent = nil
child = nil
// deinit ÇAĞRILMADI — bellek sızıntısı!

// Düzeltme: weak
class WeakChild {
    weak var parent: Parent?  // weak — retain count'u artırmaz
    deinit { print("WeakChild deallocated") }
}

// Düzeltme: unowned (garantili yaşam süresi için)
class UnownedChild {
    unowned let parent: Parent
    init(parent: Parent) { self.parent = parent }
    deinit { print("UnownedChild deallocated") }
}

// Instruments ile doğrulama
func profileMemory() {
    // 1. Instruments > Leaks çalıştırın
    // 2. Nesneler oluşturan eylemi gerçekleştirin
    // 3. Sızıntılar için Leaks'i kontrol edin
    // 4. Allocations'ta dealloc olmayan nesneleri bulun
    for _ in 0..<1000 {
        let p = Parent()
        let c = WeakChild()
        p.child = c as? Child
        // c.parent = p — EKLEMİYORUZ, weak
    }
}

Parent ve Child arasındaki retain cycle örneği: her ikisi de birbirine strong referanslar tutar, ARC sayaçları sıfırlayamaz. Düzeltme, Child'daki weak parent'tır. weak, parent serbest bırakıldığında otomatik olarak sıfırlanır. unowned, parent'ın yaşam süresinin child'dan daha uzun olduğu garanti edilen durumlar içindir (örneğin, viewController ve view). Retain cycle'ları erken tespit etmek için Instruments > Leaks kullanın.

Autorelease Pool

Autorelease pool, açık sahiplik olmadan oluşturulan nesneler için ertelenmiş serbest bırakma mekanizmasıdır. Swift ve Objective-C'de @autoreleasepool { }, blok sonunda boşaltılan ve havuzdaki her nesneye release gönderen bir havuz oluşturur. Döngülerde (binlerce geçici nesne oluşturma) ve RunLoop olmayan arka plan iş parçacıklarında kritik öneme sahiptir. UIKit RunLoop, her yinelemede ana autorelease havuzunu otomatik olarak boşaltır.

dyld: Dinamik Yükleyici ve Uygulama Başlatma

dyld (dynamic link editor), bir iOS uygulaması başlatılırken Mach-O yürütülebilir dosyalarını ve ilgili dinamik kütüphaneleri (dylib) yüklemekten sorumlu sistem yükleyicisidir. dyld, /usr/lib/dyld konumunda bulunur ve libSystem'in bir parçasıdır. Yükleme süreci birkaç aşama içerir: Mach-O ayrıştırma, bağımlılık yükleme (Library Loader, LC_LOAD_DYLIB), adres yeniden konumlandırma (ASLR), Objective-C Runtime başlatma ve main() çağrısı.

Uygulama başlatma süresi, dyld'ye kritik olarak bağlıdır: ne kadar çok dinamik kütüphane ve Objective-C sınıfı varsa, pre-main time o kadar uzun olur. Apple, +load metotlarının sayısını en aza indirmeyi (bunlar main'den önce yürütülür) ve bunları +initialize (tembel başlatma) ile değiştirmeyi önerir. 2020'den beri Apple, iOS'ta önceden oluşturulmuş dyld önbelleği kullanır: sistem kütüphaneleri tek bir önbellekte önceden bağlanır ve yüklemeyi hızlandırır.

Pre-main Time Ölçümü

swift
import Foundation

// DYLD_PRINT_STATISTICS ile başlatma süresini ölçme
// Xcode'da: Edit Scheme > Run > Arguments > Environment Variables
// DYLD_PRINT_STATISTICS = 1
// DYLD_PRINT_STATISTICS_DETAILS = 1

// Pre-main time programlı ölçüm
@main
struct AppMain {
    static func main() {
        let launchStart = CFAbsoluteTimeGetCurrent()

        // UIApplicationMain burada gerçekleşir
        AppDelegate.main()

        let launchEnd = CFAbsoluteTimeGetCurrent()
        let preMainTime = launchEnd - launchStart
        print("Pre-main time: (preMainTime) sec")
    }
}

// Optimizasyon: +load'ı +initialize ile değiştirme
class OptimizedClass {
    // ❌ +load main'den önce yürütülür
    // override class func load() { }

    // ✅ +initialize ilk erişimde yürütülür
    static let shared = OptimizedClass()
    private init() {
        // Burada başlatma
    }
}

// dylib sayısını optimize etme
// Statik kütüphanelerin birleştirilmesi LC_LOAD_DYLIB sayısını azaltır
// Yalnızca kullanılan Objective-C sınıflarını bağlamak için -ObjC bayrağını kullanın
// Xcode: Build Settings > Mach-O Type > Static Library

pre-main time ölçmek için Xcode şemasında DYLD_PRINT_STATISTICS kullanın. Çıktı, toplam süreyi, dylib yükleme süresini, rebase/bind süresini, Objective-C kurulum süresini ve başlatıcı süresini gösterir. Hedef değerler: soğuk başlatma için toplam < 400ms, sıcak başlatma için < 200ms. Optimizasyonlar: kütüphaneleri birleştirme, +load'ı +initialize ile değiştirme, Objective-C sınıf sayısını azaltma (Swift kullanın), minimum sayıda dinamik framework.

dsc (dyld Shared Cache)

dyld shared cache, iOS'ta önceden bağlanmış sistem kütüphanelerinin bir önbelleğidir. Tüm sistem dylib'leri (UIKit, Foundation, CoreGraphics) tek bir dosyada birleştirilir: /System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm64. Bu, her sistem kütüphanesini ayrı ayrı yükleme ihtiyacını ortadan kaldırır — dyld önbelleğe erişir, bu da başlatmayı önemli ölçüde hızlandırır. 10+ dinamik framework'e sahip uygulamalar en büyük gecikmeyi yaşar, çünkü özel dylib'ler dsc'ye dahil değildir.

Sıkça Sorulan Sorular

iOS Runtime nedir ve hangi bileşenlerden oluşur?

iOS Runtime, iOS'ta uygulama çalışma zamanı ortamıdır ve Objective-C Runtime (libobjc.dylib), Swift Runtime (libswiftCore.dylib), Cocoa Touch framework'leri, dyld (dinamik yükleyici) ve ARC (bellek yönetimi) içerir. Objective-C için message passing, Swift için statik dağıtım, Mach-O dosya yükleme ve otomatik bellek yönetimi sağlar.

Objective-C Runtime ile Swift Runtime arasındaki fark nedir?

Objective-C Runtime, geç bağlama ile objc_msgSend (message passing) aracılığıyla dinamik bağlama kullanır. Swift Runtime, performans için statik dağıtım (sınıflar için vtable, struct için direct call) kullanır. @objc dynamic, Swift sınıfları için Objective-C Runtime'ı etkinleştirir. Swift struct'ın isa işaretçisi yoktur ve retain/release kullanmaz.

iOS'ta ARC nasıl çalışır?

ARC (Automatic Reference Counting), derleme zamanı bellek yönetimidir. Clang derleyicisi otomatik olarak retain/release çağrıları ekler. Her nesnenin bir referans sayısı vardır; sıfıra ulaştığında dealloc çağrılır. Retain cycle'lar (karşılıklı strong referanslar) weak/unowned referanslarla önlenir. Sızıntıları tespit etmek için Instruments > Leaks kullanın.

dyld nedir ve uygulama başlatmayı nasıl etkiler?

dyld, Mach-O dosyaları için dinamik yükleyicidir. Yürütülebilir dosyayı ve tüm bağımlı dylib'leri yükler, yeniden konumlandırma (ASLR) yapar, Objective-C Runtime'ı başlatır ve main()'i çağırır. Pre-main time, dylib ve +load metotlarının sayısına bağlıdır. Ölçüm için DYLD_PRINT_STATISTICS kullanın. Optimizasyon: kütüphaneleri birleştirin, +load'ı +initialize ile değiştirin.

Method Swizzling nedir ve ne zaman kullanılmalıdır?

Method Swizzling, Objective-C Runtime'ın class_getInstanceMethod ve method_exchangeImplementations aracılığıyla bir metodun IMP'sini (uygulama işaretçisi) anında değiştirme tekniğidir. A/B testi, analitik (otomatik ekran izleme) ve izleme için kullanılır. Kritik ihtiyaç olmadan üretimde önerilmez. Swift'te @objc dynamic + Method Swizzling ile değiştirilir.

Özet

  • iOS Runtime — Objective-C Runtime, Swift Runtime, dyld ve ARC içeren iOS uygulama çalışma zamanı ortamı
  • Objective-C Runtime — message passing (objc_msgSend), isa işaretçisi, swizzling, dinamik sınıflar
  • Swift Runtime — statik dağıtım (vtable, direct call), value types, protocol witnesses
  • ARC (Automatic Reference Counting) — derleme zamanında retain/release ile otomatik bellek yönetimi
  • dyld — uygulama başlatma hızını (pre-main time) belirleyen dinamik Mach-O yükleyici
  • Retain cycle'lar — weak/unowned referanslarla önlenir; Instruments Leaks ile hata ayıklama
  • Optimizasyon — +load'ı en aza indirin, dylib'leri birleştirin, value types için Swift struct kullanın

Anahtar teslim bir mobil uygulama geliştireceğiz

IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.

Projeyi tartış

Ayrıca okuyun