iOS Runtime — Apple iOS əməliyyat sistemində tətbiqlərin icra mühiti, Objective-C Runtime, Swift Runtime, Cocoa Touch freymvorkları və Automatic Reference Counting (ARC) vasitəsilə yaddaş idarəetmə mexanizmlərini əhatə edir. iOS Runtime metodların dinamik bağlanmasına (message passing), siniflərin yüklənməsinə, yaddaş idarəetməsinə və iOS freymvorkları vasitəsilə aparat təminatı ilə qarşılıqlı əlaqəyə cavabdehdir. Apple Developer Documentation-a görə, runtime anlayışı performans optimallaşdırması, sazlama və sabit iOS tətbiqlərinin hazırlanması üçün zəruridir.
Əsas məqamlar
iOS Runtime — Apple cihazlarında iOS altında tətbiqlərin icrasını təmin edən sistem komponentlərinin məcmusudur. Objective-C Runtime (libobjc.A.dylib kitabxanası), Swift Runtime (libswiftCore.dylib), Core Foundation, Cocoa Touch freymvorkları (UIKit, Foundation), dinamik yükləyici dyld və yaddaş, thread-lar və proseslərarası kommunikasiyanı idarə etmək üçün icra mühitini əhatə edir.
Arxitektura baxımından iOS Runtime üç səviyyədə işləyir. Aşağı səviyyədə — Mach-O ikili formatı və icra olunan faylı və kitabxanaları yükləyən dyld. Orta səviyyədə — metod dispetçerizasiyasına və obyektlərin idarə edilməsinə cavabdeh olan Objective-C Runtime və Swift Runtime. Yuxarı səviyyədə — tərtibatçı üçün API təmin edən Cocoa Touch freymvorkları (UIKit, Foundation, Core Data, Metal).
iOS Runtime anlayışı tərtibatçıya mürəkkəb tapşırıqları həll etməyə imkan verir: A/B testi və analitika üçün metod svizzlinqi (Method Swizzling), siniflərin dinamik yüklənməsi, ARC anlayışı vasitəsilə yaddaş optimallaşdırması, retain cycle və yaddaş sızmalarının aradan qaldırılması, dyld vasitəsilə tətbiqin işə salınma vaxtının optimallaşdırılması. Runtime bilikləri olmadan sistem səviyyəsində profilləşdirmə və optimallaşdırma mümkün deyil.
| Komponent | Kitabxana | Təyinat |
|---|---|---|
| Objective-C Runtime | libobjc.A.dylib | Message passing, dinamik siniflər, svizzlinq |
| Swift Runtime | libswiftCore.dylib | Value types, generics, protocol witnesses |
| Core Foundation | CoreFoundation.framework | CFType, toll-free bridging |
| dyld | dyld (usr/lib/dyld) | Mach-O yüklənməsi, kitabxanaların bağlanması |
| libSystem | libSystem.B.dylib | POSIX threads, libc, libdispatch (GCD) |
iOS üçün tətbiqlər Mach-O (Mach Object) formatına tərtib edilir. Mach-O faylı başlıq (header), yükləmə əmrləri (load commands) və seqmentlərdən (segments) ibarətdir: __TEXT (kod, sabitlər), __DATA (qlobal dəyişənlər, Objective-C metadata), __LINKEDIT (simvollar, yerləşdirmə cədvəlləri). dyld Mach-O-nu təhlil edir və ilk təlimatı icra etməzdən əvvəl asılılıqları yükləyir.
Objective-C Runtime — iOS Runtime-ın ən güclü hissəsidir. Erkən bağlanma (early binding) ilə C++-dan fərqli olaraq, Objective-C message passing vasitəsilə gec bağlanma (late binding) istifadə edir. [receiver message] metod çağırışı birbaşa funksiya çağırışı kimi deyil, obyektin sinifində metodun icrasını dinamik olaraq tapan objc_msgSend(receiver, @selector(message)) kimi tərtib edilir.
Hər bir Objective-C obyekti öz sinfinə isa göstəricisini saxlayır. Sinif metod siyahısını (method list), metod keşini (method cache) və super sinfə göstəricini ehtiva edir. objc_msgSend miras zəncirini gəzir: sinif keşini yoxlayır, sonra method list-i, daha sonra super sinfə keçir. Metod tapılmazsa, forward işə salınır: resolveInstanceMethod, forwardingTargetForSelector və forwardInvocation.
Method Swizzling — runtime-da IMP (implementation pointer) mübadiləsi vasitəsilə metod icrasının uçarkən dəyişdirilməsi texnikası. A/B testi, analitika (ekranların avtomatik izlənməsi) və monitorinq üçün istifadə olunur. OS yeniləmələri ilə ziddiyyət təşkil edə biləcəyi üçün son zərurət olmadan istehsalatda tövsiyə edilmir.
// viewDidLoad izlənməsi üçün 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 {
// Hadisənin izlənməsi
NSLog(@"View Did Load: %@", self.class);
// Orijinal icranın çağırılması
[self swizzled_viewDidLoad];
}
@end UIViewController (Tracking) kateqoriyası tətbiqdəki bütün UIViewController-lərdə viewDidLoad-u swizzled_viewDidLoad ilə əvəz edir. dispatch_once tək svizzlinqi təmin edir. class_addMethod ikiqat svizzlinqin və super siniflərlə konfliktlərin qarşısını alır. Kontrollerlərin mənbə kodunu dəyişmədən analitikada ekranların avtomatik izlənməsi üçün istifadə olunur.
Müasir iOS-da (arm64) Apple isa göstəricisini optimallaşdırıb: bu, sadəcə sinif ünvanı deyil, yaddaş idarəetmə bayraqları və sinif haqqında məlumat ehtiva edən bit sahəsidir (non-pointer isa). Tagged pointers — başqa bir optimallaşdırma: kiçik ölçülü NSNumber, NSDate və NSString dəyərləri yığında obyektlər kimi deyil, birbaşa göstəricidə saxlanılır, malloc və retain/release yüklərini aradan qaldırır. tagged pointer isa-nın ən kiçik biti ilə tanınır.
Swift Runtime Objective-C Runtime-dan prinsipial olaraq fərqlənir: Swift standart olaraq sinif metodları üçün vtable və value types və extension methods üçün direct call vasitəsilə statik dispetçerizasiyadan (static dispatch) istifadə edir. Dinamik dispetçerizasiya (dynamic dispatch) yalnız @objc və ya dynamic ilə işarələnmiş metodlar üçün istifadə olunur. Bu, Objective-C ilə müqayisədə 40%-ə qədər performans artımı verir.
Value types (struct, enum) Swift-də — Objective-C-dən əsas fərqdir. Onlar yığında (stack) və ya başqa obyektin daxilində saxlanılır, retain/release istifadə etmir və istinad sayğacı üçün ARC-də iştirak etmir. Struct isa göstəricisinə malik deyil və objc_msgSend vasitəsilə göndərilə bilməz. Protocol witnesses — existential container üçün dinamik dispetçerizasiyaya imkan verən protokollar üçün vtable analoqudur.
Swift Runtime həmçinin generics (mangled symbols vasitəsilə reified generics) və string, array, dictionary, set optimallaşdırması üçün COW (Copy-on-Write) daxildir. Kolleksiya kopyalanarkən həqiqi kopyalama yalnız nüsxələrdən biri dəyişdirildikdə baş verir. Bu, kolleksiyaların funksiyalar arasında ötürülməsi zamanı yükləri minimuma endirir.
import Foundation
// Swift: statik dispetçerizasiya (class üçün vtable)
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 istifadəsi
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")
}Nümunə Swift-də üç növ dispetçerizasiyanı nümayiş etdirir: class (Dog) üçün vtable, @objc dynamic (Cat) üçün objc_msgSend və struct (Cow) üçün direct call. Existential container-də ([SoundMaker]) protocol witnesses overhead əlavə edir. Praktikada Swift mümkün olan hər yerdə statik dispetçerizasiyanı seçir, C-yə yaxın performans təmin edir.
Swift Runtime Objective-C Runtime ilə tam uyğunluq üçün layihələndirilmişdir. NSObject-dən miras alan hər bir Swift sinfi avtomatik olaraq Objective-C Runtime-da qeydiyyatdan keçir və objc_msgSend vasitəsilə çağırıla bilər. @objc atributu Swift metodunu Objective-C-dən əlçatan edir. String körpüsü: Swift String Objective-C API-yə ötürülərkən avtomatik olaraq NSString-ə körpülənir (toll-free bridging).
ARC (Automatic Reference Counting) — tərtib mərhələsində işləyən iOS-da yaddaş idarəetmə sistemi. Kompilyator (Clang) obyektlərin həyat dövrünü təhlil edir və avtomatik olaraq retain/release/autorelease çağırışlarını yerləşdirir. Tərtibatçı onları əl ilə çağırmalı deyil — iOS 5-dən əvvəlki Manual Retain-Release (MRR)-dən fərqli olaraq. ARC Objective-C obyektləri və Swift class səviyyəsində işləyir, lakin value types (struct, enum) üçün deyil.
Hər bir Objective-C obyekti və Swift classı non-pointer isa daxilində extra_rc sahəsində saxlanılan istinad sayğacına (retain count) malikdir. Obyekt yaradıldıqda retain count = 1. Retain zamanı sayğac artır, release zamanı azalır. Sayğac 0-a çatdıqda obyekt dealloc (Objective-C) və ya deinit (Swift) vasitəsilə dealokasiya edilir. ARC thread-safe-dir: retain/release atomik əməliyyatlardan (OSAtomicIncrement32/OSAtomicDecrement32) istifadə edir.
Retain cycles — ARC-in əsas problemi. Əgər A obyekti B-yə strong istinad saxlayırsa, B isə A-ya strong istinad saxlayırsa, hər iki obyekt heç vaxt dealokasiya edilməyəcək, çünki onların istinad sayğacları sıfırlanmayacaq. Həll yolu — zəif istinadlar (__weak Objective-C-də, weak Swift-də) və ya unowned istinadlar. Zəif istinadlar retain count-u artırmır və obyektin dealokasiyası zamanı avtomatik olaraq sıfırlanır (nil).
import Foundation
// Retain cycle nümunəsi
class Parent {
var child: Child?
deinit { print("Parent deallocated") }
}
class Child {
var parent: Parent? // strong — retain cycle yaradır!
deinit { print("Child deallocated") }
}
var parent: Parent? = Parent()
var child: Child? = Child()
parent?.child = child
child?.parent = parent // dövr: Parent -> Child -> Parent
parent = nil
child = nil
// deinit çağırılmır — yaddaş sızması!
// Düzəliş: weak
class WeakChild {
weak var parent: Parent? // weak — retain count-u artırmır
deinit { print("WeakChild deallocated") }
}
// Düzəliş: unowned (zəmanətli həyat müddəti üçün)
class UnownedChild {
unowned let parent: Parent
init(parent: Parent) { self.parent = parent }
deinit { print("UnownedChild deallocated") }
}
// Instruments vasitəsilə yoxlama
func profileMemory() {
// 1. Instruments > Leaks açın
// 2. Obyektlər yaradan hərəkəti yerinə yetirin
// 3. Leaks-də sızmaları yoxlayın
// 4. Allocations-da dealloc olmayan obyektləri tapın
for _ in 0..<1000 {
let p = Parent()
let c = WeakChild()
p.child = c as? Child
// c.parent = p — ƏLAVƏ ETMİRİK, weak
}
}Parent və Child arasında retain cycle nümunəsi: hər ikisi bir-birinə strong istinadlar saxlayır, ARC sayğacları sıfırlaya bilmir. Düzəliş — Child-da weak parent. weak parent-in dealokasiyası zamanı avtomatik sıfırlanır. unowned — parent-in həyat müddəti child-dən uzun olduğu hallar üçün (məsələn, viewController və view). Retain cycle-ları erkən mərhələdə aşkar etmək üçün Instruments > Leaks istifadə edin.
Autorelease pool — açıq mülkiyyət olmadan yaradılmış obyektlər üçün təxirə salınmış release mexanizmi. Swift və Objective-C-də @autoreleasepool { } blokun sonunda boşaldılan, hovuzdakı hər bir obyektə release göndərən bir hovuz yaradır. Dövrlərdə (minlərlə müvəqqəti obyekt yaratmaq) və RunLoop olmadan fon thread-larında kritik əhəmiyyət daşıyır. UIKit RunLoop hər iterasiyada əsas autorelease hovuzunu avtomatik boşaldır.
dyld (dynamic link editor) — iOS tətbiqi işə salındıqda Mach-O icra olunan fayllarını və əlaqəli dinamik kitabxanaları (dylib) yükləməyə cavabdeh olan sistem yükləyicisidir. dyld /usr/lib/dyld ünvanında yerləşir və libSystem-in bir hissəsidir. Yükləmə prosesi bir neçə mərhələni əhatə edir: Mach-O-nun təhlili, asılılıqların yüklənməsi (Library Loader, LC_LOAD_DYLIB), ünvanların yerləşdirilməsi (ASLR), Objective-C Runtime-ın işə salınması və main()-in çağırılması.
Tətbiqin işə salınma vaxtı (launch time) kritik olaraq dyld-dən asılıdır: nə qədər çox dinamik kitabxana və Objective-C sinfi varsa, pre-main time daha uzun olur. Apple +load metodlarının sayını minimuma endirməyi tövsiyə edir (onlar main-dən əvvəl icra olunur), onları +initialize (tənbəl inisiallaşdırma) ilə əvəz etməyi məsləhət görür. 2020-ci ildən Apple iOS-da prebuilt dyld cache istifadə edir: sistem kitabxanaları vahid keşdə əvvəlcədən bağlanır, bu da yükləməni sürətləndirir.
import Foundation
// DYLD_PRINT_STATISTICS vasitəsilə işə salınma vaxtının ölçülməsi
// Xcode-da: Edit Scheme > Run > Arguments > Environment Variables
// DYLD_PRINT_STATISTICS = 1
// DYLD_PRINT_STATISTICS_DETAILS = 1
// Pre-main time-ın proqram ölçülməsi
@main
struct AppMain {
static func main() {
let launchStart = CFAbsoluteTimeGetCurrent()
// UIApplicationMain burada baş verir
AppDelegate.main()
let launchEnd = CFAbsoluteTimeGetCurrent()
let preMainTime = launchEnd - launchStart
print("Pre-main time: (preMainTime) sec")
}
}
// Optimallaşdırma: +load-un +initialize ilə əvəz edilməsi
class OptimizedClass {
// ❌ +load main-dən əvvəl icra olunur
// override class func load() { }
// ✅ +initialize ilk müraciətdə icra olunur
static let shared = OptimizedClass()
private init() {
// İnisiallaşdırma burada
}
}
// dylib sayının optimallaşdırılması
// Statik kitabxanaların birləşdirilməsi LC_LOAD_DYLIB sayını azaldır
// Yalnız istifadə olunan Objective-C siniflərini keçidləmək üçün -ObjC bayrağından istifadə edin
// Xcode: Build Settings > Mach-O Type > Static LibraryPre-main time ölçmək üçün Xcode sxemində DYLD_PRINT_STATISTICS istifadə edin. Nəticə total time, dylib loading time, rebase/bind time, Objective-C setup time və initializer time göstərəcək. Hədəf dəyərlər: soyuq başlanğıc üçün total < 400ms, isti başlanğıc üçün < 200ms. Optimallaşdırmalar: kitabxanaların birləşdirilməsi, +load-un +initialize ilə əvəz edilməsi, Objective-C siniflərinin sayının azaldılması (Swift istifadə edin), dinamik freymvorkların minimal sayı.
dyld shared cache — iOS-da əvvəlcədən bağlanmış sistem kitabxanalarının keşi. Bütün sistem dylib-ləri (UIKit, Foundation, CoreGraphics) bir faylda birləşdirilib: /System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm64. Bu, hər bir sistem kitabxanasını ayrıca yükləmək ehtiyacını aradan qaldırır — dyld keşə müraciət edir, bu da işə salmanı əhəmiyyətli dərəcədə sürətləndirir. 10+ dinamik freymvorklu tətbiqlər ən böyük gecikmə ilə üzləşir, çünki fərdi dylib-lər dsc-ə daxil deyil.
Tez-tez verilən suallar
iOS Runtime — iOS-da tətbiqlərin icra mühiti, Objective-C Runtime (libobjc.dylib), Swift Runtime (libswiftCore.dylib), Cocoa Touch freymvorkları, dyld (dinamik yükləyici) və ARC (yaddaş idarəetməsi) daxildir. Objective-C üçün message passing, Swift üçün statik dispetçerizasiya, Mach-O fayllarının yüklənməsi və avtomatik yaddaş idarəetməsini təmin edir.
Objective-C Runtime gec bağlanma ilə objc_msgSend (message passing) vasitəsilə dinamik bağlanmadan istifadə edir. Swift Runtime performans üçün statik dispetçerizasiyadan (siniflər üçün vtable, struct üçün direct call) istifadə edir. @objc dynamic Swift sinifləri üçün Objective-C Runtime-ı aktivləşdirir. Swift struct isa göstəricisinə malik deyil və retain/release istifadə etmir.
ARC (Automatic Reference Counting) — tərtib mərhələsində yaddaş idarəetməsi. Clang kompilyatoru avtomatik olaraq retain/release çağırışlarını yerləşdirir. Hər bir obyektin istinad sayğacı var, sıfırlandıqda dealloc çağırılır. Retain cycle-lar (qarşılıqlı strong istinadlar) weak/unowned istinadlarla qarşısı alınır. Sızmaları aşkar etmək üçün Instruments > Leaks istifadə edin.
dyld — Mach-O fayllarının dinamik yükləyicisi. İcra olunan faylı və bütün asılı dylib-ləri yükləyir, yerləşdirməni (ASLR) həyata keçirir, Objective-C Runtime-ı işə salır və main()-i çağırır. Pre-main time dylib-lərin sayından və +load metodlarından asılıdır. Ölçmək üçün DYLD_PRINT_STATISTICS istifadə edin. Optimallaşdırma: kitabxanaların birləşdirilməsi, +load-un +initialize ilə əvəz edilməsi.
Method Swizzling — Objective-C Runtime class_getInstanceMethod və method_exchangeImplementations vasitəsilə metodun IMP (implementation pointer) uçarkən dəyişdirilməsi texnikası. A/B testi, analitika (ekranların avtomatik izlənməsi) və monitorinq üçün istifadə olunur. Son zərurət olmadan istehsalatda tövsiyə edilmir. Swift-də @objc dynamic + Method Swizzling ilə əvəz olunur.
Xülasə
Açar təslim mobil tətbiq hazırlayacağıq
IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.
Həm də oxuyun