Objective-C: у чому суть мови та її місце в iOS-розробці

Автор: IT Sectr Опубліковано: 2026-02-09 Час читання: 8 хв

Objective-C — мова програмування з динамічною диспетчеризацією повідомлень, створена Бредом Коксом у 1980-х роках. Apple обрала Objective-C як основну мову для NeXTSTEP, а згодом і для iOS SDK. Programming With Objective-C — введення в синтаксис повідомлень та управління пам'яттю.

Головне

  • Objective-C — строга надмножина C із синтаксисом повідомлень у квадратних дужках та динамічним runtime
  • Message passing — виклик методу через [object selector:argument] із динамічним пошуком реалізації через objc_msgSend
  • Categories — механізм додавання методів до існуючих класів без успадкування та без доступу до вихідного коду
  • Blocks — анонімні функції із замиканням, попередник Swift closures
  • ARC vs MRC — автоматичний та ручний підрахунок посилань для управління пам'яттю

Що таке Objective-C?

Objective-C — мова програмування, що розширює C можливостями об'єктно-орієнтованого програмування в стилі Smalltalk. Код на Objective-C компілюється через LLVM або GCC у нативний машинний код, зберігаючи повну зворотну сумісність із C — будь-який C-код коректний в Objective-C.

Apple придбала NeXT (разом із Objective-C) у 1997 році. Мова стала основою Cocoa та Cocoa Touch — фреймворків для macOS та iOS. З 2014 року Apple просуває Swift як заміну, але Objective-C залишається критично важливим для легасі-проєктів та деяких системних фреймворків. За даними Apple (WWDC 2024), близько 35% додатків в App Store все ще містять код Objective-C.

Ключова особливість — динамічний runtime. На відміну від Swift, де виклики методів резолвляться на етапі компіляції, Objective-C надсилає повідомлення під час виконання через функцію objc_msgSend. Це дозволяє перевизначати методи на льоту (method swizzling), додавати класи динамічно та використовувати forward invocation для неіснуючих селекторів.

Синтаксис: C + об'єктна надбудова

Будь-який C-код коректний в Objective-C. Об'єктна надбудова додає класи (@interface/@implementation), категорії, протоколи (@protocol) та динамічну типізацію (id). Файли мають розширення .m (реалізація) та .h (заголовки).

Синтаксис повідомлень та динамічна диспетчеризація

Синтаксис повідомлень — головна відмінність Objective-C від C-подібних мов. Замість object.method(argument) використовується [object method:argument]. Кожне повідомлення проходить через objc_msgSend, який динамічно шукає реалізацію методу в runtime.

objective-c
// Синтаксис повідомлень з іменованими параметрами
NSString *greeting = [NSString stringWithFormat:@"Hello, %@", name];

// Вкладені повідомлення
NSArray *sortedArray = [[array sortedArrayUsingSelector:@selector(compare:)] copy];

// Перевірка типу через introspection
if ([object isKindOfClass:[UIView class]]) {
    UIView *view = (UIView *)object;
    view.backgroundColor = [UIColor redColor];
}

Динамічний dispatch дозволяє перевизначати методи в runtime (method swizzling) — потужна, але небезпечна техніка. Наприклад, фреймворки на кшталт AFNetworking та Aspects використовують swizzling для перехоплення викликів URLSession. Apple попереджає: swizzling може порушити роботу системних фреймворків при неправильному використанні.

Категорії та розширення класів

Категорії — унікальна можливість Objective-C додавати методи до існуючих класів (включаючи системні, на кшталт NSString та UIView) без успадкування та без доступу до вихідного коду. Категорії оголошуються через @interface ClassName (CategoryName).

objective-c
// Файл UIColor+Hex.h — категорія для UIColor
@interface UIColor (Hex)
+ (instancetype)colorWithHex:NSUIntegerhex;
@end

