iOS Runtime: шта је то, окружење извршавања апликација на iPhone-у

Аутор: IT Sectr Објављено: 2026-05-17 Време читања: 12 мин

iOS Runtime — окружење извршавања апликација на оперативном систему Apple iOS, које укључује Objective-C Runtime, Swift Runtime, Cocoa Touch фрејмворке и механизме управљања меморијом кроз Automatic Reference Counting (ARC). iOS Runtime је одговоран за динамичко повезивање метода (message passing), учитавање класа, управљање меморијом и интеракцију са хардвером кроз iOS фрејмворке. Према Apple Developer Documentation, разумевање runtime-а је неопходно за оптимизацију перформанси, отклањање грешака и развој стабилних iOS апликација.

Главне ствари

  • iOS Runtime — окружење извршавања апликација, које укључује Objective-C Runtime, Swift Runtime и Cocoa Touch
  • Objective-C Runtime — динамичко повезивање метода кроз message passing (objc_msgSend)
  • Swift Runtime — статичка диспечација са оптимизацијама кроз value types и generics
  • ARC (Automatic Reference Counting) — аутоматско управљање меморијом у фази компилације
  • dyld — динамички учитавач који учитава фрејмворке и библиотеке при покретању апликације

Шта је iOS Runtime?

iOS Runtime — скуп системских компоненти које обезбеђују извршавање апликација на Apple уређајима под iOS-ом. Укључује Objective-C Runtime (библиотека libobjc.A.dylib), Swift Runtime (libswiftCore.dylib), Core Foundation, Cocoa Touch фрејмворке (UIKit, Foundation), динамички учитавач dyld и runtime окружење за управљање меморијом, нитима и међупроцесном комуникацијом.

Архитектонски, iOS Runtime ради на три нивоа. На доњем нивоу — Mach-O бинарни формат и dyld, који учитава извршну датотеку и библиотеке. Средњи ниво — Objective-C Runtime и Swift Runtime, одговорни за диспечацију метода и управљање објектима. Горњи ниво — Cocoa Touch фрејмворки (UIKit, Foundation, Core Data, Metal), који пружају API за програмера.

Разумевање iOS Runtime омогућава програмеру да решава сложене задатке: swizzling метода (Method Swizzling) за A/B тестирање и аналитику, динамичко учитавање класа, оптимизацију меморије кроз разумевање ARC-а, отклањање retain cycle-ова и цурења меморије, оптимизацију времена покретања апликације кроз dyld. Без познавања runtime-а, профилисање и оптимизација на нивоу система су немогући.

Компоненте iOS Runtime-а

КомпонентаБиблиотекаНамена
Objective-C Runtimelibobjc.A.dylibMessage passing, динамичке класе, swizzling
Swift RuntimelibswiftCore.dylibValue types, generics, protocol witnesses
Core FoundationCoreFoundation.frameworkCFType, toll-free bridging
dylddyld (usr/lib/dyld)Учитавање Mach-O, повезивање библиотека
libSystemlibSystem.B.dylibPOSIX threads, libc, libdispatch (GCD)

Mach-O формат

Апликације за iOS се компилирају у Mach-O формат (Mach Object). Mach-O датотека садржи заглавље (header), команде учитавања (load commands) и сегменте (segments): __TEXT (код, константе), __DATA (глобалне променљиве, Objective-C метаподаци), __LINKEDIT (симболи, табеле релокације). dyld анализира Mach-O и учитава зависности пре извршења прве инструкције.

Objective-C Runtime: message passing и динамичка диспечација

Objective-C Runtime — најмоћнији део iOS Runtime-а. За разлику од C++ са раним повезивањем (early binding), Objective-C користи касно повезивање (late binding) кроз message passing. Позив методе [receiver message] се компилира не као директни позив функције, већ као objc_msgSend(receiver, @selector(message)), који динамички проналази имплементацију методе у класи објекта.

Сваки Objective-C објекат чува показивач isa на своју класу. Класа садржи листу метода (method list), кеш метода (method cache) и показивач на суперкласу. objc_msgSend пролази кроз ланац наслеђивања: проверава кеш класе, затим method list, потом прелази на суперкласу. Ако метода није пронађена, покреће се forward: resolveInstanceMethod, forwardingTargetForSelector и forwardInvocation.

