Первая программа на Objective-C
Черновик статьи. Ниже — пошаговый маршрут: Command Line Tools, clang, Foundation, класс с messaging, properties и сборка в Xcode.
| Шаг | Тема | Зачем |
|---|---|---|
| 1 | Xcode CLT | clang и SDK |
| 2 | Hello World CLI | Foundation, NSLog |
| 3 | @interface / @implementation | Класс и методы |
| 4 | NSString и literals | Текст |
| 5 | NSArray, NSDictionary | Коллекции |
| 6 | @property и ARC | Современный стиль |
| 7 | Xcode project | Отладка в IDE |
Контекст — история, о разделе. Современный стек — Swift.
Шаг 1 — подготовка
Objective-C для Apple-платформ разрабатывают на macOS с Xcode (App Store) или Command Line Tools:
xcode-select --install
clang --version
xcrun --show-sdk-path
| Компонент | Назначение |
|---|---|
| clang | Компилятор C/Obj-C/Swift |
| Foundation.framework | Базовые классы (NSString, NSArray) |
| Xcode | IDE, симулятор iOS |
Без Mac можно изучать синтаксис теоретически; практическая сборка iOS — только Apple toolchain.
Шаг 2 — Hello World (CLI)
main.m:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"Привет, Objective-C!");
}
return 0;
}
Сборка и запуск:
clang -fobjc-arc -framework Foundation main.m -o hello
./hello
Разбор:
#import <Foundation/Foundation.h>— базовые классы Apple.@autoreleasepool— scope для autorelease объектов под ARC.NSLog— лог в stderr с timestamp (не путать сprintf).-fobjc-arc— включить Automatic Reference Counting.
Шаг 3 — класс Greeter
Greeter.h:
#import <Foundation/Foundation.h>
@interface Greeter : NSObject
- (void)sayHelloTo:(NSString *)name;
+ (NSString *)defaultGreeting;
@end
Greeter.m:
#import "Greeter.h"
@implementation Greeter
- (void)sayHelloTo:(NSString *)name {
NSString *safe = name.length > 0 ? name : @"Гость";
NSLog(@"Hello, %@!", safe);
}
+ (NSString *)defaultGreeting {
return @"Hello, World!";
}
@end
main.m с классом:
#import <Foundation/Foundation.h>
#import "Greeter.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Greeter *g = [[Greeter alloc] init];
[g sayHelloTo:@"Аня"];
NSLog(@"%@", [Greeter defaultGreeting]);
}
return 0;
}
Сборка нескольких файлов:
clang -fobjc-arc -framework Foundation main.m Greeter.m -o greeter
./greeter
Разбор messaging:
[Greeter alloc]— сообщение классу, возвращает instance.init— инициализация (часто пишут[[Greeter alloc] init]).-— instance method;+— class method.NSString *— указатель на объект;nil— отсутствие объекта.
Шаг 4 — NSString
NSString *s1 = @"Literal";
NSString *s2 = [NSString stringWithFormat:@"Число: %d", 42];
BOOL empty = s1.length == 0;
NSString *upper = [s1 uppercaseString];
| Literal / API | Пример |
|---|---|
@"..." | Immutable string |
stringWithFormat: | Форматирование как printf |
length | Длина в UTF-16 code units |
isEqualToString: | Сравнение содержимого |
Не сравнивайте строки через == — только isEqualToString: или isEqual:.
Шаг 5 — NSArray и NSDictionary
NSArray *nums = @[ @1, @2, @3 ];
NSDictionary *user = @{
@"name": @"Anna",
@"role": @"admin",
};
for (NSNumber *n in nums) {
NSLog(@"%@", n);
}
NSString *role = user[@"role"];
Fast enumeration for (... in ...) — идиоматический обход. Mutable варианты: NSMutableArray, NSMutableDictionary.
Шаг 6 — @property
Современный стиль вместо ручных ivar:
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age;
@end
@implementation Person
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age {
self = [super init];
if (self) {
_name = [name copy];
_age = age;
}
return self;
}
@end
| Атрибут | Смысл |
|---|---|
nonatomic | Без блокировок (UI объекты) |
copy | Копия для NSString (immutable safety) |
assign | Примитивы (NSInteger) |
strong | Удержание объекта (по умолчанию для объектов) |
ARC синтезирует геттер/сеттер для @property.
Шаг 7 — проект в Xcode
- File → New → Project → macOS → Command Line Tool.
- Language: Objective-C.
- Добавьте
Greeter.h/Greeter.mв target. - Product → Run (⌘R).
Breakpoint в sayHelloTo: — Debug (⌘Y). Variables view показывает name как NSString.
Для iOS: iOS → App template — UIViewController в Obj-C (опционально в полной версии раздела).
Шаг 8 — blocks (фрагмент)
Blocks — замыкания в Obj-C:
void (^block)(void) = ^{
NSLog(@"Внутри block");
};
block();
NSArray *mapped = [nums valueForKey:@"description"];
Blocks важны для GCD и completion handlers в legacy iOS API. В Swift им соответствуют closures.
Типичные ошибки
| Симптом | Причина | Решение |
|---|---|---|
Foundation not found | Нет -framework Foundation | Добавить флаг линкера |
| unrecognized selector | Опечатка в сообщении | Проверить сигнатуру в .h |
| EXC_BAD_ACCESS | Сообщение deallocated объекту | Zombie Objects в Instruments |
String compare == fail | Сравнение указателей | isEqualToString: |
| ARC ошибки в MRC файле | Смешение стилей | -fobjc-arc на все .m |
Что дополнится в полной версии
- Минимальное UIKit приложение с label.
- Bridging header Swift → Obj-C.
- Instruments — leaks и retain cycles.
- Categories — добавление метода на NSString.
- Unit-тесты XCTest для Greeter.
Расширьте Greeter: метод greetingForHour: возвращает "Доброе утро" или "Добрый вечер" по NSCalendar. Соберите через clang и через Xcode, сравните пути сборки.
См. также: о разделе · история · Swift — о разделе.
Шаг 15 — NSCopying (обзор)
@interface Person : NSObject <NSCopying>
@property (nonatomic, copy) NSString *name;
@end
@implementation Person
- (id)copyWithZone:(NSZone *)zone {
Person *copy = [[Person alloc] init];
copy.name = self.name;
return copy;
}
@end
copy property attribute вызывает copyWithZone: — важно для NSString, NSArray.
Шаг 16 — Grand Central Dispatch preview
dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{
NSLog(@"Background");
});
Legacy concurrency до Swift async/await.
Шаг 17 — Instruments Leaks (обзор)
Xcode → Product → Profile → Leaks. Запустите app, воспроизведите retain cycle — Instruments покажет цепочку retain.
Упражнения CLI
- Greeter с
-greetingForHour:поNSCalendar. NSMutableArrayadd/remove в loop.- Category на NSString
trimmed. - Unit test на
defaultGreeting. - Сравнить
isEqual:и==для NSString.
Шаг 9 — NSError pattern
NSError *error = nil;
NSData *data = [NSData dataWithContentsOfURL:url options:0 error:&error];
if (!data) { NSLog(@"%@", error); return; }
Шаг 10 — argv и UTF-8
if (argc > 1) {
NSString *arg = [NSString stringWithUTF8String:argv[1]];
[greeter sayHelloTo:arg];
}
Шаг 11 — NSMutableArray
NSMutableArray *items = [NSMutableArray array];
[items addObject:@"a"];
[items removeLastObject];
Шаг 12 — протокол delegate
@protocol GreeterDelegate <NSObject>
- (void)didGreet:(NSString *)name;
@end
@property (nonatomic, weak) id<GreeterDelegate> delegate;
weak — без retain cycle.
Шаг 13 — lldb
lldb ./hello
(lldb) breakpoint set -n main
(lldb) run
(lldb) po greeter
Шаг 14 — Makefile
hello: main.m Greeter.m
clang -fobjc-arc -framework Foundation main.m Greeter.m -o hello
Упражнения
-sayGoodbyeTo:в Greeter.- Чтение
data.txtчерезstringWithContentsOfFile:encoding:error:. - CSV split
componentsSeparatedByString:@",". - JSON через
NSJSONSerialization. - Category
NSString (Greeting). - XCTest для
defaultGreeting. @autoreleasepoolв loop — memory.- Swift
@objc+ call from.m. greetingForHour:по NSCalendar.clang -fsyntax-onlycheck.
FAQ
Xcode обязателен? CLI enough for Foundation; UIKit needs Xcode.
-fobjc-arc? Explicit ARC for clang CLI.
#import vs #include? Import once.
.mm file? Obj-C++ mix.
NSLog in prod? Use os_log; ok for learning.
Leaks? Instruments.
GNUstep? Not drop-in for this tutorial.
Greeter without NSObject? Inherit NSObject for Apple model.
Git ignore? Git 117.
Delegate not called? Assign delegate property.
Encoding? NSUTF8StringEncoding.
Compare strings? isEqualToString: not ==.
Zombie? Instruments for use-after-free.
Bridging? Swift intro.
UIKit next? Single ViewController read-only.
Связанные материалы
| Тема | Ссылка |
|---|---|
| История | 1.md |
| О разделе | intro.md |
| Swift | /encyclopedia/5-languages/5-14-swift/intro |
| Git | /encyclopedia/4-code-dev/4-13-osnovy-raboty-s-git/112 |
Troubleshooting расширенный
| Симптом | Причина | Решение |
|---|---|---|
| Undefined symbols | .m не в compile | Makefile / Target Membership |
| ARC mismatch | MRC file | -fno-objc-arc per file |
| Delegate nil | weak deallocated | Strong ref elsewhere |
| XCTest fail link | No test target | Add Unit Testing Bundle |
| Header not found | Search paths | Header Search Paths |
@import fail | Old clang | Use #import |
Шаг 15 — NSString formatting
NSString *msg = [NSString stringWithFormat:@"%@ has %ld pts", name, (long)n];
%@ object, %ld long, %f double.
Шаг 16 — NSJSONSerialization
NSData *json = [@"{\"a\":1}" dataUsingEncoding:NSUTF8StringEncoding];
id obj = [NSJSONSerialization JSONObjectWithData:json options:0 error:&error];
Dictionary from JSON — common legacy API pattern.
Шаг 17 — category на NSString
@interface NSString (Greeting)
- (NSString *)greetingPrefix;
@end
@implementation NSString (Greeting)
- (NSString *)greetingPrefix { return [@"Hello, " stringByAppendingString:self]; }
@end
Шаг 18 — Instruments Leaks (обзор)
- Xcode → Product → Profile.
- Choose Leaks template.
- Run app; red bars = leak.
- Fix retain cycles with
weak.
Дополнительные упражнения
greetingForHour:morning/evening по calendar.- Read file line-by-line
enumerateLinesUsingBlock:. - Sort
NSArraywithsortedArrayUsingComparator:. - Write XCTest for
sayHelloTo:with empty string. - Docker not applicable — native macOS build only.
FAQ (дополнение)
Swift bridging auto? Xcode generates Target-Swift.h.
Next after CLI? UIKit ViewController read-only.
Makefile vs Xcode? Makefile for CI; Xcode for debug UI.
performSelector? Legacy dynamic dispatch — avoid in new code.
Core Foundation bridge? __bridge_transfer under ARC.
Copy block property? copy attribute standard for blocks.
Dot vs message syntax? Equivalent for properties; message for methods.
Fast enumeration mutable? Do not mutate collection during for-in.
NSLog vs os_log? os_log for production structured logging.
Simulator CLI? macOS Command Line Tool — not iOS simulator binary.
Шаги 1–18 покрывают CLI, class, collections, delegate, debug, Makefile. Дальше — UIKit и mixed Swift target.
См. также: о разделе · история · Swift.