// Файл UIColor+Hex.m — реалізація
@implementation UIColor (Hex)
+ (instancetype)colorWithHex:NSUIntegerhex {
    CGFloat r = ((hex >> 16) & 0xFF) / 255.0;
    CGFloat g = ((hex >> 8) & 0xFF) / 255.0;
    CGFloat b = (hex & 0xFF) / 255.0;
    return [self colorWithRed:r green:g blue:b alpha:1.0];
}
@end

Розширення (Class Extension) — особливий вид категорії, оголошений в .m файлі без імені: @interface ClassName (). На відміну від категорій, розширення можуть додавати не тільки методи, але й ivar (змінні екземпляра) та property. Розширення використовуються для приховування внутрішнього API від зовнішніх модулів.

Блоки (Blocks) — анонімні функції із замиканням

Блоки — анонімні функції в Objective-C, що захоплюють змінні із зовнішньої області видимості. Синтаксис блоків: ^(int x) { return x * 2; }. Блоки використовують в UIKit для callback-обробників, в Grand Central Dispatch для асинхронних завдань та в колекціях NSArray/NSDictionary для функціональних операцій.

objective-c
// Оголошення типу блоку
typedef void (^CompletionBlock)(BOOL success, NSError *error);

// Блок як параметр
- (void)fetchDataWithCompletion:(CompletionBlock)completion {
    __weak typeof(self) weakSelf = self;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        BOOL result = [weakSelf processData];
        if (completion) {
            completion(result, nil);
        }
    });
}

Управління пам'яттю в блоках критично важливе. Блоки захоплюють self сильним посиланням, що створює retain cycle при прямому використанні. Рішення — __weak typeof(self) weakSelf = self, з подальшою перевіркою всередині блоку. Ця проблема повністю вирішена в Swift через capture lists [weak self].

ARC та MRC: управління пам'яттю

Objective-C пройшла шлях від MRC (Manual Reference Counting) до ARC (Automatic Reference Counting). В MRC розробник вручну викликав retain (збільшення лічильника), release (зменшення) та autorelease (відкладений release). Помилки в цих викликах призводили до витоків пам'яті або крашів через висячі покажчики.

ОпераціяMRCARC
Створення об'єкта[[Object alloc] init][[Object alloc] init]
Утримання посилання[object retain]Автоматично
Звільнення[object release]Автоматично
Автозвільнення[object autorelease]Автоматично (не потрібно)
Weak посилання__weak не існував__weak (автоматичний nil)

ARC був представлений в Xcode 4.2 та LLVM 3.0 (2011). Компілятор автоматично вставляє retain/release на етапі компіляції, аналізуючи життєвий цикл об'єктів. ARC не є збиранням сміття — це статичний аналіз з автоматичними вставками. Objective-C ARC сумісний з Swift ARC: обидва використовують одну систему підрахунку посилань на рівні runtime.

Взаємодія зі Swift через Bridging Header

Спільне використання Objective-C та Swift в одному проєкті — звичайна практика для легасі-проєктів. Apple надає Bridging Header — автоматично генерований файл, через який Swift бачить Objective-C класи, а Objective-C — Swift класи, успадковані від NSObject.

objective-c
// ProjectName-Bridging-Header.h
// Swift бачить ці заголовки автоматично
#import "LegacyManager.h"
#import "NetworkClient.h"
#import "DataStore.h"

Міграція з Objective-C на Swift — поетапний процес. Нові файли пишуться на Swift, старі поступово рефакторяться. Bridging Header автоматично генерується при додаванні першого Swift-файлу в Objective-C проєкт. Для зворотної видимості (ObjC → Swift) Xcode генерує файл <ProjectName>-Swift.h, що містить @interface для Swift-класів, позначених @objc.

swift
// Swift-клас, видимий з Objective-C
@objc class SwiftRouter: NSObject {
    @objc func navigateToProfile(userId: Int) {
        // реалізація
    }
}

Обмеження: Swift value-типи (struct, enum) не видні з Objective-C напряму — їх потрібно обгортати в class з @objc. Generics в Swift обмежено доступні з Objective-C. Рекомендований підхід — писати новий код на Swift, а існуючий Objective-C код рефакторити тільки при зміні відповідної функціональності.

