이해를 돕기 위한 Objective-C 문법 (Ch.1)


부서에서 새로 iphone application 개발연구회를 시작하게 되었습니다. 얼마전까진 android에 대해서 공부하고 있었는데, 원래 iphone app 때문에 맥북을 구매한게 있어서 다시 처음으로 돌아갔지요. Android는 약간 맛만 본 수준이지만 언제든지 다시 돌아가도 된다는 생각이 있었고, 맥북을 산지 6개월이 되었는데 별 다른 성과가 없어서 그게 제일 아쉬운 상태였습니다. 다른 분들과 협의하면서 책을 한 권 더 구매하긴 했지만 (이번 책까지 무려 3권이라는…) 책에 돈 들이는건 전혀 아깝지 않다고 생각하기 때문에 문제 없십니다. 저는 objective c를 약간 (아주 약간) 공부했지만 다른 분들은 대부분 처음이고 objective c를 연구회에서 다루지 않을 예정이라 다른 분들이 교재를 이해하기 좋을 정도로만 문법적인 내용을 정리할 예정입니다. 또 제가 막내라…투철한 서비스 정신 :)


요 책 샀습니다. 번역서로 출간된지 그리 오래되지 않아서 ipad 란 단어도 찾아볼 수 있네요. SDK 3.1.3을 기반으로 설명되어 있는 듯 합니다. Head First 시리즈가 갖고 있는 공통점인 대화형 기술. 아주 즐겁습니다. ㅎㅎ 주인공도 등장하고 말도 안되는 배우들 사진 붙여넣고…


아래는 Ch.1 에서 등장하는 예제에 대해 필요한 설명입니다. 저도 모르는 부분은 뺐습니다. @property 같은거요. 책에서는 나중에 설명한다고 되어있으니 저도 나중에…



#import <UIKit/UIKit.h>

C/C++ 에서 사용하는 #include와 동일


.m / .h 파일

.h는 헤더파일. 구현은 .m 파일에 작성


@interface / @implementation

Class를 만들고 싶다면 선언은 @interface에 하면 되고 (보통 헤더파일에), @implementation으로 구현부를 시작한다. 두가지 모두 끝 부분에 @end를 추가해 주어야 함.


예제 이해하기

@interface

iDecideViewController : UIViewController    //UIViewController를 상속받는 iDecideViewController interface

{

IBOutlet UILabel *decisionText;    //멤버변수는 중괄호 안에 선언

}

- (IBAction)buttonPressed : (id)sender;    //멤버함수 선언은 ‘-’ 로 시작. 상세내용은 아래

@end    // interface 종료


@implementation

iDecideViewController    //iDecideViewController 구현

- (IBAction)buttonPressed : (id)sender

{

decisionText.text = @”Go for it!”;    //buttonPressed가 수행할 내용

}


위 예제에 대한 추가설명

- (IBAction)buttonPressed : (id)sender;

return type은 괄호 안에 표시 여기서는 IBAction 임을 의미 . (id)sender는 parameter이며 type은 id, 이름은 sender. id type은 객체의 instance를 의미하는데 C++에서 가장 상위클래스이면서 특정 type으로 결정되지 않아 범용으로 사용하는 Object 와 유사.



Objective-C #03 OOP


Indirection에 이어 OOP에 대한 부분을 공부했다. Objective C 자체가 객체지향 때문에 C에서 small talk를 차용했다고 알려져 있기 때문에 얼마나 효율적인가가 궁금하긴 했다. 일단 C++ 과 비교했을 때 비슷하다는 느낌도 들었지만 가독성 면에서는 더 낫다는 생각이다. 또 java와 비슷하게 interface와 implementation 부분을 구분하기 좋다는 것도 장점이지 않을까 싶다. 내용 정리하면서 사용했던 예제는 보통 C++이나 java에서 OOP 설명에 많이 사용하는 도형 예제이다. (Circle, Rectangle, Triangle 을 처음엔 구조체로 작성했다가 클래스로 바꾸고, 나중엔 상속 개념을 도입하면서 추상화 클래스인 Shape 라는 걸 만들면서 refactoring 하는 그런…)


