Objective-C — мова програмування з динамічною диспетчеризацією повідомлень, створена Бредом Коксом у 1980-х роках. Apple обрала Objective-C як основну мову для NeXTSTEP, а згодом і для iOS SDK. Programming With 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-код коректний в Objective-C. Об'єктна надбудова додає класи (@interface/@implementation), категорії, протоколи (@protocol) та динамічну типізацію (id). Файли мають розширення .m (реалізація) та .h (заголовки).
Синтаксис повідомлень — головна відмінність Objective-C від C-подібних мов. Замість object.method(argument) використовується [object method:argument]. Кожне повідомлення проходить через objc_msgSend, який динамічно шукає реалізацію методу в runtime.
// Синтаксис повідомлень з іменованими параметрами
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).
// Файл 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 від зовнішніх модулів.
Блоки — анонімні функції в Objective-C, що захоплюють змінні із зовнішньої області видимості. Синтаксис блоків: ^(int x) { return x * 2; }. Блоки використовують в UIKit для callback-обробників, в Grand Central Dispatch для асинхронних завдань та в колекціях NSArray/NSDictionary для функціональних операцій.
// Оголошення типу блоку
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].
Objective-C пройшла шлях від MRC (Manual Reference Counting) до ARC (Automatic Reference Counting). В MRC розробник вручну викликав retain (збільшення лічильника), release (зменшення) та autorelease (відкладений release). Помилки в цих викликах призводили до витоків пам'яті або крашів через висячі покажчики.
| Операція | MRC | ARC |
|---|---|---|
| Створення об'єкта | [[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.
Спільне використання Objective-C та Swift в одному проєкті — звичайна практика для легасі-проєктів. Apple надає Bridging Header — автоматично генерований файл, через який Swift бачить Objective-C класи, а Objective-C — Swift класи, успадковані від NSObject.
// 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-клас, видимий з 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 код рефакторити тільки при зміні відповідної функціональності.
Головна перевага 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 необхідне для підтримки існуючої кодової бази.
Objective-C підтримує property як синтаксичний цукор над ivar з геттерами/сеттерами. Модифікатори atomic/nonatomic, strong/weak/copy, readonly/readwrite та assign/retain визначають поведінку пам'яті та потокобезпеку. Key-Value Observing (KVO) — механізм спостереження за змінами властивостей, вбудований в runtime: будь-який об'єкт може підписатися на зміни властивостей іншого об'єкта через addObserver.
Часто задавані питання
Виклик методу виглядає як [object selector:argument]. Повідомлення проходить через objc_msgSend, який динамічно знаходить реалізацію в runtime. Селектор — це ім'я методу (@selector(methodName)), а не вказівник на функцію. Це дозволяє swizzling та forward invocation.
Category (@interface ClassName (Name)) додає методи до будь-якого класу (включаючи системні) без успадкування. Extension (@interface ClassName ()) оголошується в .m файлі та може додавати property та ivar. Category не може додавати ivar, extension — може.
Blocks — анонімні функції з синтаксисом ^(параметри) { тіло }, що захоплюють змінні з контексту. Аналог лямбд в C++ та closures в Swift. Вимагають __weak для запобігання retain cycles при захопленні self. Використовуються в UIKit, GCD та Foundation.
MRC — ручний підрахунок посилань: програміст викликає retain, release, autorelease. ARC — автоматичний: компілятор вставляє retain/release на основі статичного аналізу. ARC не є GC — об'єкти звільняються відразу при обнуленні лічильника. ARC підтримує __weak та __strong модифікатори.
Так, через Bridging Header. Swift бачить Objective-C через цей заголовок, Objective-C бачить Swift через <ProjectName>-Swift.h. Swift-класи повинні бути успадковані від NSObject та позначені @objc. Struct та enum з Swift не видні Objective-C напряму.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.