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];
}Dynamic dispatch позволяет переопределять методы в runtime (method swizzling) — мощная, но опасная техника. Например, фреймворки вроде AFNetworking и Aspects используют swizzling для перехвата вызовов URLSession. Apple предупреждает: swizzling может нарушить работу системных фреймворков при неправильном использовании.
Categories — уникальная возможность 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];
}
@endExtensions (Class Extension) — особый вид категории, объявленный в .m файле без имени: @interface ClassName (). В отличие от категорий, extensions могут добавлять не только методы, но и ivar (instance variables) и property. Extensions используются для скрытия внутреннего API от внешних модулей.
Blocks — анонимные функции в Objective-C, захватывающие переменные из внешней области видимости. Синтаксис блоков: ^(int x) { return x * 2; }. Blocks используют в 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). Ошибки в этих вызовах приводили к утечкам памяти или крэшам из-за dangling pointers.
| Операция | 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 на этапе компиляции, анализируя lifetime объектов. 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 генерирует файл
// 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 через
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.