1. 용어

다른 언어에서는 볼 수 없었던 조금은 생소한 용어를 아래에 따로 정리했다. 사실 메세지의 경우엔 Objectvie C 코드에서 가장 특이하다고 생각했던 문법에 관련된 내용으로 이 부분만 책에서 봤는데도 Objective C 예제 코드들을 이해하기가 쉬웠다.

Message : Objective C에서 객체가 수행하는 액션.

Method dispatcher : Objective C의 코드에서 메세지가 보내지면 method dispatcher 가 메소드를 찾는다. 가장 먼저 해당 클래스에서 찾고, 없으면 상위 클래스로 올라가면서 찾는 방식을 취하며 찾아도 없으면 오류를 내보낸다.


2. Message

아래의 그림을 살펴보면,

이상한 부분이 하나 눈에 띈다.

[shape draw];

이게 뭔지 도대체 알 수 없었다. 배열도 아니고…Objective C 샘플들을 보면 대괄호로 묶인 저런 문장들이 자주 보인다. 메세지를 보낸다는 의미로 해석하면 된다고 하는데, 책 내용 그대로 보면 shape 라는 객체에 draw라는 메세지를 보낸다는 의미라고 한다. 근데 왠지 뭔가 뒤죽박죽 이상해서 내 나름대로 편하게 해석하기로 했다. shape에게 draw 라는 명령을 내리는 거라고 생각하면 훨씬 편한 듯 하다. 뭐 그게 그거일 수도 있다…ㅎㅎ

다시 정리하면 shape라는 객체가 존재하는데 그 객체는 멤버함수로 draw를 가지고 있어야 하고 그 draw를 호출하는 내용이라고 볼 수 있다. 그런데 그건 그렇다고 하더라도 또 이상한게 보인다. “id”. 객체를 가리키는 포인터로 이해하면 쉽다. 난잡하게 생긴 포인터 대신 깔끔하게 id 라는 타입을 사용하는 듯 하다. 그러니까 [shape draw]의 shape가 id 타입의 변수이지만 어차피 객체를 가리키기 때문에 shape가 가리키고 있는 객체에게 draw 를 지시하는 내용.


헷갈릴 수 있으니 또 다른 예문을 보자.

첫번째 예제와 같은 프로젝트에 사용한 main 함수의 내용이다. 첫번째 예제와 비슷한 부분이 몇 군데 보인다. 일단 재미있는 부분은 shapes[0] = [Circle new]; 문장인데 처음에 다루었던 내용을 보면 같다. 단지 Circle 이라는 클래스에 new라는 메세지를 보내고 있을 뿐이다. 한마디로 Circle 객체를 새로 만들라는 의미.

두번째로, 비슷하지만 좀 이상한 부분이 보인다.

[shape[0] setBounds: rect0];

shape[0]는 객체를 가리키는 id 배열 중 하나이니 어차피 객체를 가리키는 거라 생각하면 되지만 뒷 부분의 콜론 좌우는 좀 어색하게 느껴진다. 알고보니 간단했는데, setBounds는 메세지. 그러니까 멤버함수인거고 콜론 이후의 rect0는 setBounds에 전달하고 싶은 parameter. 끝. 이상한거 없음.


3. Interface / Implementation

첫 부분에 언급했던 것처럼 Objective C에서는 interface 와 implementation이 구분하기 좋게 문법적으로 구성되어 있다. 아래는 상속개념까지 적용해서 refactoring 까지 마친 상태의 도형의 최상위 클래스 Shape이다.

클래스를 interface와 implementation으로 구분해 놓은 내용이고, @interface로 시작해 @end로 끝맺음을 하는 형태를 갖추어야 한다. Shape 옆의 콜론은 상속을 받는 의미로 해석하면 편할 듯 하고 extends 정도로 해석하면 되지 않을까? (나만의 생각) 그러니까 Shape는 NSObject (C++에서 Object 클래스 같은 최상위 클래스)를 상속받고 그 아래의 중괄호로 싼 내용은 멤버변수라고 생각하면 된다. 특이한 부분은 ‘-’ 로 시작하는 3줄. 직관적으로 멤버함수를 의미한다는게 느껴진다. (void) 는 리턴타입을 의미하며, 위에 있는 main 함수 예문을 참고해서 보면 setFillColor 라는 method는 ShapeColor 타입의 fillColor를 인자로 받는 method 라는 걸 명시해주고 있다.

