코딩 · 프로그래밍 언어
초보자를 위한 Objective-C 튜토리얼
핵심 답변
Objective-C는 C 위에 Smalltalk 스타일의 객체 지향 메시징을 얹은 언어입니다. 대괄호로 메시지를 보내는 방식([object doSomething:arg])으로 메서드를 호출하고, @interface와 @implementation 블록으로 클래스를 정의하며, 문자열·배열·딕셔너리에는 Apple의 Foundation 프레임워크를 사용합니다. Swift 이전에는 macOS와 iOS 앱의 주력 언어였고, 지금도 기존 Apple 코드베이스 곳곳에서 쓰입니다. Mac에서는 clang -fobjc-arc -framework Foundation hello.m으로 컴파일합니다.
Objective-C란 무엇이고 누가 쓰는가
Objective-C는 1980년대 초 Brad Cox와 Tom Love가 만들었고, NeXT가 NeXTSTEP 시스템에 채택했으며, 이후 NeXTSTEP이 macOS와 iOS의 기반이 되면서 Apple이 물려받았습니다. C의 엄격한 상위 집합이어서 모든 C 코드가 그대로 유효한 Objective-C이며, 두 언어를 자유롭게 섞어 쓸 수 있습니다.
오늘날 Apple은 새 앱에 Swift를 권장하지만 Objective-C도 완전히 지원합니다. 오래된 iOS·macOS 앱, 많은 시스템 프레임워크와 서드파티 라이브러리, C나 C++를 Apple API와 함께 쓰는 프로젝트에서 볼 수 있습니다. 한 프로젝트 안에서 Swift와 Objective-C가 함께 동작하므로 둘 다 알아 두면 Apple 플랫폼 개발자에게 유용합니다.
설치하고 Hello, world 실행하기
macOS에서는 Xcode 또는 명령줄 도구만(xcode-select --install) 설치하면 clang 컴파일러와 Foundation 프레임워크가 포함됩니다. 소스 파일의 확장자는 .m, 헤더는 .h입니다.
// hello.m
#import <Foundation/Foundation.h>
int main(void) {
@autoreleasepool {
NSLog(@"Hello, world!");
}
return 0;
}clang -fobjc-arc -framework Foundation hello.m -o hello
./hello
# 2026-10-31 10:00:00.000 hello[1234:5678] Hello, world!NSLog는 표준 오류로 출력하며 타임스탬프와 프로세스 이름을 붙입니다. @"..."는 C 문자열이 아니라 NSString 객체를 만듭니다. -fobjc-arc는 표준 메모리 관리 방식인 자동 참조 카운팅(ARC)을 켭니다. Linux에서는 GNUstep 프로젝트가 호환되는 Foundation 라이브러리를 제공하며, 빌드 명령은 다릅니다.
타입, 객체, 메시지
Objective-C는 C이므로 모든 C 타입을 쓸 수 있습니다. 객체는 항상 포인터(NSString *name)로 다룹니다. 메서드를 호출하는 것을 메시지를 보낸다고 하며, 인자는 메서드 이름의 각 부분 사이사이에 놓입니다.
| 타입 | 예시 | 비고 |
|---|---|---|
| C 숫자 | int, double, NSInteger | NSInteger는 64비트 플랫폼에서 64비트입니다 |
| BOOL | BOOL ok = YES; | YES / NO |
| NSString * | @"hello" | 불변 문자열 객체 |
| NSNumber * | @42, @3.14, @(x + 1) | 컬렉션에 담을 수 있도록 숫자를 객체로 감쌉니다 |
| id | id thing = @"x"; | 정적 타입 없이 어떤 객체든 가리키는 포인터 |
| nil | nil | 객체 널 포인터 |
NSString *name = @"Ana";
NSInteger age = 30;
NSString *line = [NSString stringWithFormat:@"%@ is %ld", name, (long)age];
NSLog(@"%@", line); // Ana is 30
NSLog(@"%lu", (unsigned long)[name length]); // 3
NSString *upper = [name uppercaseString]; // @"ANA"
BOOL same = [name isEqualToString:@"Ana"]; // YES
NSString *nothing = nil;
NSLog(@"%lu", (unsigned long)[nothing length]); // 0: messages to nil return 0포맷 문자열에서 %@는 객체를 출력합니다(객체의 description 메서드를 호출). nil에 메시지를 보내는 것은 허용되며 nil 또는 0을 반환합니다. 많은 크래시를 막아 주지만 버그를 숨길 수도 있습니다.
제어 흐름
제어 흐름은 C와 똑같습니다. if, else, switch, for, while, do ... while을 씁니다. Objective-C는 컬렉션을 위한 빠른 열거 for ... in을 더했습니다.
NSInteger score = 85;
if (score >= 90) {
NSLog(@"A");
} else if (score >= 80) {
NSLog(@"B");
} else {
NSLog(@"C");
}
for (int i = 1; i <= 3; i++) {
NSLog(@"%d squared is %d", i, i * i);
}
for (NSString *fruit in @[@"apple", @"pear"]) {
NSLog(@"%@", fruit);
}클래스, 프로퍼티, 메서드
클래스는 공개 인터페이스(보통 .h 파일)와 구현(.m 파일)으로 나뉩니다. 인스턴스 메서드는 -, 클래스 메서드는 +로 시작합니다. @property는 프로퍼티를 선언하고, getter, setter, 밑줄로 시작하는 이름의 백킹 변수를 자동으로 만듭니다.
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic) NSInteger age;
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age;
- (NSString *)greeting;
@end
@implementation Person
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age {
self = [super init];
if (self) {
_name = [name copy];
_age = age;
}
return self;
}
- (NSString *)greeting {
return [NSString stringWithFormat:@"Hi, I am %@, %ld", self.name, (long)self.age];
}
@end
// Usage inside main:
Person *ana = [[Person alloc] initWithName:@"Ana" age:30];
NSLog(@"%@", [ana greeting]); // Hi, I am Ana, 30
ana.age = 31; // dot syntax calls the setter객체는 두 단계로 만듭니다. alloc이 메모리를 확보하고 init... 메서드가 초기화합니다. 위 메서드의 이름은 initWithName:age:입니다(콜론도 이름의 일부). 프로토콜(@protocol)은 인터페이스 역할을 하고, 카테고리를 쓰면 NSString 같은 기존 클래스에도 메서드를 추가할 수 있습니다.
Foundation 컬렉션과 블록
Foundation 프레임워크는 불변 컬렉션과 그 가변 서브클래스를 제공합니다. 리터럴을 쓰면 짧게 작성할 수 있습니다. 컬렉션에는 객체만 담을 수 있으므로 숫자는 NSNumber로 감싸야 하고, nil은 저장할 수 없습니다(대신 [NSNull null]을 사용).
| 클래스 | 리터럴 또는 생성 | 가변 버전 |
|---|---|---|
| NSArray | @[@"Ana", @"Ben"] | NSMutableArray |
| NSDictionary | @{@"Ana": @30} | NSMutableDictionary |
| NSSet | [NSSet setWithArray:arr] | NSMutableSet |
| NSString | @"text" | NSMutableString |
NSArray<NSString *> *names = @[@"Ana", @"Ben"];
NSMutableArray<NSString *> *more = [names mutableCopy];
[more addObject:@"Cleo"];
NSDictionary<NSString *, NSNumber *> *ages = @{@"Ana": @30, @"Ben": @17};
NSLog(@"%lu names, Ana is %@", (unsigned long)more.count, ages[@"Ana"]);
NSInteger (^square)(NSInteger) = ^(NSInteger x) { return x * x; };
NSLog(@"%ld", (long)square(7)); // 49
NSArray<NSNumber *> *sorted = [@[@3, @1, @2]
sortedArrayUsingComparator:^NSComparisonResult(NSNumber *a, NSNumber *b) {
return [a compare:b];
}];
NSLog(@"%@", [sorted componentsJoinedByString:@", "]); // 1, 2, 3^로 쓰는 블록은 클로저, 즉 주변 스코프의 변수를 캡처하는 익명 함수입니다. Apple API는 콜백, 정렬, Grand Central Dispatch를 이용한 비동기 작업에 블록을 사용합니다. <NSString *> 부분은 컴파일러와 Swift를 돕는 경량 제네릭이며 생략할 수 있습니다.
메모리 관리와 오류 처리
ARC에서는 컴파일러가 참조 카운팅 호출을 대신 넣어 줍니다. 강한 참조가 하나도 가리키지 않으면 객체가 해제됩니다. 순환 참조만 끊어 주면 되며, 보통 한쪽을 weak로 표시합니다(예: delegate 프로퍼티).
복구 가능한 오류에는 NSError 패턴을 씁니다. 메서드는 실패하면 nil 또는 NO를 반환하고, 참조로 전달받은 NSError 객체를 채웁니다. 예외(@try, @catch, NSException)도 있지만 Cocoa에서는 범위를 벗어난 인덱스 같은 프로그래머 실수를 알리는 용도이며 일반적인 실패에는 쓰지 않습니다.
NSError *error = nil;
NSString *text = [NSString stringWithContentsOfFile:@"/no/such/file"
encoding:NSUTF8StringEncoding
error:&error];
if (text == nil) {
NSLog(@"Failed: %@", error.localizedDescription);
}작은 완성 프로그램: 단어 빈도
#import <Foundation/Foundation.h>
int main(void) {
@autoreleasepool {
NSString *text = @"the cat and the hat and the bat";
NSMutableDictionary<NSString *, NSNumber *> *counts = [NSMutableDictionary dictionary];
for (NSString *word in [text componentsSeparatedByString:@" "]) {
counts[word] = @(counts[word].integerValue + 1);
}
NSArray<NSString *> *sorted = [counts keysSortedByValueUsingComparator:
^NSComparisonResult(NSNumber *a, NSNumber *b) { return [b compare:a]; }];
for (NSString *word in [sorted subarrayWithRange:NSMakeRange(0, 2)]) {
printf("%s %ld\n", word.UTF8String, (long)counts[word].integerValue);
}
}
return 0;
}
// the 3
// and 2새로운 단어는 counts[word]가 nil이고 nil.integerValue는 0을 반환하므로 첫 횟수가 1이 됩니다. 비교자가 순서를 뒤집어 횟수가 많은 순으로 정렬하고, printf는 NSLog의 접두사 없이 출력합니다.
다음 단계
- 언어 기초는 문서 아카이브에 있는 Apple의 "Programming with Objective-C" 가이드.
- Apple 개발자 문서의 Foundation 프레임워크 레퍼런스.
- 정확한 메모리 규칙은 자동 참조 카운팅에 관한 Clang 문서.
- 혼합 코드베이스를 유지보수한다면 Objective-C와 Swift가 함께 동작하는 방식을 익혀 보세요.
자주 묻는 질문
Objective-C는 아직도 쓰이나요?
네, 주로 기존 iOS·macOS 앱과 라이브러리에서 쓰입니다. Apple은 새 코드에 Swift를 권장하지만 Xcode와 Apple SDK는 여전히 Objective-C를 지원합니다.
Swift와 Objective-C 중 무엇을 먼저 배워야 하나요?
새로 Apple 개발을 시작한다면 Swift를 먼저 배우세요. 오래된 코드를 읽거나 유지보수해야 할 때, C나 C++ 라이브러리를 긴밀하게 쓸 때, Apple 프레임워크가 내부적으로 어떻게 동작하는지 이해하고 싶을 때 Objective-C를 배우면 됩니다.
Objective-C의 대괄호는 무슨 뜻인가요?
메시지를 보낸다는 뜻입니다. [receiver message]는 receiver 객체의 message 메서드를 호출합니다. 인자는 [dict setObject:value forKey:key]처럼 콜론 뒤에 옵니다.
Objective-C에는 가비지 컬렉션이 있나요?
현대 Objective-C는 컴파일러가 retain과 release 호출을 추가하는 자동 참조 카운팅(ARC)을 씁니다. macOS용 Apple의 예전 가비지 컬렉터는 지원 중단되어 더 이상 사용할 수 없습니다.