Method Swizzling — техника замене имплементације методе у лету кроз размену IMP (implementation pointer) у runtime-у. Користи се за A/B тестирање, аналитику (аутоматско праћење екрана) и мониторинг. Не препоручује се у продукцији без крајње нужде, јер може доћи у конфликт са ажурирањима оперативног система.

Пример: Method Swizzling у Objective-C

objective-c
// Method Swizzling за праћење viewDidLoad
#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 {
    // Праћење догађаја
    NSLog(@"View Did Load: %@", self.class);
    // Позив оригиналне имплементације
    [self swizzled_viewDidLoad];
}

@end

Категорија UIViewController (Tracking) замењује viewDidLoad са swizzled_viewDidLoad у свим UIViewController-има у апликацији. dispatch_once гарантује једнократно swizzling. class_addMethod спречава двоструко swizzling и конфликте са суперкласама. Користи се за аутоматско праћење приказа екрана у аналитици без измене изворног кода контролера.

isa показивач и tagged pointers

У модерном iOS (arm64), Apple је оптимизовао isa показивач: то није само адреса класе, већ битно поље (non-pointer isa) које садржи флагове управљања меморијом и информације о класи. Tagged pointers — још једна оптимизација: вредности NSNumber, NSDate и NSString мале величине се чувају не као објекти на heap-у, већ директно у показивачу, елиминишући overhead malloc-а и retain/release. tagged pointer се препознаје по најмлађем биту isa.

Swift Runtime: статичка диспечација и оптимизација

Swift Runtime се суштински разликује од Objective-C Runtime: Swift подразумевано користи статичку диспечацију (static dispatch) кроз vtable за методе класе и direct call за value types и extension methods. Динамичка диспечација (dynamic dispatch) се користи само за методе означене са @objc или dynamic. Ово даје повећање перформанси до 40% у поређењу са Objective-C.

Value types (struct, enum) у Swift-у — кључна разлика у односу на Objective-C. Чувају се на стеку (stack) или унутар другог објекта, не користе retain/release и не учествују у ARC-у за бројач референци. Struct нема isa показивач и не може се послати кроз objc_msgSend. Protocol witnesses — аналог vtable за протоколе, који омогућава динамичку диспечацију за existential container.

Swift Runtime такође укључује generics са реификацијом (reified generics кроз mangled symbols) и COW (Copy-on-Write) за оптимизацију string, array, dictionary, set. При копирању колекције, стварно копирање се дешава само при модификацији једне од копија. Ово минимизира overhead при преношењу колекција између функција.

Swift vs Objective-C диспечација

swift
import Foundation

// Swift: статичка диспечација (vtable за class)
class Animal {
    func makeSound() { print("...") }  // vtable
}

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

// @objc dynamic: Objective-C Runtime dispatch
class Cat: Animal {
    @objc dynamic override func makeSound() {
        print("Meow")
    }  // objc_msgSend
}

// Struct — no runtime dispatch
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
let soundMakers: [SoundMaker] = [Dog(), Cow(), Duck()]
for maker in soundMakers {
    maker.makeSound()  // protocol witness dispatch
}

// Тестирање перформанси
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")
}

Пример демонстрира три типа диспечације у Swift: vtable за class (Dog), objc_msgSend за @objc dynamic (Cat) и direct call за struct (Cow). Protocol witnesses у existential container ([SoundMaker]) додају overhead. У пракси, Swift бира статичку диспечацију свуда где је то могуће, обезбеђујући перформансе блиске C-у.

Swift Runtime и Objective-C мост

Swift Runtime је дизајниран за потпуну компатибилност са Objective-C Runtime. Свака Swift класа која наслеђује NSObject аутоматски се региструје у Objective-C Runtime и може се позвати кроз objc_msgSend. @objc атрибут чини Swift методу доступном из Objective-C-а. String мост: Swift String се аутоматски премошћава у NSString при преношењу у Objective-C API (toll-free bridging).

ARC: Automatic Reference Counting и управљање меморијом

ARC (Automatic Reference Counting) — систем управљања меморијом у iOS-у, који ради у фази компилације. Компилатор (Clang) анализира животни век објеката и аутоматски убацује позиве retain/release/autorelease. Програмер не мора да их позива ручно — за разлику од Manual Retain-Release (MRR) пре iOS 5. ARC ради на нивоу Objective-C објеката и Swift class, али не за value types (struct, enum).