구현부인 implementation도 @로 시작해 @end로 끝맺음한다. Interface 부분과 동일한 형태로 method를 나열해 중괄호로 묶어 구현내용을 넣어두었다. 마지막에 있는 draw는 Shape를 상속받을 Circle, Rectangle 등의 sub class에서 overriding 해서 쓸 거라 빈 내용으로 두었다.


4. Inheritance

상속이다. OOP에서 가장 중요한 개념 중 하나. 역시나 예제 위주로 보는게 편하다. 아래의 그림.

Shape를 상속받는 Circle과 Rectangle 클래스의 interface와 implementation 부분이다. 대충보면 대부분 이해가 되는데 하나 이상한게 interface 안에 내용이 없다는 사실. 두 클래스 모두 Shape의 멤버변수와 멤버함수 이외에 추가할게 없는 경우엔 저렇게 빈 공간으로 두어도 무방하다. implementation 부분을 보면 Shape의 draw를 overriding 해서 쓰려고 재정의해 두었다. 두 클래스의 draw의 차이점은 NSLog에 들어가는 circle과 rect 문자열의 차이. ㅎㅎ


초반에 용어정리해 두었던 method dispatcher를 생각해보면, method dispatcher는 내가 사용한 예제에서 draw를 호출할 때 각 클래스(Circle, Rectangle)에 있는 draw를 먼저 호출하려 할거고 만약 재정의되어 있지 않다면 상위의 Shape로 올라가 draw를 찾아서 있다면 Shape의 draw를 호출하게 만든다.



Objective-C #02 Indirection


책을 보고나서 정리한다는게 생각처럼 쉽지 않다. 특히 예제가 많거나 할 때는, 가장 좋은 방법이 직접 해보는거라 다 넣고 싶지만 너무 길어지게 되어서 고민된다. 그래서 이후로는 그냥 팁이나 문법적인 내용만 간략하게 정리할 생각이다. 어차피 대부분의 Objective-C 책들이 C 문법을 어느정도 알고 있다는걸 가정하고 쓰여졌으니까.


1. Indirection

책에선 간접적인 방법에 의한 참조를 indirection이라고 표현하고 있다. 코드에 직접 값을 입력하거나 하는 방법이 아닌 포인터 등을 이용해 접근하는 방법을 의미한다. 예를 들어 for 문을 돌리는데 원하는 반복횟수를 직접 숫자로 입력하는게 가장 간편한 방법이 되겠지만 이런 방법을 아무 생각없이 사용하게 되면 이후의 유지보수나 확장성 측면에서는 별로 좋지 않다. 그래서 함수 상단에 반복횟수를 저장하는 변수를 하나(예를 들어 흔히쓰는 count나 cnt 등으로) 두고 그 변수를 for문이나 함수 내의 다른 곳에서 사용하게 되면 수정할 필요가 있을 때, for 문이나 함수 전체를 훑어보고 수정할 필요 없이 저장된 변수의 값만 하나 살짝 바꿔주면 된다. 책에선 이런 식의 접근 방법을 indirection 이라하고 객체지향의 의미를 설명하게 전에 언급해 두었다. 실제 예는 아래에.

위의 그림은 word.txt 파일에서 문자열을 한줄씩 읽어 문자열과 함께 문자열의 길이를 출력해 주는 예이다. 이걸 indirection의 의미를 이용해 어떻게 바꿀 수 있을까? 파일명을 코드에 넣지 않고 인자로 입력받는 방법을 쓴다면 여기서 좀 더 나아간 코드가 될 것 같다는 느낌이 든다. 물론 위의 예제 자체도 indirection을 약간 사용한 내용이다. 파일에서 읽어오는 것이 아니라 문자열을 직접 코드 안에 넣었다면 유지보수나 확장이 더 힘든 코드가 될 것이다.(물론 때에 따라 코드 안에 직접 문자열을 넣어두는게 좋을 때도 있지만…) 어찌되었든 위의 코드를 조금 더 개선해보면 아래와 같이 된다.

