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

오랜만에 정리합니다. 계속 미루고 있다가 책을 마지막까지 다 보고 나서야 정리할 생각을 하게 되었습니다. 했던거 마무리는 해야 하니까요. 이번 chapter 7에서는 새로운 application을 만드는 것으로 시작을 합니다. 수배자에 대한 정보를 listing 해주는 application 인데, chapter 9 까지 이어지네요. chapter 7에서는 tab bar를 사용해서 수배자와 검거된 사람들의 list를 분리하고 그에 필요한 data 관리는 Core Data로 하는 방법을 알려주고 있습니다. Core Data는 다시 별도로 정리를 해야할 것 같습니다. 꽤 복잡해 보여서요. 물론 직접 DB 관리하면서 쿼리문을 코드에 넣는 것보다는 훨씬 낫겠지만요.


Tab bar icon 추가 혹은 변경

Tab bar에 있는 물음표를 선택하던가 직접 구성요소를 다 펼쳐서 Tab bar item을 선택합니다. 그 이후에 오른쪽의 속성 창에서 Identifier 부분을 Custom으로 변경하고 Image에 넣고자 하는 혹은 바꾸고자 하는 image 파일을 선택해주면 됩니다.

위의 그림처럼 설정하면 끝. 간단합니다. :)


Core Data에서 Entity의 attributes 옵션들

Entity를 새로 구성하려고 attribute 들을 추가하고 수정하다보면 몇 개의 옵션들이 있는 것을 볼 수 있습니다. 그 중에서 Transient는 data를 따로 저장할 필요가 없는 임시 attribute 라는 걸 알려주는 용도이며, Indexed는 Core Data가 index 값을 별도로 만들어 빠른 검색을 가능하게 만들 필요가 있을 때 사용합니다.


Core Data의 세가지 요소

Core Data는 세가지 요소로 구성된다고 책에 언급되어 있습니다. 개인적으로는 entity도 하나의 요소가 되는거 아닌가 싶은데 일단 책에서는 그렇게 말하고 있네요. 세가지는 아래와 같습니다.

Managed Object Context : Data를 관리하는 녀석. Entity로 만들어낸 data model 들도 context가 관리합니다.

Persistent Store Coordinator : 아래 store를 관리하는 역할을 수행합니다.

Persistent Object Store : 용어 그대로 store 입니다. 책의 예제에서는 sqlite 파일이 되겠습니다.


NSFetchRequest

Managed Object Context에게 찾고자 하는 객체를 알려주기 위한 용도로 NSFetchRequest 라는 클래스를 사용해야 합니다. NSFetchRequest를 사용할 때 크게 세가지를 결정해줘야 하는데, Entity Info, Predicate, Sort Descriptor 가 그 것입니다. 어떤 entity를 찾을건지 결정해주고(Entity Info), 충족해야 하는 조건 혹은 검색 조건을 만들어줘야 하구요(Predicate). 조건을 충족하는 data가 있을 경우에 정렬시켜서 뽑을 수 있습니다.(Sort Descriptor)


NSSearchPathForDirectoriesInDomains

책에서 계속 simulator를 사용하고 있기 때문에 실제 device와 약간 차이가 나는 부분들이 있습니다. 그 중에 하나가 DB가 존재하는 위치인데, device와 달리 simulator 사용시에는 다른 디렉토리에 저장이 되기 때문에 별도의 method를 사용해서 DB file을 찾아줘야만 합니다. 그 때 사용하는 method가 NSSearchPathForDirectoriesInDomains 입니다. 이 method의 prototype을 살펴보니 지금까지 보아왔던 Objective C의 method와는 많이 다르네요. C, C++, Java 등에서 흔히 사용하는 method와 같은 모습입니다. (아래의 그림, NSSearchPathForDirectoriesInDomains prototype)

NSSearchPathForDirectoriesInDomains method의 문서를 살펴보니 특정 directory, domain을 위한 경로 문자열을 리스트로 넘겨준다고 되어있습니다. NSSearchPathDirectory와 NSSearchPathDomainMask는 각각 enum으로 여러가지 상수들을 포함하고 있습니다. Chapter 7의 예제에서는 각각 NSDocumentDirectory, NSUserDomainMask를 사용했구요.


NSManagedObject의 생성

예제에서는 NSManagedObject를 context에서 가져오는 것으로 설명되어 있습니다만, 별도로 생성하는 방법도 있다고 합니다. 일반적인 클래스 객체의 생성처럼 alloc, init만 해서 생성하는 것은 안되고 아래처럼 entity에 대한 정보도 필요하다고 되어있네요.

[NSEntityDescription insertNewObjectForEntityForName:@"Fugitive"

inManagedObjectContext:managedObjectContext];


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


