개발/Objective-C
오브젝의 시작 - 맥부기 펌
강아지아빠
2010. 11. 10. 14:42
기본적으로 오브젝은
앱개발 도중 나오는 데로 정리할 생각이다.
일단은 개념부터 조금 정리해야할 것 같아.
맥부기 까페(http://cafe.naver.com/mcbugi) 의 강의 몇개를 정리해두겠다.
copy & paste가 아닌 순수 타자로 개인적 정리를 포함하여 수정하겠다.
개인 공부용이니 문제가 될 시 삭제 또는 비공개 전환할 것이고
원문은 맥부기 카페(http://cafe.naver.com/mcbugi) 를 이용하기 바람.
지금부터 시작
오브씨 시작...
상관없지만 오브젝씨는 Brad Cox라는 사람이 만들었다.
*코딩 기본규칙
1. 클래스는 대문자로 시작한다.
2. 인스턴스 변수는 소문자로 시작한다. private 할 경우 _xxx, __xxx 를 허용한다.
(혹시 모를까 private란 : 상속을 금지하고 오버라이딩도 안됌. 객체 접근도 불가 보통 내부 클래스 접근 지정자를 정의함 - 해당 클래스에서만 수행 결국 데이터가 변환되거나 고쳐져서는 안될 변수에 선언하고 오버라이딩 또한 안되고 다른 클래스에서는 쓰지 못하도록 상속 금지를 할때 사용)
3. 클래스 메소드이건 인스턴스 메소드 이건 상관없이 소문자로 시작.
(참고정리 :
클래스 메소드는 당연하겠지만 클래스가 수행하는 메소드
인스턴스 메소드는 인스턴스가 수행하는 메소드
오브씨에서는 C++과 달리 SmallTalk의 영향으로 클래스도 하나의 객체(인스턴스)로 취급된다.
고로 쉽게 말해 유틸클래스라고 생각하면 편하다 인스턴스를 생성하지 않고
바로 사용할 수 있는 메서드 정도 .. [클래스 매서드];
인스턴스 메서드는 인스턴스를 생성해서 사용 [인스턴스변수 매서드];
기본적으로 인스턴스 메소드는 '-'로
클래스 메소드는 '+'로 표시한다.
"*참고에 참고 SmallTalk란?
객체지향(Object-Oriented) 프로그래밍 언어
간단히 말해 모든 사물이나 개념을 '객체', 쉽게 말하면 하나의 '물건'이나 '물체'로 취급 "
)
4. 캐멀 표기법을 따른다.
(참고정리 : 각 표기법
1. 캐멀 표기법 (Camel case)
식별자의 첫자는 소문자로 시작하여, 첫 단어 이외의 나머지 단어는 대문자로 시박한다.
예) getText
2. 파스칼 표기법 (Pascal case)
식별자의 각 단어는 대문자로 시작하여 나열한다. 캐멀 표기법과 유사하지만
첫 글자도 대문자로 시작한다.
예) ContntPopup
3. 모든 대문자표기법 (UpperCase)
식별자의 모든 문자를 대문자로 표기한다.
통상적으로 두개 이상의 문자로 구성하는 경우에 사용한다.
예) CBT,TEL,IO
4. 헝가리안 표기법 (Hungarian Notation)
각 식별자의 데이터 형식을 이름의 앞에 붙인다.
예) strQuery
위의 예의 strQuery 의 데이터 형식은 String 형이란 것을 알 수 있다.
)
* 기본문법
1. 최상위 클래스 - NSObject
NSProxy 도 있지만 우선 무시하자.
자신만의 클래스를 만들 때는 최소한 NSObject 에서 상속을 받아야 한다.
2. 상속, 클래스변수, 연산자 오버로딩
다중상속을 지원하지 않는다.
클래스 변수를 지원하지 않는다.
연산자 오버로딩을 지원하지 않는다.
아규먼트 오버로딩도 지원하지 않는다.
3. id, self, super, nil, @""
id 는 객체를 가르키는 포인터 이다. void * 와 같다.
self 는 자신을 가르키는 포인터 이다. java의 this와 같다.
super 는 현 객체가 상속한 상위 객체이다. java의 super 와 같다.
nil 은 객체가 없음을 나타내는 객체 포인터이다. java의 null 과 같다.
c style의 스트링은 "하하" 처럼 표기하고, 유니코드 스트링은 @"하하"처럼 표기한다.
* 참고로 메소드의 기본 리턴타입은 id이다.
- (id) sayHello;
- sayHello;
위 둘은 같다. 즉, 명시하지 않으면 기본으로 객체 타입을 리턴하는 것으로 간주한다.
하지만 요즘은 첫번째를 많이 사용한다.
4. 객체생성
MyObject *className = [[MyObject alloc] init];
alloc 은 클래스 메소드 이다. init 은 인스턴스 메소드 이다.
alloc 은 메모리를 준비한다. init은 인스턴스 변수를 초기화 한다.
init은 기본 초기화 메소드이다. 이런 초기화 메소드를 만들 때는
반드시 initXXX 로 만들도록 한다. (NSObject 참고)
만약 다른 객체를 생성해서 리턴하는 것이라면
가능한 createXXX로 만들자.
5. 메세지 보내기
오브씨는 "[" 로 시작하고, "]"로 닫고, 문장의 끝은 ";" 로 마무리 한다.
또한 표현을 '객체에 메세지를 보낸다' 라고 한다.
6. 메세지 중첩하여 보내기
어떤 메소드의 결과값(리턴값)이 객체일 경우 메세지를 중첩해서 보낼 수 있다.
즉, myObject 에 draw 라는 메세지를 보냈을 경우,
그 리턴값이 myObject 라면
[[myObject draw] transform];
위 처럼 보낼 수 있다. 3중, 4중 으로도 가능하다.
위의 것은
[myObject draw];
[myObject transform];
과 같다. 생소하지만 익숙해지도록 하자.
7. 메세지 인자
메세지 인자는 ":"로 구별한다.
또 ":" 앞에 좀 더 분명하게 하기 위해 메세지 구별자를 추가할 수도 있다.
[myObject drawWithX:pointX withY:pointY];
[myObject drawWithX:pointX:pointY]; // 위의 것과 같지만 pointY가 무엇인지 알기 힘듬.
[self show]; //자기 자신에게 보내는 메세지
8. import
헤더를 인클루드 할 때는 C의 include 보다는 import를 사용하자,
이중으로 include 되더라도 허용케 한다.
9. 클래스 선언
*."클래스이름.h" 로 파일을 만든다. (클래스 이름과 달라도 상관은 없다.순전히 구별을 위한 편의성)
*.Foundation 이나 UIKit 을 임포트 한다. (모르면 UIKit을 일단 임포트)
*."@interface 클래스네임 : 상위클래스" 로 시작한다.
*. 그 아래 { } 사이에 인스턴스 변수를 선언한다.
*. "+ (리턴형) 클래스 메소드 ;" 로 클래스 메소드를 선언한다.
*. "- (리턴형) 인스턴스 메소드;" 로 인스턴스 메소드를 선언한다.
*. "@end" 로 닫는다.
예문) MyName.h
#import <Foundation/Foundation.h> //또는 #import <UIkit/UIkit.h>
@interface MyName : NSObject
{
NSString *name;
}
+ (MyName*) newInstance; // 또는 + (id) newInstance;
- (id) initWithName:(NSString *)name;
@end
10. 클래스 구현
*. "클래스이름.m" 로 파일을 만든다.
*. "#import 클래스이름.h" 로 클래스 선언을 임포트 한다.
*. "@implementation MyName" 으로 구현을 시작한다.
*. "+ (리턴형) 클래스메소드 {...}" 에서 구현한다.
*. "- (리턴형) 인스턴스메소드 {...}" 에서 구현한다.
예문) Myname.m
#import "MyName.h"
@implementation MyName
+ (MyName*) newInstance
{
//여기에 구현을 한다. 아래는 기본 패턴
return [[MyName alloc] initWithName:nil]autorelease];
}
-(id) initWithName:(NSString *)name
{
//여기에 구현을 한다. 아래는 기본 패턴
if(self=[super init]){
}
return self;
}
@end
여기까지가 기본적인 사항이다.
11. 객체의 메모리 관리 ( alloc, dealloc, release, autorelease)
메모리 관리는 좀 복잡하다.
'생성한자 또는 명시적으로 소유하겠다고 선언한 자가 소거의 책임이 있다"
라고 정의하면 될듯 하다.
이것을 OWNERSHIP 이라고 한다.
가비지 콜렉션을 지원하지 안혹, 개발자가 관리하는 구조이며,
retainCount를 사용하는 메커니즘 이다.
retainCount가 0이면 소거된다.
객체의 생성은 alloc, copy 가 있다. 어떤 객체를 생성/복사하지 않고
소유하고자 할 경우 retain 이란 메세지를 보낸다.
alloc, copy, retain 메세지를 보내면 retainCount가 1씩 증가한다.
그 반대는 release 메소드이다.
이 메세지를 보내면 리테인카운트가 1씩 감소한다.
어떤 객체가 가진 인스턴스 변수를 소거하고자 할 경우엔,
- (void)dealloc; 메소드에서 처리하면 된다.
그리고 반드시 끝날때는 [super dealloc]; 을 해줘야 한다.
-(void)dealloc {
[myName release];
[super dealloc];
}
autorelease 는 릴리즈풀에 넣어두고 소유자가 없을 경우나
풀이 소거될때 자동 소거되도록 한다.
프로그램을 하다보면 소유자가 불분명해지는 경우가 있다.
이 말은 소거를 누가 할지 결정하기 힘들다는 뜻이다.
그럴 때 이 메서드를 많이 사용한다.
클래스 메소드에서 객체를 생성해 리턴해줄 경우를 생각해보자
리턴을 하게 되면 이 객체는 누가 소유할까?
아주 잠시동안이지만 아무도 소유할 수 없는 상황이 발생한다.
이때 소거를 잠시 미루도록 autorelease 를 하여 릴리즈 풀에 담아둔다.
(릴리즈 풀은 애플문서를 참고하자)
몇가지 예제를 보자
-(void) memoryTest{
//인스턴스 변수일 경우
myInstance1 = [[NSString stringWithString:@"Hangule"] retain]; // autorelease 된 객체
myInstance2 = [[NSString alloc] initWithString:@"Hangule"];
//일반 변수일 경우
NSString *str1 = [NSString stringWithString:@"Hangule"];
NSString *str2 = [[NSString alloc] initWithString:@"Hangule"];
NSString *str3 = [NSString stringWithString:@"Test"];
[str2 release];
}
myInstance1 에 autorelease 된 객체입니다.
stringWithString: 으로 생성된 객체는 메소드가 끝나면 소거된다.
따라서 retain 을 해서 소거를 막고, 소유를 한다.
myInstance2 는 alloc 된 객체이다.
리테인카운트 1 이고 인스턴스 변수이므로 객체가 살아있는 동안 남아
있어야 하므로 release 를 하면 안된다.
str1은 autorelease 된 객체이다. 메소드가 끝나면 소거 된다.
str2는 alloc 된 객체이다. 리테인 카운트 1이고, 메소드가 끝나도
소거 되기 위해선 반드시 release 를 해줘야 한다.
myInstance1 에 str3 을 어사인하고자 할 경우엔 어떻게 하는지 보겠다.
[myInstance1 release]; // 릴리즈 카운트 감소. 더이상 own 할 필요가 없으니
이제 해방시켜주자. 안하면 메모리 롹~!
myInstance1 = [str3 retain]; //str3는 지역변수이므로 메소드가 끝나면 사라진다.
리테인해서 소거를 막는다.
여기서 myInstance1 = str3; 하는 것과 myInstance1 = [str3 retain]; 하는 것은
엄청난 차이가 있다.
앞의 것은 포인터가 같아지는 것이고, 뒤의 것은 소유(ownership) 하는 것이기 때문이다.
또 다른 예를 하나 더 보겠다.
NSArray 란 것은 객체 어레이를 담는 콜렉션 중 하나이다.
NSMutableArray 는 가감을 할 수 있고,
NSArray 는 생성된 시점이후에 가감을 할 수 없다.
Mutable 이 붙은 클래스는 동일한 룰이 적용된다.
-(void) someMethod {
NSMutableArray *mArray = [NSMutableArray array];
NSString *str = [NSString stringWithString:@"Hangl"];
[mArray addObject:str];
[mInstanceArray release]; //이전 인스턴스변수는 새로운 값을 소유하기 전에
반드시 릴리즈 시켜야 한다.
myInstanceArray = [mArray retain];
}
이 경우 메소드가 끝나면 str은 없어진다. 그럼 mArray 에는 str이 어떻게 될까?
ownership 에 의해 mArray 는 str을 담고 있어야 하므로
addObject: 할때 str을 retain 하는 것이다.
따라서 메소드가 끝나도 str은 없어지지 않는다.
즉 자기가 쓰겠다고 했으니 retain 을 하는 것이다.
따라서 mArray 가 소거 될 때, 또는 str이 제거될 때 release 가 불리는 것을
유추할 수 있다.
그럼 retain 해서 1 증가하고, release 해서 1 감소하니 결국 소거가 될 수 있는 것이다.
마지막으로 하나더 보자 (초보단계는 아님)
여기 MutableArray *myMutableArray 가 있다.
첫번째 것을 꺼내 마지막으로 넣고자 할때 어떻게 해야 할지 보자
-(void)moveToLast {
id firstObject = [myMutableArray objectAtIndex:0]; //객체 포인터 얻기
[firstObject retain]; // 메모리에서 소거되지 않도록 소유하고 (+1)
[myMutableArray removeObjectAtIndex:0]; // 어레이에서 제거한다.
이때 어레이는 제거되는 객체를 release 한다 (-1)
[myMutableArray addObject:firstObject]; // 어레이 마지막에 추가한다.
이때 어레이는 객체를 retain 한다 (+1)
[firstObject release]; // 이제 필요가 없으니 소유권을 버린다. (-1)
}
원리는 똑같다. 즉 사용할 것이라면 리테인을 하는 것이다.
단순히 제거할 것이 아니라 다시 사용할 것이므로 retain을 해서 잠시 소유 해야 한다.
그리고 소유할 필요가 없으면 release를 한다.
만약 [firstObject retain]; 을 하지 않았다면 위 코드가 문제없이 수행될 수 있을까?
정답은 그럴수도 있고 아닐 수도 있다.
id firstObject = [myMutableArray objectAtIndex:0];
[myMutableArray removeObjectAtIndex:0];
[myMutableArry addObject:firstObject];
즉 이렇게 해도 문제가 없을 수도 있다. 어떤상황일까?
firstObject 객체를 누군가 소유하고(즉, 리테인 카운트가 2이상) 있다면
removeObject를 할때 release를 보내더라도 소거가 되지 않는다.
그래서 이 코드는 상황에 따라 제대로 돌았다가 안돌았다가 하는
알 수 없는 상황이 만들어 진다.
또 release 를 하더라도 바로 소거되지 않을 수도 있기 때문에 ,
설령 다른 객체가 소유하지 않더라도 왔다갔다 하는 상황이 나올 수도 있다.
이런 상황은 소유개념에 대한 이해가 없으면 충분히 발생할 수 있다.
이것이 리테인카운트 메커니즘을 사용하는 메모리 관리시스템의 약점중 하나이다.
어느 곳 하나 삐긋하면 메모리에 구멍이 나 메모리 누수가 연출된다.
메모리 관련해서는 반드시 애플의 문서를 읽어보기 권장.
참고로, 원래 오브씨란 언어에 이런 개념이 있었던 것은 아니다
OPENSTEP 이후부터 이런 개념이 추가되 었다 .
12. SEL 타입, @selector()
오브씨에서 메소드 포인터를 SEL 이라는 타입을 쓴다고 이해하자.
그 SEL 타입을 만들 때, @selector() 라는 것을 사용한다.
SEL method = @selector(drawWithX:pointX);
SEL 은 어떤 객체가 불리워질지를 정의하지 않는다.
요건 오브씨의 런타임을 보면 이해할 수 있는데, 우선은 어떤 객체에
메소드를 어떻게 부르는지만 살펴보자.
[myObject performSelector:@selector(drawWithX)]; // 인자가 없을 경우
[myObject performSelector:@selector(drawWithX:)WithObject:floatObject]; // 인자는 반드시 객체
[myObject performSelector:@selector(drawWithX:) withObject:floatObject afterDelay:0.1f];
// 0.1sec 뒤에 실행
마지막 것은 예전에 없던 것인데 추가된 것이다.
폰의 특성상 약간 늦게 시작해야 빠른 듯 느껴지는 경우가 있다.
그런 상황에 유용할 수 있다.
만약 myObject 에 해당 메소드가 없으면? 당근 에러이다.
그럼 어떤 메소드를 받을 수 있는지는 어떻게 알아낼까?
바로
-(BOOL)responseToSelector:(SEL)aSelector;
를 사용한다.
13. delegate, dataSource
이건 패턴의 하나인데, 구태여 소개하는 것은 아예 delegate 라고 못밖아서
인스턴스 이름으로 쓰기 때문이다.
말 그대로 대행자일 뿐이다. 내용도 간단히 생각하자
어떤 객체가 어떤 일을 하고자 할 때나 끝났을 때 대행자를 가지고 있는 경우
그 대행자가 수행할 수 있도록 메세지를 보내는 것.
(메소드 내에 wil, Did, Should 가 들어가 있다면 대부분 델리게이트 메소드)
내부적인 구현방법을 보고 이해해 보자.
if ( delegate && [delegate resopnseToSelector:@selector(doOther:))
[delegate performSelector:@selector(doOther:)]; // performSelector 메소드가 SEL을 호출.
즉, 객체의 변수로 delegate 라는 객체가 있고, -doOther 라는 메소드를 받을 수 있으면
메세지를 보내는 식이다.
비슷한 것으로 dataSource 도 있다.
마찬가지로 패턴의 하나인데, 직접적으로 dataSource 라고 사용하고 방법도
delegate 와 같다. 테이블, 피커 등에 데이터를 전달할 경우 많이 사용한다.
delegate, dataSource 의 개념에 해당하는 인스턴스 변수를 어떻게 짓던 상관은 없지만,
가급적 맞추는 것이 소스보기와 이해에 편하다.
또 하나, delegate 나 dataSource 를 지정할 때는 그 객체를 retain 하지 않는 것이 보편적이다.
단순하게 delegate = otherObject; 처럼 한다.
이를 weak refernce 한다고 하고 소멸의 책임이 없다.
그러나 delegate = [otherObject retain]; 이렇게 하면 소멸의 책임을 진다.
즉, -dealloc 에서 [delegate release]; 를 해줘야 한다.
14. category
카테고리는 메소드의 묶음이다.
위에서 *.h 에 메소드를 정의하는 방법을 보았다.
그것이 기본 카테고리 이다.
즉, 오브씨는 메소드를 묶음 단위로 표기할 수 있고, 또한 기존의 클래스에 묶음으로
메소드를 더할 수도 있다.
여기서 기존의 클래스에 메소드를 더할 수 있다는 것이 중요하다.
예를 들면,
NSString 이란 클래스는 스트링을 다루는 기본 클래스이다.
이 클래스에 한글이 있는지 없는지를 리턴하는 메소드를 추가하고 싶은 경우,
대부분은 서브클래싱을 생각하게 된다.
그러나, 오브씨는 서브클래싱없이 메소드를 더한다.
방법은 클래스의 생성과 매우 유사하다.
다만 슈퍼클래스 이름 대신 메소드 묶음 이름 (카테고리 이름)을 "(...)" 안에 적는다.
@interface NSString (MyNSStringAddition)
- (BOOL) hasHangel;
@end
이렇게 선언하고, 구현은 interface 대신 implementation 을 사용하면 된다.
그리고 아래처럼 사용하면 된다.
NSString *myString = [[NSString alloc] initWith:@"Hangul 한글"];
BOOL hasHangul = [myString hasHangul];
자바스크립트에서 prototype 으로 기존 클래스에 메소드를 추가하는 것을
본적 있는 분들은 이해가 빠르겠다.
두 개의 룰이 필요하다.
1. 기존의 메소드 이름과 겹치지 말아야 한다는 것과
2. 카테고리명이 유일해야 한다는 것
바로 이 두가지이다.
메소드의 묶음이기 때문에 인스턴스 변수를 가지는 것이 아니다.
따라서 별도의 인스턴스가 추가되는 상황이 아니라면 서브클래싱 하지 않고
메소드만 추가하는 카테고리가 훨씬 합당하다.
또한 메소드를 구조적으로 묶어줌으로써 코드 관리를 용이하게 하는 장점도 있다.
카테고리는 다른 파일로 (별도 헤더파일과 구현파일) 만들어 사용하는 경우가 많고,
구태여 헤더파일에 넣어 API 를 공개시키지 않고 싶을 경우
(즉, 프라이빗하게 사용하고자 할 경우), *.m 파일에서 정의하여 사용하기도 한다.
또 예를 든것처럼 기존 클래스에 메소드가 없는 경우에도 사용한다.
(기존 클래스의 메소드를 완전히 변경할 수도 아예 클래스를 변경할 수도 있다.
메소드스위즐링, 포우져라고 하는 데 크게 신경쓰지 않아도 된다)
15. protocol
프로토콜은 주고 받을 수 있는 메세지를 규약하기 위한 것이다.
자바의 인터페이스와 흡사하다.
프로토콜을 정의하는 방법은 다음과 같다.
@protocol 프로토콜이름 <적용대상 클래스 이름>
예제)
@protocol DoYouLikeMe <NSObject>
@required // 이하 메소드는 반드시 구현해야 하는 메소드 이다.
-(BOOL)canAccept:(NSString *)otherName;
@optional // 꼭 구현할 필요는 없다.
-(BOOL)doYouLikeIt:(NSString *)boysName;
@end
어떤 클래스가 프로토콜을 구현을 하고 있다고 컴파일러에게 알리고자 할 경우,
@interface MyName : NSObject <프로토콜 이름, 프로토콜 이름 ...>
이렇게 한다. dataSource 패턴에 적절하다.
그럼 어떻게 객체가 프로토콜을 정의하고 있는 알 수 있을까?
역시 NSObject 에서
-(BOOL)conformsToProtocol:(Protocol *)aProtocol;
을 사용하면 된다.
예제)
if([receiver conformsToProtocol:@protocol(MyXMLSupport)]){
// 코드...
}
또 어떤 객체가 프로토콜을 사용하고 있다는 것을 알려주고자 할 경우,
MyObject <프로토콜이름> *myObject;
처럼 사용한다.
informal, fomal protocol 이란 것도 보게 될 것이다.
위에 DoYouLikeMe 같이 정의하는 것을 formal 이라고 하고
NSObject 에 카테고리처럼 정의 하는 것을 informal 이라고 한다.
formal 은 컴파일러가 체크를 할 수 있지만,
informal 은 컴파일러가 체크를 할 수 없다.
informal 은 delegate methods 를 구성할 경우 많이 사용한다.
아래를 보면 카테고리와 흡사하게 정의됨을 볼 수 있다.
대부분 NSObject 에 바인딩되고, 구현하지 않는다는 점에서 다르다.
(프로토콜이란 것은 적용하는 객체가 구현하는 것이니 당연하다)
@interface NSObject(MyProtocol)
-(id)makeGirlfriend:(NSString*)name;
-(NSArray *)girlsLikeMe;
@end
informal 프로토콜은 카테고리와 약간 구별하기 애매할 것이다.
구현을 하느냐 안하느냐로 우선 구분해야 한다.
처음부터이해하려고 하지 않아도 된다.
16. get/set 그리고 property
오브씨에서는 get/set 에 대한 규정이 자바처럼 엄격하지 않다.
대부분 setXXX 는 쓰지만, getXXX 보다는 xXX로 쓴다.
이런 get/set 을 보다 쉽게 만든 것이 바로 Objective C 2.0 에 포함된
property 이고, "." 로 객체의 변수에 접근할 수 있게 한다 .
사용법도 간단하다.
헤더 파일엔 다음처럼 선언한다.
@property (nonatomic, retain) NSString *name; // 객체를 쓰레드 세이프하지 않게하고, retain 한다
@property (nonatomic, readonly)NSString *myGirl; // get 만할 수 있다는 뜻.
nonatomic 은 쓰레드로 접근할 때 락을 하느냐 마느냐 입니다.
폰에서는 대부분 퍼포먼스의 이유로 nonatomic 으로 사용하라고 한다.
구현파일(*.m) 파일엔 @implementation 아래에
@synthesize name, myGirl;
처럼 하면 자동적으로 get/set 이 구현된다. (컴파일 타임에 생성된다는 의미)
사용은
myObject.name = @"hehehe";
NSString *herName = myObject.myGirl;
처럼 "." 표현을 사용한다.
코드량을 많이 줄여주고, 잘못 사용하면 메모리 락이 날 수 있다.
따라서 애플 문서를 반드시 참고하자.
17. private, protected, public ...
@private, @protected, @public 으로 표기하고 그 이하의 변수가 포함된다.
다만 메소드에는 이런 스코프 제한이 없다.
즉, 헤더에 보여지는 것은 모두 public 이다.
private 한 경우 "_" 를 붙여 개발자 상호간에 이해하는 컨벤션을 쓴다던가,
또는 *.m 파일에 카테고리를 사용해서 헤더에 노출을 시키는 방법을 쓴다
(물론 클래스 덤프하면 다 보인다)
더불어 클래스 변수도 없다.
*.m 파일 내의 @implementation 안에서 static 으로 선언해 쓰거나 해야 한다.
상속받은 객체에서 보기를 원한다면 extern 선언을 해야 한다.
18. @class, @protocol
예제 코드를 보다보면 헤더에 "@class XXXX;" 식의 코드를 볼 수 있다.
이것은 XXX 라는 클래스가 있다는 것을 컴파일러에게 알려주는 역할을 한다.
마찬가지로 @protocol 도 같은 역할을 한다.
물론, 헤더파일에 해당 클래스를 임포트 시켜도 된다.
@class 는 컴파일러나 링커가 임포트 보다는 좀 쉽게 처리할 수 있게 한다
이정도만 알면 된다.
여기까지가 기본문법이고
이하는 몇가지 아이폰을 개발하면서 알아두면 좋을 것들이다.
19. 디자인 패턴
첫번째로 MVC 모델이다. 관련된 내용은 인터넷에 수많은 글들이 있으니 생략.
두번째는 target/action 패러다임을 사용한다. - 아래설명
세번째는 살펴본 바와 같이 delegate, dataSource 패턴을 사용한다.
네번째는 노티피케이션 이다.
어떤 일이 일어난 것에 관심이 있을 경우 노티피케이션 센터에 관심이 있다고 등록한다.
어떤 객체가 노티피케이션을 보내면 센터에 등록된 모든 객체가 반응할 수 있게 한다.
일종의 메세지 브로드캐스팅이라고 보면 된다.
다섯번째는 late loading 이다.
불필요한 객체나 리소스를 처음부터 생성하지 말고, 가능하면 필요할 때 생성/로드 하라는 것.
여기서 필요를 결정하는 것은 개발자이지 반드시는 아니다.
20. 프레임웍 구조
오브씨의 기본 프레임웍은 Foundation 이라고 한다.
문자, 문자셋, 어레이, 집합, 딕셔너리, 넘버, 날짜, 로케일 등등이 모두 여기에
포함되어 있다.
화면과 관련된 것은 아이폰에서는 UIKit 이라고 하고, OS X 에는 AppKit 이라고 한다.
물론 스트링은 유니코드를 기반으로 한다.
한글 인코딩은 EUC-KR(5601-1987), MS949, KSC5601_7, KSC5601_92_Johab,
Mac Korean(EUC-KR 과 같으나 CRLF 가 다름) 등을 지원한다.
참고로 오브씨의 프레임웍/런타임을 통칭해서 Cocoa 라고 부른다.
NeXT 사가 애플에 합병되면서 가져온 프레임웍이고 OPENSTEP 이란
스펙에 따라 만들어 졌다.
Mac 고유의 API 는 수정되어 Carbon 이라고 불린다.
CFxxx 이렇게 시작하는 것들이 그것들이라고 보면 된다.
(Carbon 을 이런 함수들모만 보는 것은 무리가 있긴 하다..)
21. firstResponder, File's owner
이것은 언어적인 특성이라기 보다는 MVC 를 구성하면서 파생된 개념으로 보면 된다.
firstResponder 는 OS X 에서는 첫번째 키보드나 마우스 이벤트를 받는 객체이다.
아이폰에서는 키보드나 마우스란 것이 없으니 터치만 해당된다.
반드시 그런 것은 아니지만, 우선 아이폰에서는 화면상에서 키보드가 나타나고
커서가 보이는 것이 바로 firstResponder 객체라고 보면 된다.
예를 하나 들어보겠다.
만약 메뉴에서 copy & paste 를 구현한다고 할때,
-copy; -paste; 메소드를 보내야 할 것이다.
이때 어떤 객체에 보내야 효율적인 코딩이 될까?
바로 firstResponder 에 보내는 것이다.
File's owner 는 인터페이스빌더(약칭 IB) 에서 사용하는 개념이다.
코코아에서는 MVC 중 V와 C를 냉동보관했다가
어플리케이션이 필요할 때 녹여서 메모리에 올리는 기법을 사용한다.
물론 코드만 가지고 어플리케이션을 짜도 되지만,
툴을 사용하면 코드량을 획기적으로 줄일 수 있다는 장점이 있다.
대략 UI를 잡은 다음에 코드로 세밀하게 다듬는 식의 방법을 많이 사용한다.
(OS X 에서 애플의 어플리케이션을 보면 이런 방법을 많이 사용)
그 냉동된 파일을 NIB(NeXT Interface Builder) 이라고 하는데
요즘은 XIB( 확장자 *.xib) 이라고 한다.
IB 를 가지고 화면을 꾸미고 컨트롤과 연결한 후 실제 프로그램에선
NIB 을 로딩해야 한다.
이 때 NIB 의 소유자를 FIle's owner 라고 한다.
대부분 MVC 중 C 에 해당하는 것들이 File's owner 가 된다.
즉 컨트롤과 뷰간에 객체를 인식(outlet) 하고 메세지를 보내고자 할 경우,
컨트롤 객체가 생성이 되어 있어야 할 것이다.
바로 그것이 File's owner 가 된다.
22. outlet, target, action, connection
오브씨는 이벤트 드리븐이 아니라 메소드(메세지) 드리븐이라는 말을 한다.
어떤 객체에 이벤트가 발생 했을 때 타 객체에 메세지를 보내는 방식으로
일을 처리 한다. 처리할 이벤트를 등록해서 처리하는 방법이 아니다.
NIB 파일은 위에서 설명한 대로 MVC 중 V와 C를 녹여(시리얼라이즈) 해 놓았는데
컨트롤이 화면의 객체를 알아야 할 필요가 있다.
그래야 메세지를 보내 속성을 바꾸거나, 텍스트를 뿌리거나, 슬라이드를 이동하거나
사이즈를 줄이거나 할 수 있는 것이다.
IB 에서는 이를 outlet 이라고 한다.
또한 컨트롤 (버튼, 슬라이더 등) 이 움직였을 때 컨트롤이 반응할 수도 있어야 한다.
이처럼 메세지를 받는 쪽을 target 이라고 하고 보내는 메세지를 action 이라고 한다.
또 IB 에서 outlet 을 정하거나, 메세지를 보내는 것을
커넥션을 맺는다고 한다. (control + drag 해서)
OS X 에서는 action 메소드는 항상
"-(IBAction)actionMethod:(id)sender;" 처럼 정의 한다.
iPhone 에서는 "-(IBAction)actionMethod;" 처럼 정의해도
인터페이스 빌더가 알아챈다.
IBAction 은 void 형이고, 인터페이스 빌더가 액션메소드 임을 알아내기 위한
예약어라고 보면 된다.
IBOutlet 은 인터페이스 빌더가 outlet 을 구별하기 위해 사용하는 예약어라 보면 된다.
후아 여기까지 읽어가며 타이핑 하는 데도 오래 걸리는 군요
저도 기억이 새록새록 돋아나는 게 ..
우선은 developer 카테고리를 좀 정리하며 복습을 해야 겠습니다.