Сваки Objective-C објекат и Swift класа има бројач референци (retain count), који се чува у extra_rc пољу унутар non-pointer isa. При креирању објекта, retain count = 1. При retain-у бројач расте, при release-у опада. Када бројач достигне 0, објекат се деалоцира кроз dealloc (Objective-C) или deinit (Swift). ARC је thread-safe: retain/release користе атомске операције (OSAtomicIncrement32/OSAtomicDecrement32).

Retain cycles — главни проблем ARC-а. Ако објекат A чува strong референцу ка B, а B — strong референцу ка A, оба објекта никада неће бити деалоцирана, јер њихови бројачи референци неће бити нула. Решење — слабе референце (__weak у Objective-C, weak у Swift) или unowned референце. Слабе референце не повећавају retain count и аутоматски се анулирају (nil) при деалокацији објекта.

Отклањање retain cycle-ова кроз Instruments

swift
import Foundation

// Пример retain cycle
class Parent {
    var child: Child?
    deinit { print("Parent deallocated") }
}

class Child {
    var parent: Parent?  // strong — ствара retain cycle!
    deinit { print("Child deallocated") }
}

var parent: Parent? = Parent()
var child: Child? = Child()
parent?.child = child
child?.parent = parent  // циклус: Parent -> Child -> Parent
parent = nil
child = nil
// deinit НЕ позива се — цурење меморије!

// Поправка: weak
class WeakChild {
    weak var parent: Parent?  // weak — не повећава retain count
    deinit { print("WeakChild deallocated") }
}

// Поправка: unowned (за гарантовани животни век)
class UnownedChild {
    unowned let parent: Parent
    init(parent: Parent) { self.parent = parent }
    deinit { print("UnownedChild deallocated") }
}

// Провера кроз Instruments
func profileMemory() {
    // 1. Покрените Instruments > Leaks
    // 2. Извршите акцију која креира објекте
    // 3. Проверите Leaks на цурења
    // 4. У Allocations пронађите објекте без dealloc
    for _ in 0..<1000 {
        let p = Parent()
        let c = WeakChild()
        p.child = c as? Child
        // c.parent = p — НЕ додајемо, weak
    }
}

Пример retain cycle између Parent и Child: оба држе strong референце један ка другом, ARC не може да нулира бројаче. Поправка — weak parent у Child. weak се аутоматски анулира при деалокацији parent-а. unowned — за случајеве када је животни век parent-а гарантовано дужи од child-а (нпр. viewController и view). Користите Instruments > Leaks за откривање retain cycle-ова у раним фазама.

Autorelease pool

Autorelease pool — механизам одложеног release-а за објекте креиране без експлицитног власништва. @autoreleasepool { } у Swift и Objective-C ствара пул који се празни на крају блока, шаљући release сваком објекту у пулу. Критично важан у петљама (креирање хиљада привремених објеката) и на позадинским нитима без RunLoop-а. UIKit RunLoop аутоматски празни главни autorelease pool на свакој итерацији.

dyld: динамички учитавач и покретање апликације

dyld (dynamic link editor) — системски учитавач одговоран за учитавање Mach-O извршних датотека и повезаних динамичких библиотека (dylib) при покретању iOS апликације. dyld се налази на путањи /usr/lib/dyld и део је libSystem-а. Процес учитавања укључује неколико фаза: парсирање Mach-O, учитавање зависности (Library Loader, LC_LOAD_DYLIB), релокацију адреса (ASLR), иницијализацију Objective-C Runtime и позив main().

Време покретања апликације (launch time) критично зависи од dyld-а: што више динамичких библиотека и Objective-C класа, дужи је pre-main time. Apple препоручује минимизирање броја +load метода (извршавају се пре main-а), замењујући их са +initialize (лења иницијализација). Од 2020. године Apple користи prebuilt dyld cache на iOS-у: системске библиотеке су унапред повезане у јединствени кеш, што убрзава учитавање.

Мерење pre-main time-а

swift
import Foundation

// Мерење времена покретања кроз DYLD_PRINT_STATISTICS
// У Xcode-у: Edit Scheme > Run > Arguments > Environment Variables
// DYLD_PRINT_STATISTICS = 1
// DYLD_PRINT_STATISTICS_DETAILS = 1