Runtime та method swizzling в Objective-C

Головна перевага Objective-C перед Swift — повний доступ до runtime. Method swizzling, заміна реалізації методів під час виконання, дозволяє перевизначати системні методи без успадкування. Бібліотеки на кшталт Aspects та JRSwizzle використовують цю можливість для AOP (аспектно-орієнтованого програмування) — моніторингу, аналітики та логування.

Forward invocation — ще одна runtime-можливість: якщо об'єкт не відповідає на селектор, система викликає forwardInvocation:, дозволяючи перенаправити повідомлення іншому об'єкту. Це основа Proxy-патерну в Objective-C (NSProxy), який використовується для відкладеної ініціалізації та розподілених об'єктів.

Apple рекомендує мінімізувати використання runtime-трюків у новому коді, віддаючи перевагу статичній типізації Swift. Однак у легасі-проєктах знання objc_msgSend, method_exchangeImplementations та objc_getAssociatedObject необхідне для підтримки існуючої кодової бази.

Property та Key-Value Observing

Objective-C підтримує property як синтаксичний цукор над ivar з геттерами/сеттерами. Модифікатори atomic/nonatomic, strong/weak/copy, readonly/readwrite та assign/retain визначають поведінку пам'яті та потокобезпеку. Key-Value Observing (KVO) — механізм спостереження за змінами властивостей, вбудований в runtime: будь-який об'єкт може підписатися на зміни властивостей іншого об'єкта через addObserver.

Часто задавані питання

Що таке синтаксис повідомлень в Objective-C?

Виклик методу виглядає як [object selector:argument]. Повідомлення проходить через objc_msgSend, який динамічно знаходить реалізацію в runtime. Селектор — це ім'я методу (@selector(methodName)), а не вказівник на функцію. Це дозволяє swizzling та forward invocation.

Чим категорії (Categories) відрізняються від розширень (Extensions)?

Category (@interface ClassName (Name)) додає методи до будь-якого класу (включаючи системні) без успадкування. Extension (@interface ClassName ()) оголошується в .m файлі та може додавати property та ivar. Category не може додавати ivar, extension — може.

Що таке блоки (Blocks) в Objective-C?

Blocks — анонімні функції з синтаксисом ^(параметри) { тіло }, що захоплюють змінні з контексту. Аналог лямбд в C++ та closures в Swift. Вимагають __weak для запобігання retain cycles при захопленні self. Використовуються в UIKit, GCD та Foundation.

В чому різниця між ARC та MRC?

MRC — ручний підрахунок посилань: програміст викликає retain, release, autorelease. ARC — автоматичний: компілятор вставляє retain/release на основі статичного аналізу. ARC не є GC — об'єкти звільняються відразу при обнуленні лічильника. ARC підтримує __weak та __strong модифікатори.

Чи можна використовувати Objective-C та Swift в одному проєкті?

Так, через Bridging Header. Swift бачить Objective-C через цей заголовок, Objective-C бачить Swift через <ProjectName>-Swift.h. Swift-класи повинні бути успадковані від NSObject та позначені @objc. Struct та enum з Swift не видні Objective-C напряму.

Підсумки

  • Objective-C — строга надмножина C з динамічним runtime та синтаксисом повідомлень для Cocoa/Cocoa Touch
  • Message passing — виклики через [object method] з динамічною диспетчеризацією через objc_msgSend та можливістю swizzling
  • Категорії — додавання методів до існуючих класів без успадкування, включаючи системні класи Apple
  • Блоки — анонімні функції із замиканням, що вимагають __weak для запобігання retain cycles
  • ARC vs MRC — еволюція від ручного retain/release до автоматичного підрахунку посилань через компілятор
  • Bridging Header — механізм спільної роботи Swift та Objective-C в одному проєкті для поетапної міграції
  • Легасі-значимість — близько 35% додатків App Store містять код Objective-C, що потребує підтримки

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також