main 함수 상단만 변경했다. 먼저 인자의 갯수를 판단해서 파일 이름을 넣어두었는지 판단한 후에 해당 인자를 파일 이름으로 해서 fopen을 하는 내용으로 변경되었는데, 책과 거의 비슷하게 만들어 보니 오류가 있다. 해당 파일이 없을 경우를 고려해서 파일이 없을 땐 아무 작업도 하지 않고 종료되게 해야 하는데 위의 예제는 fopen에 실패해도 그냥 fgets를 호출해 파일포인터를 사용하게 된다. 분명 잘못된 파일이면 run time error 가 발생할게 뻔하다. 하지만 어차피 indirection 을 설명하기 위한거니까 이해해 주시길. 이렇게 변경하면 최초의 예제보다는 좀 더 간편한 코드가 만들어진다.

 

2. XCode에서 argument 설정하기

두번째 예제를 만들어보면서 XCode에서 argument는 어떻게 설정하는지 알게 되었다. 바로 다음의 그림 참조.

코드가 보이는 편집기 말고 XCode 화면을 보면 왼쪽에 Executables가 보이고 화살표를 눌러 하위 내용을 보면 코드가 들어있는 m 파일이 빌드된 바이너리가 보인다. 그 바이너리를 더블클릭하면 아래와 같은 창이 열리면서 다양한 설정메뉴들이 나타난다.

상위의 탭에서 두번째에 위치한 Arguments를 클릭하면 처음엔 비어있는데 가운데쯤에 있는 + 버튼을 누른 후에 인자로 넣고 싶은 내용을 추가해주면 끝난다. 나의 경우엔 문자열이 들어있는 텍스트 파일이 빌드된 실행파일보다 두 단계 상위 디렉토리에 있었기 때문에 상대경로로 지정해 두었다. 여기까지 입력한 후에 콘솔창을 띄우고 Run 해보면 끝. 물론 txt 파일이 정상이어야 하고 인자로 입력한 위치/파일명과 동일한 파일로 이미 만들어져 있어야…^^

 

 

Objective-C #01


특이한 문법 때문에 Objective C 책을 한 권 샀다. 시간날 때 마다 공부해서 정리할 생각. 책은 Apress에서 발간한 원서를 BJ 퍼블릭이란 곳에서 번역한 출판한 “(아이폰과 맥 OS X 개발을 위한) Objective C” 이다. (원래 서명은 굵은 꺽쇠(?)로 감싸줘야 한다고 배웠었는데, 맥북이란게 한글 친화적이지 못한 부분이 조금 있어서 어쩔 수 없이 큰 따옴표를…)



1. Hello World

누구나 언어를 처음 배우면 처음 해보는게 Hello World 출력하는 프로그램이다. 왜냐하면, 1.간단하고, 2.사용할 IDE의 기본을 후딱 맛보기 좋으며, 3.여러가지 이유로 각종 책들이 젤 첫 예제로 Hello World를 사용하기 때문이다. 아무것도 모르는 우리들이야 어쩔 수 있나 무작정 따라할 수 밖에.


IDE로는 OS X 설치된 맥만 있으면 무료로 다운받을 수 있는 Xcode. 무조건 실행해보면, Visual Studio나 Eclipse 등의 IDE들 처럼 프로젝트를 새로 만들거냐 라는 물음의 메뉴가 최상위. 기타 등등은 아래에 배치되어 있다. 새 프로젝트 만들겠다고 선택한 후

위 그림과 같은 화면이 나오면 왼쪽 메뉴에서 Application, 오른쪽에서 Command Line Tool, 중앙의 Type을 Foundation으로 선택한뒤 Choose 버튼을 꾹 누른다. 그리고 적당한 프로젝트 이름과 위치를 지정해주면 끝. Xcode가 뚝딱뚝딱 뭔가 처리하더니 아래와 비슷한 화면이 보이게 된다.

