Starbucks Caramel Frappuccino
본문 바로가기
  • 그래 그렇게 조금씩
iOS Developer/Objective-C

1. Objective-C / Hello world!

by Toughie 2024. 5. 3.

KAKAO-Choonsik

 

Objective-C (옵젝씨라고도 부름)는 Swift 이전에 iOS, Mac OS 개발에 사용된 언어로

이름에서 알 수 있듯이 객체지향의 4가지 근간인 (캡슐화, 데이터 숨기기, 상속, 다형성)을 지원한다. 

 

가장 기본적인 Hello world를 찍어보고 관련 개념들을 알아보자. 자바처럼 좀 길다..

??? : print("Hello world!") 

 

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    
    NSLog (@"hello world");
    [pool drain];
    return 0;
}

 

#import

헤더파일을 구현 파일에 포함시키는 지시어.

Foundation의 경우 꺽쇠 안에 경로를 지정해서 넣어준다.


Foundation Framework

UIKit 개발하면서 많이 본 프레임워크.

확장된 타입인 NSArray, NSDictionary, NSSet과 같은 자료형.

파일, 문자열 조작을 위한 다양한 함수들.

URL 핸들링, date 포메팅, 데이터 핸들링, 에러 핸들링과 같은 기능들이 포함되어 있다.


int main(int argc, const char * argv[])

프로그램의 entry 포인트. 즉 진입점이다. (프로그램이 실행될 때 운영체제의 초기 제어를 받음)


argc(Argument Count)

명령줄을 통해 프로그램에 전달된 인자의 수

첫 번째 인자는 항상 실행 파일의 이름 자체이기에, argc는 항상 1 이상이다.

ex) ./myProgram 실행 -> argc는 1, ./myProgram option1 -> argc는 2 이런 식이다.

 

argv(Argument Vector)

실제 인자의 내용을 담고 있는 문자열 배열을 가리키는 포인터

argv[0]은 프로그램의 실행 파일 이름, argv[1]부터 argv[argc-1]까지가 실제 전달된 인자들.

argv[argc]는 NULL 포인터로 인자리스트의 끝을 나타낸다.

 

CLI 환경에서 실행하거나 유닛 테스트 등을 진행할 때 main 함수에 직접 인자를 전달할 수 있다.


옵젝씨로 파일을 만들면. h파일과. m파일이 함께 생성된다. 

c언어에서 코드 상단부에 변수를 선언하고, 함수 프로토타입을 선언하거나 아예 헤더 파일을 분리하는 것과 동일하다.

 

헤더파일(.h) 

클래스 선언 - 클래스의 인터페이스 선언(프로퍼티, public 메서드, 채택하는 프로토콜)

상수, 열거형 - 전역으로 사용할 상수나 열거형 타입 정의

함수 선언 - 전역 함수의 프로토타입

 

구현파일(.m)

헤더파일에 선언된 것들을 구현해 주는 곳.

특히 클래스 외부에 노출되지 않는 private 함수들을 구현해 준다.

 

왜 이렇게 인터페이스와 구현을 분리할까?

1. 캡슐화 - 내부 로직과 구현은 숨기고, 인터페이스만 노출해서 캡슐화 원칙을 지킬 수 있다.

2. 재사용성, 유지보수성 향상 - 인터페이스는 놔두고 내부 구현만 자유롭게 수정할 수 있다.

(프로토콜을 사용하는 이유를 생각해 보면 좀 더 잘 와닿을 것이다.)


NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

Swift에서는 ARC(자동으로 참조 카운트에 따라 메모리 해제_자바에서 GC)가 동작하지만,

Objective - C에서는 ARC가 이후에 도입되었기 때문에 만약 활성화가 되지 않았다면 수동으로 메모리를 관리해줘야 한다.

NSAutoreleasePool은 옵젝씨에서 메모리 관리를 돕는 객체로, 참조 카운팅 방식을 사용한다.

내친김에 옵젝씨 메모리 관련 메서드를 알아보자.

alloc

allocation. 객체를 위한 메모리를 할당하고 해당 객체에 대한 포인터를 반환한다.

객체의 메모리를 초기화하지는 않기 때문에 주로 init과 함께 쓴다.

MyClass *myObject = [MyClass alloc];

 

(C에서 malloc을 통해 동적 할당을 하고 포인터 반환 null체크를 하는 것과 동일한 듯)

 

init

alloc으로 할당된 메모리를 초기화한다.

MyClass *myObject = [MyClass alloc init];

retain

객체의 참조 카운트를 1 증가시킨다.

참조카운트가 0이 되면 메모리에서 해제되기 때문에, 객체를 유지시켜야 하는 경우 사용한다.

release

객체의 참조 카운트를 1 감소시킨다.

참조카운트가 0이 되면 메모리에서 해제된다.(dealloc 메서드가 호출됨)

이미 0인데 또 release 하면 에러남(c에서 free 한 거 또 free 하면 에러 나는 것처럼)

autorelease

객체를 autorelease pool에 추가한다. 

자동 해제 풀은 객체들이 일시적으로 등록될 수 있는 특별한 구조로,

해제될 때 가지 메모리에 일시적으로 유지되는 것을 보장할 수 있다.

 

예시코드에서 로그만 찍는데 NSAutoreleasePool을 생성하고 drain 해주는 이유는,

옵젝씨에서 NSAutoreleasePool은 메모리 관리를 위해 필수적인 요소로, 

대부분 메인에서 생성하고 프로그램 종료 전에 drain을 하기 때문이다. 

drain

(pool에서 물을 빼다!) 

NSAutoreleasePool 객체를 해제하고, 풀에 있는 모든 객체에 대해 release를 호출한다.

pool에 있는 모든 객체의 참조카운트를 0으로 만드는 것이 아니다. 다 1씩 감소한다..

클로저 내부에서 생성하고 끝에 [drain] 등을 통해 활용하는 것으로 보이는데, 이건 써봐야 알 것 같다.

참조 카운트가 1인데, 0이 되어야 할 autorelease객체들을 모아두고 drain을 하면 전부 카운트가 0이 돼서(release 메시지를 보내서) 해제가 되긴 하겠다. 

 

*ps

위 예시 코드처럼 포인터를 따로 만드는 경우가 아니라,

@autoreleasepool { ... }와 같이 코드블럭을 활용하면

자동으로 릴리스풀을 만들고 블록 실행이 끝나면 풀을 비워준다고 한다. 

주로 반복문 내부에서 객체를 찍어내는 경우 메모리 관리를 위해 자주 활용하는듯!

int main(void) {
     @autoreleasepool {
     //신나는 코딩~~
     }
     //블럭 실행 종료 후 릴리스 풀 자동 해제
}

NSLog

디버깅용 로그 찍기.

컴파일러가 NSLog를 따로 제외해서 배포 환경에서는 메세지가 출력되지 않는다는데 확인해봐야 하는 듯.

문자열을 찍을 때 앞에 @을 붙이는 것은 NSString객체를 만들라는 것이다. 

그냥 문자열을 넣으면 C스타일의 char*문자열이 되는데, 옵젝씨에서는 NSString 사용이 권장된다고 한다.

NSString *name = @"Alice";
int age = 25;
NSLog(@"Name: %@, Age: %d", name, age);

 

위와 같이 문자열 포메팅도 가능하다


return 0

c기반 프로그램에서 main 함수가 성공적으로 종료되면 0을 반환한다.

0이 아닌 값이면 뭔가 잘못된 것이다..


 

겨우 hello world만 찍는 건데도 알아야 되는 개념이 굉장히 많다.