Ch.4 부터는 독특한 Objective C 문법이 나오질 않네요. 객체에 메세지 보내는 방법, property, protocol 등만 익숙하면 크게 어려울게 없는 듯 해서 중요해 보이는 부분이나 이상했던 부분 위주로 정리해볼까 합니다.


UITableViewController

이전 chapter 들에서 picker 라는게 나오면서 DataSource와 Delegate를 protocol로 지정해 사용했었는데, Chapter 4에 사용되는 UITableViewController는 DataSource와 Delegate를 이미 protocol로 사용하고 있기 때문에 개발자가 별도로 선언할 필요가 없다고 합니다. 실제로 UITableViewController의 정의 부분을 찾아보니 아래처럼 protocol로 선언되어 있네요.

그래서인지 이미 UITableViewController가 구현해야만 하는 method 들이 이미 만들어져 있습니다.


NSMutableArray

NSMutableArray를 사용하는 부분이 있는데 초기화를 개발자가 원하는 문자열을 사용해 초기화 하는 것으로 예제들이 되어 있습니다. 물론 다른 방법(method)을 사용해도 무방하겠지만요. 재밌다고 생각한건 초기화 할 때 일련의 문자열 마지막에 nil이 들어있다는 사실입니다. 이전 chapter 들에서도 비슷한 부분들이 있었는데, C / C++ 과 비슷하게 문자열 마지막의 nil은 문자열의 마지막임을 알려주는 용도로 사용된다고 합니다. 물론 배열 안에 추가되는 것은 아니구요.


NSBundle

이번 chapter에서 독특한 클래스가 등장합니다. NSBundle 이라는. Property list를 DB 대신 이용하면서 property list의 경로를 찾기 위해 사용하고 있는데, 문서들을 찾아보니 프로그램에서 사용되는 resource나 code 들을 그룹핑한 파일시스템에서의 위치를 표현한다고 하네요. Cocoa에서 몇개의 파일들과 디렉토리들을 묶음으로 관리하는데 그 묶음을 bundle이라고 한다고 합니다. 자세한 내용은 더 찾아봐야 하겠지만 일단은 우리가 작성하는 application에 존재하는 파일이나 resource 들을 읽어올 때 사용해야 한다고 생각하면 편할 듯 합니다. 나중엔 음원이나 영상들을 얻어와서 읽어들일 때도 사용가능할 듯 싶네요.


UIViewController subclass 생성시 유의사항

이번에 사용되는 예제에서 detail view를 위해 UIViewController subclass를 생성해서 작성하게 됩니다. 이 때 책에 표시가 되어있는데 저는 깜빡하고 넘어갔던 부분이 “with XIB for user interface” 옵션인데요. 이걸 체크하지 않으면 h와 m 파일만 생성됩니다. 전 xib 파일을 따로 생성해서 작업을 했었는데, 어찌된 일인지 나중에 run 해보면 빌드는 정상인데 run time error가 발생하더군요. xib 파일을 따로 인식하지 못하는 모양이었습니다. 분명 별도로 생성한 xib 파일을 나중에 엮어주는 방법이 있을텐데 한참이나 고생하다가 옵션 체크해서 새로 작성했던 기억이 있습니다. 저처럼 고생하는 일이 없기를… 그래도 이런 실수들 한거 전 좋습니다. 알아낸게 있으니까요. :)


Debugger와 Console 단축키

단축키는 여러모로 편리합니다. 머릿속에 각인시켜 두는게 힘들긴 하지만요. 이번 chapter에서 debugging에 대한 맛보기가 등장하는데 따로 debugger 윈도우나 console이 올라오지 않으면 아래의 단축키를 사용하세요.

Debugger : Command + Shift + Y

Console : Command + Shift + R



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


이번주에 연구회에서 책 Ch.3에 대한 내용을 발표하게 되어서 Ch.2 부터 정리했습니다. 원래 지난주에 Ch.3까지 다 정리하긴 했는데 그게 머릿속으로만 정리한거라 ㅎㅎ. Ch.2와 3이 거의 연결되는 내용이라 따로 분리할 필요는 없을거 같긴 한데 그래도…


Protocol

@interface InstaTwitViewController : UIViewController

<UIPickerViewDataSource, UIPickerViewDelegate> {

}