난 HelloWorld 라고 프로젝트 이름을 지정했더니 HelloWorld.m 파일이 보인다. 그게 Objective-C 소스가 들어갈 파일이다. 나머지 파일에 대해선 천천히 공부할 예정. 책에서 언젠간 언급이 되겠지. (난 시간이 없다. ㅎㅎ 언급이 안되더라도 언젠간 알아야 할 시점이 올거다. 아마도…) 더블클릭해서 원래 Xcode가 만들어준 소스를 지우고 아래처럼 입력해본다.

C와는 다르게 #import 라는게 있지만, 생김새보니 딱 #include 역할이다. stdio.h 대신 Foundation.h 가 쓰인다고 생각하면 될 듯 하다. 참고로 Foundation.h 파일의 위치는 /System/Library/Frameworks/Foundation.framework/Headers/ 이다. 분명 나중에 해당 헤더들을 한번쯤은 열어볼 필요가 생길 것 같다. C처럼 생각한다면…


헤더파일 import를 제외하고는 어색한 부분이 딱 하나 보인다.

NSLog(@”Hello World”);

NS는 스티브 잡스가 애플에서 쫓겨났을 때 만들었던 NeXT 라는 회사에서 만든 NextSTEP의 약자라고 하는데, 저 앞부분만 제외하고 보면 Log니까 콘솔에 글자를 찍어주는 함수일거란 느낌이 든다. 근데 @는 뭘까? @는 뒤에 오는 문자열이 NSString 형태라는 알려주는 기호라고 한다. 그러니까 NSLog는 C의 printf와 비슷한데, printf에서처럼 일반 문자열을 사용하는게 아니라 NSString type을 사용하기 때문에 @를 넣어준다고 생각해주면 쉽다. printf 보다 편리한 점은, 뿌려주고 싶은 문자열 마지막에 개행문자(\n, \r\n)를 넣지 않아도 된다는 점. 빌드해보면 아래와 같은 결과를 얻을 수 있다. (Build + Run 은 Command + R이고, 이전에 콘솔 창을 먼저 열어줘야 하는데 단축키는 Command + Shift + R)

굵은 글씨로 Hello World 라고 떡하니 찍혀있다. 어찌보면 당연한건데 항상 처음하는건 신기하고 기쁘다.



2. %@, BOOL

좀 더 예제를 다뤄보기로 하자. 역시 전부 다 지우고 이리저리 입력해 보았다. 아래의 그림.

그냥 단순히 숫자 비교하는건데, 속도를 위해 main 함수를 보면 아래와 같은 구문이 있다.

NSLog(@”%d and %d are same, right? %@”, 3, 5, boolToString(areSame(3, 5)));

printf 처럼 형식지정자가 비슷하다. int type 받을거니까 %d 사용했는데, %@ 라는 것도 있다. 직관적으로 NSString type을 받을 것임을 느낄 수 있다. 간단하다. 특이한 것 하나는 YES, NO 같은게 있다는건데 이건 뭘까? 바로 BOOL type 이다.

 

C에서도 bool type 이 있고 true, false 가 있으니 마찬가지라고 생각하면 된다. 보통 C/C++ 로 개발하다 보면, 편의성이나 type들이 꼬이는걸 막기 위해 개발자들이 별도로 사용하기 위해 type을 재정의하는 경우가 있는데 Objective C도 마찬가지인 듯 보인다. 그냥 쓰기 편하게 unsigned char를 재정의 한 것이고 각각 1을 YES, 0을 NO 라는 키워드로 정의해 두었다. (1바이트 이상의 값을 BOOL 변수에 대입하면 하위 바이트 값만 저장되므로 유의)

 

며칠전 자기 전에 30분 정도 본 내용인데 정리하려니 시간이 배는 더 걸리는 것 같다. 하지만 이래야 복습이 된다. 간단한 것도 잘 익혀둬야 나중에 삽질하지 않으니 기본은 철저히 익히는게 좋은 듯. 계속 정리할 거다. 꼭…

 

참! 결과 화면. ㅎㅎ 결과를 봐야 재미 있는게 개발인데…빠져선 안된다.