Перейти к основному содержимому

Первая программа на Objective-C

Разработчику

Черновик статьи. Ниже — пошаговый маршрут: Command Line Tools, clang, Foundation, класс с messaging, properties и сборка в Xcode.

ШагТемаЗачем
1Xcode CLTclang и SDK
2Hello World CLIFoundation, NSLog
3@interface / @implementationКласс и методы
4NSString и literalsТекст
5NSArray, NSDictionaryКоллекции
6@property и ARCСовременный стиль
7Xcode 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)
XcodeIDE, симулятор 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

  1. File → New → Project → macOS → Command Line Tool.
  2. Language: Objective-C.
  3. Добавьте Greeter.h / Greeter.m в target.
  4. 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

Что дополнится в полной версии

  1. Минимальное UIKit приложение с label.
  2. Bridging header Swift → Obj-C.
  3. Instruments — leaks и retain cycles.
  4. Categories — добавление метода на NSString.
  5. 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

  1. Greeter с -greetingForHour: по NSCalendar.
  2. NSMutableArray add/remove в loop.
  3. Category на NSString trimmed.
  4. Unit test на defaultGreeting.
  5. Сравнить 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

Упражнения

  1. -sayGoodbyeTo: в Greeter.
  2. Чтение data.txt через stringWithContentsOfFile:encoding:error:.
  3. CSV split componentsSeparatedByString:@",".
  4. JSON через NSJSONSerialization.
  5. Category NSString (Greeting).
  6. XCTest для defaultGreeting.
  7. @autoreleasepool в loop — memory.
  8. Swift @objc + call from .m.
  9. greetingForHour: по NSCalendar.
  10. clang -fsyntax-only check.

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 не в compileMakefile / Target Membership
ARC mismatchMRC file-fno-objc-arc per file
Delegate nilweak deallocatedStrong ref elsewhere
XCTest fail linkNo test targetAdd Unit Testing Bundle
Header not foundSearch pathsHeader Search Paths
@import failOld clangUse #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 (обзор)

  1. Xcode → Product → Profile.
  2. Choose Leaks template.
  3. Run app; red bars = leak.
  4. Fix retain cycles with weak.

Дополнительные упражнения

  1. greetingForHour: morning/evening по calendar.
  2. Read file line-by-line enumerateLinesUsingBlock:.
  3. Sort NSArray with sortedArrayUsingComparator:.
  4. Write XCTest for sayHelloTo: with empty string.
  5. 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.