위의 예문에서 보면 특이하게 <> 기호로 감싼 부분이 있는데, UIPickerViewDataSource와 UIPickerViewDelegate 라는 protocol을 사용하겠다는 의미입니다. Protocol이란 개념이 Objective C 2.0 부터 포함되어 있는거 같은데(?), 다른 언어들만 접해본 입장에서는 생소한 개념입니다. 근데 뭐 간단히 생각해보면 일반적인 protocol을 보통은 ‘어떤 시스템에서 미리 약속해 놓은 규약’ 정도의 의미로 사용하고 있으니 비슷하게 이해하고 넘어가면 되지 않을까 싶네요. Objective C의 특징과 연결해서 생각해보면 Objective C가 다중상속을 금지하고 있기 때문에 보완하고자 만들어진 개념이 아닐까 생각됩니다. 전 Java에서 Interface와 유사한 녀석이라 생각하기로 했습니다.


alloc & init

activities = [ [NSArray alloc] initWithObjects : @”abc”, @”def”, nil];

Objective C에서는 new 대신 alloc이란 method (message)로 새로운 객체 생성을 하는 듯 합니다. 보통 class 들의 멤버들을 열어보면 보통 초기화 method들이 제공되는 듯 한데 초기화도 방법에 따라 다양한 녀석들이 있습니다. 위의 경우엔 (initWithObjects) 다른 객체들로 초기화하는 내용을 표현하고 있습니다. 위에서는 NSString type의 객체들이네요. 그리고 마지막은 nil (=null)을 넣어두고 있습니다. Objective C에서 제공하는 method 들은 대부분 이름만 봐도 대충 의미가 파악된다는게 참 좋은 점이라고 생각합니다. 근데 쓰면서 보니 마지막엔 꼭 nil을 넣어주어야 하는 것인지 갑자기 궁금해지네요. 이 내용은 확인해서 추가해야겠어요. 참 alloc으로 새로 할당한 녀석들은 꼭 나중에 release 해야 합니다.


참으로 헷갈렸던 method 이름

- (NSInteger)pickerView : (UIPickerView *)pickerView numberOfRowsInComponent : (NSInteger)component {

….

}

이거 전체가 method의 prototype입니다. 참 이상하고 헷갈리고, 책을 당장이라도 덮고 싶게 만드는 것들 중 하나입니다. 근데 한 번 이해하니 오히려 편한 부분도 있습니다. 그냥 보면 굳이 parameter로 무엇을 전달해야 하는지 reference를 찾아볼 필요가 없어요. :) 그래서 제 나름대로 이해하기 쉽게 배열하기로 했습니다. 그럼 더 편하거든요. 위의 method의 정확한 이름은 이렇게 표현할 수 있습니다.

(NSInteger)pickerView : numberOfRowsInComponent

pickerView만 이름인게 아니라 numberOfRowsInComponent까지 이름이 되는거죠. 이렇게 이해하면 됩니다. 원래 pickerView라는 녀석인데 numberOfRowsInComponent에 필요한 parameter를 하나 더 받을거다 라고요. 그래서 나름 쉽게 풀어서 다시 쓰면 아래와 같습니다.

(NSInteger)pickerView : (UIPickerView *)pickerView

numberOfRowsInComponent : (NSInteger)component {

….

}

다시 쉽게 해석해보면 첫번째 parameter로 UIPickerView의 pointer type인 pickerView를 가져야 하고, numberOfRowsInComponent라는 parameter가 필요한데, NSInteger type의 component란 이름으로 parameter를 받을거란 얘기입니다. 책에선 외부 parameter, 내부 parameter란 이름으로 얘기하고 있는데 공식적으론 numberOfRowsInComponent란 parameter가 필요하고 실제로 component란 이름의 parameter를 넘긴다고 이해하면 됩니다. 그러므로 method 내부에선 component를 가지고 처리하면 되겠습니다. numberOfRowsInComponent를 보면서 가만히 생각해보면 이게 어떤 component 안에 있는 row의 갯수를 어떻게 하겠다는 얘기니 필요한 component를 parameter로 받겠다는 걸 알 수 있습니다. 특이하게 보통 이런 경우 number of rows를 return하게 되더군요. 실제로 이 method는 특정 component를 parameter로 받아서 그 component의 row 갯수를 NSInteger type으로 return하도록 작성되어 있습니다. 설명이 좀 복잡해진 듯 하지만, 3개 이상의 parameter 들도 이런 식으로 작성되기 때문에 이런 형태에 대해 익숙해져야 할 듯 합니다.


3개의 parameter를 사용하는 예

- (NSString *)pickerView : (UIPickerView *)pickerView

titleForRow : (NSInteger) row

forComponent : (NSInteger)component {

….

}

위에서 설명한 것과 비슷한 방법으로 해석해보면 제일 끝에서부터, component를 받을거고 row도 필요하고 NSString * type의 값을 return 할거라는 걸 알 수 있고, 더 나아가면 특정 component를 받아서 component 별로 row를 입력받을 생각이고 결국은 해당 row의 title을 return 하겠다는 걸 추측해볼 수 있습니다.



이해를 돕기 위한 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 와 유사.