// Програмско мерење pre-main time
@main
struct AppMain {
    static func main() {
        let launchStart = CFAbsoluteTimeGetCurrent()

        // UIApplicationMain се дешава овде
        AppDelegate.main()

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

// Оптимизација: замена +load са +initialize
class OptimizedClass {
    // ❌ +load се извршава пре main-а
    // override class func load() { }

    // ✅ +initialize се извршава при првом обраћању
    static let shared = OptimizedClass()
    private init() {
        // Иницијализација овде
    }
}

// Оптимизација броја dylib
// Обједињавање статичких библиотека смањује број LC_LOAD_DYLIB
// Користите -ObjC флаг за линковање само коришћених Objective-C класа
// Xcode: Build Settings > Mach-O Type > Static Library

За мерење pre-main time користите DYLD_PRINT_STATISTICS у Xcode шеми. Резултат ће приказати total time, dylib loading time, rebase/bind time, Objective-C setup time и initializer time. Циљне вредности: total < 400ms за хладно покретање, < 200ms за топло. Оптимизације: обједињавање библиотека, замена +load са +initialize, смањење броја Objective-C класа (користите Swift), минималан број динамичких фрејмворка.

dsc (dyld shared cache)

dyld shared cache — кеш унапред повезаних системских библиотека на iOS-у. Све системске dylib (UIKit, Foundation, CoreGraphics) су обједињене у једну датотеку: /System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm64. Ово елиминише потребу за посебним учитавањем сваке системске библиотеке — dyld приступа кешу, што значајно убрзава покретање. Апликације са 10+ динамичких фрејмворка доживљавају највеће кашњење, јер прилагођене dylib нису део dsc-а.

Често постављана питања

Шта је iOS Runtime и од којих компоненти се састоји?

iOS Runtime — окружење извршавања апликација на iOS-у, које укључује Objective-C Runtime (libobjc.dylib), Swift Runtime (libswiftCore.dylib), Cocoa Touch фрејмворке, dyld (динамички учитавач) и ARC (управљање меморијом). Обезбеђује message passing за Objective-C, статичку диспечацију за Swift, учитавање Mach-O датотека и аутоматско управљање меморијом.

Чиме се разликује Objective-C Runtime од Swift Runtime-а?

Objective-C Runtime користи динамичко повезивање кроз objc_msgSend (message passing) са касним повезивањем. Swift Runtime користи статичку диспечацију (vtable за класе, direct call за struct) ради перформанси. @objc dynamic укључује Objective-C Runtime за Swift класе. Swift struct нема isa показивач и не користи retain/release.

Како ради ARC у iOS-у?

ARC (Automatic Reference Counting) — управљање меморијом у фази компилације. Clang компајлер аутоматски убацује позиве retain/release. Сваки објекат има бројач референци, при чијем нулирању се позива dealloc. Retain cycle-ови (узајамне strong референце) се спречавају weak/unowned референцама. Користите Instruments > Leaks за откривање цурења.

Шта је dyld и како утиче на покретање апликације?

dyld — динамички учитавач Mach-O датотека. Учитава извршну датотеку и све зависне dylib, врши релокацију (ASLR), иницијализује Objective-C Runtime и позива main(). Pre-main time зависи од броја dylib и +load метода. Користите DYLD_PRINT_STATISTICS за мерење. Оптимизација: обједињавање библиотека, замена +load са +initialize.

Шта је Method Swizzling и када га користити?

Method Swizzling — техника замене IMP (implementation pointer) методе у лету кроз Objective-C Runtime class_getInstanceMethod и method_exchangeImplementations. Користи се за A/B тестирање, аналитику (аутоматско праћење екрана) и мониторинг. Не препоручује се у продукцији без крајње нужде. У Swift-у се замењује са @objc dynamic + Method Swizzling.

Резиме

  • iOS Runtime — окружење извршавања iOS апликација, које укључује Objective-C Runtime, Swift Runtime, dyld и ARC
  • Objective-C Runtime — message passing (objc_msgSend), isa pointer, swizzling, динамичке класе
  • Swift Runtime — статичка диспечација (vtable, direct call), value types, protocol witnesses
  • ARC (Automatic Reference Counting) — аутоматско управљање меморијом са retain/release у фази компилације
  • dyld — динамички учитавач Mach-O који одређује брзину покретања апликације (pre-main time)
  • Retain cycles — спречавају се weak/unowned референцама; отклањање кроз Instruments Leaks
  • Оптимизација — минимум +load, обједињавање dylib, коришћење Swift struct за value types

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође