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];
}

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

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

Categories — уникальная возможность 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

Extensions (Class Extension) — особый вид категории, объявленный в .m файле без имени: @interface ClassName (). В отличие от категорий, extensions могут добавлять не только методы, но и ivar (instance variables) и property. Extensions используются для скрытия внутреннего API от внешних модулей.

Блоки (Blocks) — анонимные функции с замыканием

Blocks — анонимные функции в Objective-C, захватывающие переменные из внешней области видимости. Синтаксис блоков: ^(int x) { return x * 2; }. Blocks используют в 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). Ошибки в этих вызовах приводили к утечкам памяти или крэшам из-за dangling pointers.

Операция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 на этапе компиляции, анализируя lifetime объектов. 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 генерирует файл -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 через -Swift.h. Swift-классы должны быть унаследованы от NSObject и отмечены @objc. Struct и enum из Swift не видны Objective-C напрямую.

Итоги

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

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

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