Objective-C는 동적 메시지 디스패치를 갖춘 프로그래밍 언어로, 1980년대 Brad Cox가 만들었습니다. Apple은 NeXTSTEP의 주요 언어로, 이후 iOS SDK의 주요 언어로 Objective-C를 선택했습니다. Programming With Objective-C — 메시지 구문과 메모리 관리 소개.
핵심 요점
Objective-C는 Smalltalk 스타일의 객체 지향 프로그래밍 기능으로 C를 확장하는 프로그래밍 언어입니다. Objective-C 코드는 LLVM 또는 GCC를 통해 네이티브 머신 코드로 컴파일되며, C와의 완전한 하위 호환성을 유지합니다 — 모든 C 코드는 Objective-C에서 유효합니다.
Apple은 1997년 NeXT(Objective-C와 함께)를 인수했습니다. 이 언어는 macOS와 iOS의 프레임워크인 Cocoa와 Cocoa Touch의 기초가 되었습니다. 2014년부터 Apple은 Swift를 대체 언어로 추진하고 있지만, Objective-C는 레거시 프로젝트와 일부 시스템 프레임워크에 여전히 매우 중요합니다. Apple(WWDC 2024)에 따르면, App Store 앱의 약 35%가 여전히 Objective-C 코드를 포함하고 있습니다.
주요 특징은 동적 런타임입니다. 메서드 호출이 컴파일 시간에 해결되는 Swift와 달리, Objective-C는 objc_msgSend 함수를 통해 런타임에 메시지를 보냅니다. 이를 통해 메서드 즉시 재정의(method swizzling), 동적 클래스 추가, 존재하지 않는 선택자에 대한 forward invocation 사용이 가능합니다.
모든 C 코드는 Objective-C에서 유효합니다. 객체 지향 확장은 클래스(@interface/@implementation), 카테고리, 프로토콜(@protocol), 동적 타이핑(id)을 추가합니다. 파일 확장자는 .m(구현)과 .h(헤더)입니다.
메시지 구문이 Objective-C와 C 계열 언어의 주요 차이점입니다. object.method(argument) 대신 [object method:argument]를 사용합니다. 각 메시지는 objc_msgSend를 통과하며, 런타임에서 동적으로 메서드 구현을 찾습니다.
// 명명된 매개변수가 있는 메시지 구문
NSString *greeting = [NSString stringWithFormat:@"Hello, %@", name];
// 중첩 메시지
NSArray *sortedArray = [[array sortedArrayUsingSelector:@selector(compare:)] copy];
// 인트로스펙션을 통한 타입 확인
if ([object isKindOfClass:[UIView class]]) {
UIView *view = (UIView *)object;
view.backgroundColor = [UIColor redColor];
}동적 디스패치는 런타임에 메서드를 재정의(method swizzling)할 수 있게 합니다 — 강력하지만 위험한 기술입니다. 예를 들어, AFNetworking 및 Aspects와 같은 프레임워크는 URLSession 호출을 가로채기 위해 swizzling을 사용합니다. Apple은 경고합니다: 잘못 사용하면 swizzling이 시스템 프레임워크를 손상시킬 수 있습니다.
카테고리는 상속 및 소스 코드 접근 없이 기존 클래스(NSString 및 UIView와 같은 시스템 클래스 포함)에 메서드를 추가할 수 있는 Objective-C의 고유한 기능입니다. 카테고리는 @interface ClassName (CategoryName)을 사용하여 선언됩니다.
// 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 (). 카테고리와 달리 확장은 메서드뿐만 아니라 ivar(인스턴스 변수)와 프로퍼티도 추가할 수 있습니다. 확장은 외부 모듈에서 내부 API를 숨기는 데 사용됩니다.
블록은 외부 스코프에서 변수를 캡처하는 Objective-C의 익명 함수입니다. 블록 구문: ^(int x) { return x * 2; }. 블록은 UIKit의 콜백 핸들러, Grand Central Dispatch의 비동기 작업, NSArray/NSDictionary 컬렉션의 함수형 연산에 사용됩니다.
// 블록 타입 선언
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]를 통해 완전히 해결되었습니다.
Objective-C는 MRC(Manual Reference Counting)에서 ARC(Automatic Reference Counting)로 발전했습니다. MRC에서는 개발자가 수동으로 retain(카운터 증가), release(감소), autorelease(지연 해제)를 호출했습니다. 이러한 호출의 오류는 메모리 누수 또는 댕글링 포인터로 인한 충돌을 일으켰습니다.
| 작업 | MRC | ARC |
|---|---|---|
| 객체 생성 | [[Object alloc] init] | [[Object alloc] init] |
| 참조 유지 | [object retain] | 자동 |
| 해제 | [object release] | 자동 |
| 자동 해제 | [object autorelease] | 자동(필요 없음) |
| 약한 참조 | __weak 존재하지 않음 | __weak(자동 nil) |
ARC는 Xcode 4.2와 LLVM 3.0(2011)에서 도입되었습니다. 컴파일러는 객체 수명을 분석하여 컴파일 시간에 자동으로 retain/release를 삽입합니다. ARC는 가비지 컬렉션이 아닙니다 — 자동 삽입이 있는 정적 분석입니다. Objective-C ARC는 Swift ARC와 호환됩니다: 둘 다 런타임 수준에서 동일한 참조 카운팅 시스템을 사용합니다.
하나의 프로젝트에서 Objective-C와 Swift를 함께 사용하는 것은 레거시 프로젝트에서 일반적인 관행입니다. Apple은 Bridging Header를 제공합니다 — Swift가 Objective-C 클래스를 보고 Objective-C가 NSObject를 상속하는 Swift 클래스를 보는 자동 생성 파일입니다.
// ProjectName-Bridging-Header.h
// Swift가 이 헤더들을 자동으로 인식합니다
#import "LegacyManager.h"
#import "NetworkClient.h"
#import "DataStore.h"Objective-C에서 Swift로의 마이그레이션은 점진적인 과정입니다. 새 파일은 Swift로 작성되고, 기존 파일은 점진적으로 리팩터링됩니다. Bridging Header는 Objective-C 프로젝트에 첫 번째 Swift 파일을 추가할 때 자동으로 생성됩니다. 역방향 가시성(ObjC → Swift)을 위해 Xcode는 @objc로 표시된 Swift 클래스의 @interface 선언이 포함된 <ProjectName>-Swift.h 파일을 생성합니다.
// Objective-C에서 볼 수 있는 Swift 클래스
@objc class SwiftRouter: NSObject {
@objc func navigateToProfile(userId: Int) {
// 구현
}
}제한 사항: Swift의 값 타입(struct, enum)은 Objective-C에서 직접 볼 수 없습니다 — @objc와 함께 클래스로 래핑해야 합니다. Swift의 제네릭은 Objective-C에서 제한적으로 접근 가능합니다. 권장 접근 방식은 새 코드를 Swift로 작성하고 기존 Objective-C 코드는 해당 기능을 변경할 때만 리팩터링하는 것입니다.
Swift에 대한 Objective-C의 주요 장점은 런타임에 대한 완전한 접근입니다. Method swizzling(런타임에 메서드 구현 교체)은 상속 없이 시스템 메서드를 재정의할 수 있게 합니다. Aspects 및 JRSwizzle과 같은 라이브러리는 AOP(관점 지향 프로그래밍) — 모니터링, 분석, 로깅에 이 기능을 사용합니다.
Forward invocation은 또 다른 런타임 기능입니다: 객체가 선택자에 응답하지 않으면 시스템이 forwardInvocation:을 호출하여 메시지를 다른 객체로 전달할 수 있습니다. 이것은 Objective-C(NSProxy)의 프록시 패턴의 기초이며, 지연 초기화 및 분산 객체에 사용됩니다.
Apple은 새 코드에서 런타임 트릭 사용을 최소화하고 Swift의 정적 타이핑을 선호할 것을 권장합니다. 그러나 레거시 프로젝트에서는 objc_msgSend, method_exchangeImplementations 및 objc_getAssociatedObject에 대한 지식이 기존 코드베이스 유지에 필요합니다.
Objective-C는 getter/setter가 있는 ivar에 대한 프로퍼티를 구문 설탕으로 지원합니다. atomic/nonatomic, strong/weak/copy, readonly/readwrite 및 assign/retain 수식어가 메모리 동작과 스레드 안전성을 결정합니다. KVO(Key-Value Observing)는 런타임에 내장된 프로퍼티 변경 관찰 메커니즘입니다: 모든 객체는 addObserver를 통해 다른 객체의 프로퍼티 변경을 구독할 수 있습니다.
자주 묻는 질문
메서드 호출은 [object selector:argument]처럼 보입니다. 메시지는 objc_msgSend를 통과하며 런타임에서 동적으로 구현을 찾습니다. 선택자는 메서드 이름(@selector(methodName))이며 함수 포인터가 아닙니다. 이를 통해 swizzling과 forward invocation이 가능합니다.
카테고리(@interface ClassName (Name))는 상속 없이 모든 클래스(시스템 클래스 포함)에 메서드를 추가합니다. 확장(@interface ClassName ())은 .m 파일에 선언되며 프로퍼티와 ivars를 추가할 수 있습니다. 카테고리는 ivars를 추가할 수 없지만 확장은 가능합니다.
블록은 ^(매개변수) { 본문 } 구문의 익명 함수로, 컨텍스트에서 변수를 캡처합니다. C++의 람다와 Swift의 클로저와 유사합니다. self 캡처 시 retain cycle을 방지하기 위해 __weak가 필요합니다. UIKit, GCD, Foundation에서 사용됩니다.
MRC는 수동 참조 카운팅: 프로그래머가 retain, release, autorelease를 호출합니다. ARC는 자동: 컴파일러가 정적 분석에 기반하여 retain/release를 삽입합니다. ARC는 GC가 아닙니다 — 카운터가 0이 되면 객체가 즉시 해제됩니다. ARC는 __weak와 __strong 수식어를 지원합니다.
네, Bridging Header를 통해 가능합니다. Swift는 이 헤더를 통해 Objective-C를 보고, Objective-C는 <ProjectName>-Swift.h를 통해 Swift를 봅니다. Swift 클래스는 NSObject를 상속하고 @objc로 표시되어야 합니다. Swift의 struct와 enum은 Objective-C에서 직접 볼 수 없습니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.