Основная философия Objective-C строится на двух столпах: строгое надмножество языка C и парадигма обмена сообщениями, унаследованная от Smalltalk. В отличие от вызова методов в C++ или Java, в Objective-C один объект *отправляет сообщение* другому. Синтаксически это выглядит как `[receiver message];`. Например, `[myArray count];` отправляет сообщение `count` объекту `myArray`.
Объявление класса начинается с интерфейса в файле `.h` и реализации в `.m`. Интерфейс определяет публичный контракт: родительский класс, свойства и методы. Ключевое слово `@property` автоматически генерирует геттеры и сеттеры, управляя памятью через атрибуты вроде `strong`, `weak` или `copy`. Реализация содержит фактический код методов. Директива `@synthesize` (явная или неявная) связывает свойство с экземплярной переменной (`_ivar`).
Управление памятью исторически осуществлялось через ручной подсчет ссылок (Manual Retain-Release, MRR) с методами `retain`, `release` и `autorelease`. Современный подход — Automatic Reference Counting (ARC), где компилятор автоматически вставляет эти вызовы. Понимание ARC обязательно: сильные (`strong`) ссылки удерживают объект, слабые (`weak`) не увеличивают счетчик и обнуляются при освобождении объекта, что критично для избежания циклов удержания.
Рассмотрим практический пример: создание простого менеджера для загрузки данных. Сначала объявим интерфейс класса `DataFetcher`.
В файле `DataFetcher.h`:
`@interface DataFetcher : NSObject
@property (nonatomic, strong) NSURLSession *session;
@property (nonatomic, copy) void (^completionHandler)(NSData *, NSError *);
- (instancetype)initWithURL:(NSURL *)url;
- (void)startFetch;
Теперь реализация в `DataFetcher.m`:
`#import "DataFetcher.h"
@implementation DataFetcher
- (instancetype)initWithURL:(NSURL *)url {
_session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [_session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (self.completionHandler) {
self.completionHandler(data, error);
}
}];
[task resume];
}
return self;
}
- (void)startFetch {
@end`
Обратите внимание на использование `self` внутри блока. Это создает сильную ссылку, потенциально вызывая цикл удержания, если `DataFetcher` также сильно удерживает блок. Чтобы избежать этого, используют слабую ссылку: `__weak typeof(self) weakSelf = self;` и затем внутри блока `strongSelf = weakSelf;`.
Работа с коллекциями — фундаментальный навык. NSArray, NSDictionary, NSSet и их изменяемые версии (с префиксом `Mutable`) — основа. Важно помнить, что `NSArray` может содержать только объекты. Для примитивных типов, таких как `int`, их нужно обернуть в `NSNumber`. Быстрый перебор осуществляется с помощью цикла `for-in`: `for (NSString *item in stringArray) { NSLog(@"%@", item); }`. Для более сложной фильтрации и трансформации используют предикаты (`NSPredicate`) и высоуровневые функции, хотя они менее выразительны, чем `map/filter` в Swift.
Категории (Categories) и расширения (Extensions) — мощные инструменты. Категория позволяет добавлять методы к существующему классу без наследования. Например, создадим категорию для `NSString` для проверки на email.
`@interface NSString (EmailValidation)
- (BOOL)isValidEmail;
- (BOOL)isValidEmail {
return [predicate evaluateWithObject:self];
}
@end`
Расширение (`@interface ClassName ()`) похоже на категорию, но объявляется в файле реализации и позволяет добавлять приватные свойства и методы.
Протоколы (Protocols) определяют набор методов, которые класс может реализовать. Они аналогичны интерфейсам в других языках. Необязательные методы помечаются `@optional`. Широко используются для делегирования (delegation) — паттерна, где один объект поручает другому выполнение задач. Например, `UITableViewDelegate` и `UITableViewDataSource` являются протоколами.
Работа с ошибками традиционно строится на классе `NSError`. Методы, которые могут завершиться неудачей, принимают указатель на указатель на `NSError`: `(NSError **)error`. Вызывающий код передает адрес переменной, чтобы метод мог его заполнить в случае ошибки. Это паттерн "pass-by-reference".
Для интеграции с кодом на C и C++ Objective-C предлагает Компиляцию в режиме Objective-C++ (файлы `.mm`). Это позволяет использовать C++ классы и STL непосредственно в коде Objective-C, что полезно при работе с кросс-платформенными движками, например.
Отладка и инструменты. Xcode остается основной IDE. Инструменты Instruments (Allocations, Leaks, Time Profiler) незаменимы для профилирования памяти и производительности. Понимание стеков вызовов и умение читать краш-логи с символикацией — ключевой навык.
Хотя будущее за Swift, Objective-C — это прочный фундамент. Его знание открывает доступ к внутреннему устройству Cocoa и Cocoa Touch, позволяет понимать и модифицировать огромные кодовые базы, написанные за последние десятилетия. Практикуйтесь, разбирая open-source проекты и постепенно переходя от простых утилит к более сложным модулям взаимодействия с системными фреймворками.
Комментарии (13)