글쓴거 보니까 생각외로 길지 않네..
좀더 길게 써도 될듯..
복사 붙이기는 없다.. 무조건 타이핑.. gogo
- 클래스 인터페이스 : .h 파일
* 클래스는 @interfacd 와 @end 사이에 정의한다.
예제
@inferface ClassName : ItsSuperclass { // 인터페이스 뒤에 새로운 클래스 명을 적어준다. 그리고 그 뒤에 상위 클래스를 지정해 준다. 만약 상위 클래스가 없다면 생략할 수 있다. (생략시 콜론 (:) 역시 생략해야 한다)
// instance variable declarations 인스턴트를 선언한다.
float width;
float height;
BOOL filled;
NSColor *fillColor;
}
//method definitions 메소드를 선언한다.
+ alloc; // 클래스 메서드
- (void)display; // 인스턴스 메서드
- (float) getRadius; // 인자가 없는 경우 메서드 뒤에 바로 세미콜론으로 마친다
-(void) setRadius:(float)radius; // 전달하는 인자가 있을 때는 콜론을 이용하여 타입과 변수 이름으로 선언. 인자의 타입은 괄호로 묶는다.
-(void) setWidth:(float)width height:(float)height; // 인자가 두개 이상일 때는 '인자의지시자(외부이름):(타입)인자의 이름(내부 로컬이름,선언)'의 형태로 추가해준다.
@end
위와 같이 정의한 메서드의 실제호촐 코드
id *myRect = [[Rectangle alloc]init]; // 객체 생성
[myRect setWidth:12.2 height:18.0]; //메시지 전달
이런 식으로 전달하는 인자 자체에도 이름을 붙인다.
-(void)sumNumber:(int)numargs,... // 전달하는 인자의 개수가 정해지지 않을 때에는 이 같이 쉼표(,)와 생략표시 (...,Ellipsis)를 이용하여 정의할 수 있다.
인자의 개수가 정해지지 않은 매서드의 경우에는 생략 표시(Ellipsis)를 이용하여 매서드를 정의할 수 있고 사용할 수 있는데,
이 내용은 Objective-C의 문법이 아닌 C나 C++에서 사용되는 문법이다.
사용예제
- (void)sumNumber:(int)numargs,...{
// ...에 해당하는 다양한 길이의 argument들의 list를 저장하는 va_list를 만들자.
va_list listPointer;
===============================================
// 가변인수 - C
인수의 개수와 타입이 미리 정해져 있지 않다는 뜻.
이런 인수를 사용하는 함수를 가변인수 함수라 한다 .
고정인수는 생략 기호 이전에 전달되는 인수로 원형에 타입과 개수가 분명히 명시되어 있으므로 원형대로 정확하게 전달해야 한다.
가변함수 구조
void VarFunc(int Fix, ...)
{
va_list ap;
va_start(ap,Fix);
while (모든 인수를 다 읽을 때까지) {
va_arg(ap,인수타입);
}
va_end(ap);
}
va_list ap
함수로 전달되는 인수들은 스택(Stack)이라는 기억 장소에 저장되며 함수는 스택에서 인수를 꺼내 쓴다.
스택에 있는 인수를 읽을 때 포인터 연산을 해야 하는 데 현재 읽고 있는 번지를 기억하기 위해 va_list 형의 포인터 변수 하나가 필요하다.
변수 이름은 ap로 되어 있는데 아마도 Argument Pointer의 약자일 것이다.
ap는 어디까지나 지역변수 일 뿐이므로 이름은 맘대로 정할 수 있되 관습적으로 가변인수를 다루는 매크로에서는 ap라는 이름을 사용한다.
va_list 타입은 char* 형으로 정의되어 있다. 가변 인수를 읽기 위한 포인터 변수를 선언했다고 생각하면 된다.
va_start (ap, 마지막 고정인수)
이 명령은 가변 인수를 읽기 위한 준비를 하는데 ap 포인터 변수가 첫 번째 가변 인수를 가리키도록 초기화한다.
첫 번째 가변 인수의 번지를 조사하기 위해서 마지막 고정 인수를 전달한다.
va_start 내부에서는 ap가 마지막 고정인수 다음 번지를 가리키도록 해주므로 이후부터 ap 번지를 읽으면 순서대로 가변인수를 읽을 수 있다.
va_arg (ap, 인수타입)
가변 인수를 실제로 읽는 명령이다.
va_start가 ap를 첫 번째 가변 인수 번지로 맞추어 주므로 ap 위치에 있는 값을 읽기만 하면 된다.
단, ap 번지에 있는 값이 어떤 타입인지를 지정해야 이 매크로가 값을 제대로 읽을 수 있으므로 두 번째 인수로 읽고자 하는 값의 타입을 지정한다.
예를 들어 ap 위치에 있는 정수 값을 읽고자 한다면 va_arg(ap, int)를 호출해야 한다.
물론 리턴 되는 값은 인수타입에 맞는 변수로 대입받아야 한다.
이 명령은 ap 위치에서 타입에 맞는 값을 읽어 리턴하며 또한 ap 를 다음 가변 인수 위치로 옮겨준다.
그러므로 va_arg를 반복적으로 호출하면 전달된 가변 인수를 순서대로 읽을 수 있다.
* 이 명령에서 특이한 점은 int나 double 같은 타입 이름이 함수 인수로 전달될 수 있다.
함수의 인수로는 값이 전달되는 것이 정상이며 타입명은 분명히 함수의 인수가 될 수 없다.
하지만 va_arg는 진짜 함수가 아니라 매크로 함수이기 때문에 va_arg의 두번째 인수는 내부적으로 sizeof 연산자와
캐스트 연산자로 전달되기 때문에 타입명이 될 수 있다.
va_end(ap)
이 명령은 가변 인수를 다 릭은 후 뒷정리를 하는데 별다른 동작은 하지 않으며 실제로 없어도 전혀 지장이 없다.
이 명령이 필요한 이유는 호환성 때문인데 플랫폼에 따라서는 가변 인수를 읽은 후에 뒷처리를 해야 하는 경우가 있기 때문이다.
호환성을 위해서는 관례적으로 넣어주는 것이 좋다.
=================================================
// 현재는 listPointer가 초기화 되지 않았기 때문에 va_start 명령을 통해 초기화 해 줘야 한다.
// 이때 첫번째 argument 인 numargs를 함께 넣어준다.
va_start(listPointer, numargs);
int sum = 0;
for(int i = 0 ; i < numargs ; i++) {
// va_arg 매크로를 사용하여 listPointer 값을 가져온다.
int arg = va_arg(listPointer, int);
printf(" %d 년 나이는 %d 입니다. \n", i, arg);
sum += arg;
}
printf("--");
printf("Argument List 끝 \n\n");
// 마지막으로 호환성을 위해 va_end() 매크로를 사용하여 정리한다.
va_end(listPointer);
printf("나이 총 합은 %d \n\n", sum);
//printf()
C 프로그래밍의 기본적인 출력함수이다.
printf()와 puts()가 있는데 둘의 차이점은 변수를 출력할 수 있느냐와 자동으로 행이 바뀌는 여부이다.
puts()의 경우 자동으로 행이 바뀌지만 변수를 출력할 수 없고
printf()는 변수를 출력할 수 있지만 개행 문자를 써서 행을 바꾸어야 한다.
변수 사용 예제
void main()
{
int score=1;
char name='a';
double record=0.5;
printf("변수 출력. %d , %c , %f ",score,name,record);
printf("계산식을 써넣으면 계산결과가 나옵니다. %d , %c , %f ",score+10,name+1,record+0.1);
}
출력 결과
변수 출력. 1 , a, 0.500000
계산식을 써넣으면 계산결과가 나옵니다. 11, b, 0.600000 //b가 나오는 건 C에선 문자도 아스키 코드로 저장되어 연산이 가능해서임.
데이터형 |
소속그룹 |
printf()에서의 표시 |
int |
정수 |
%d |
double |
실수 |
%f |
char |
문자 |
%c |
}
이와 같은 생략(Ellipsis) 형태 매서드를 사용할 때 따라야 할 문법은
* 반드시 ... 문자만을 적어야 하며,
* ... 를 포함한 인자는 메서드 선언의 가장 마지막에 나와야 하며,
* 하나의 인자 이름을 적고 그 뒤에 ... 가 따라나와야 한다.
- 인터페이스 파일 임포트
* 인터페이스 파일은 그 클래스 인터페이스와 연관이 있는 소스 파일(즉, 구현파일)에 포함시켜야 한다.
* #import 구문을 사용하여 소스 파일에 포함할 수 있다.
#import "Rectangle.h"
* 상속의 경우에도 상위 클래스에 대한 헤더파일을 임포트 해야 한다.
- 다른 클래스 참조
* 만약 인터페이스가 상속 구조상에 있지 않은 클래스를 참조하고자 하면
@class 구문을 사용하여 선언한다.
@class Rectangle, Circle; // Rectangle 과 Circle이 클래스 파일임을 컴파일러에게 알려준다.
* 임포트 구문대신 이런 클래스 구문을 쓰는 이유는 서로가 서로를 참조하고 있는 순환의존(Cyclic Dependency)을
가질 때에도 컴파일 할 수 있다는 장점이 있기 때문.
- 인터페이스 파일의 역활
* 다른 소스 모듈에 새로운 클래스를 정의하는 것 = 이 클래스를 통해 하고자 하는 일에 필요한 모든 정보를 갖는 다는 것.
(소스코드를 보지 않고 현재의 클래스 정보를 파악할 수 있다.)
* 인터페이스 파일은 사용자들이 클래스의 상속 관계를 파악할 수 있도록 해준다.
* 컴파일러에 클래스가 어떤 인스턴스 변수를 가졌는지, 어떤 변수를 상속할지 등을 알려준다.
* 메서드 선언을 통해 메시지를 처리할 때, 리시버가 그 메시지를 처리할 수 있는 지를 파악할 수 있도록 해준다.
- 클래스 구현파일 : .m 파일
* 클래스 구현(Implementation)은 그 선언과 비슷한 구조를 지니고 있다.
* 클래스 구현은 @implementation 으로 시작해서 @end 로 끝난다.
* 모든 구현 파일은 그에 대응하는 인터페이스 파일(.h파일)을 임포트해야 한다.
이로 인해 1. 슈퍼클래스의 이름, 2. 인스턴스 변수들의 선언 을 생략할 수 있다.
* 인터페이스 파일에서 선언했던 매서드들의 실제 동작 코드를 작성/구현하게 된다.
- 인스턴스 변수의 범위 (Scope)
* 인스턴스 변수들이 인터페이스에 선언되어 있다 하더라도, 선언하는 방식에 따라 그 변수들의 범위가 정해진다.
유형 |
범위 |
상속 가능 여부 |
@private |
선언한 클래스 내에서만 |
불가 |
@protected |
선언한 클래스와 상속한 클래스 (default) |
가능 |
@public |
어디서든 접근 가능 |
가능 |
* 인스턴스 변수를 선언하는 유형은 이를 선언한 이후부터 그 변수 리스트의 끝이거나 다음 유형의 선언이 있을 때까지 모든 변수에 적용된다.
* 아무런 지시자가 없이 선언된 경우에는 모든 변수는 protected로 취급된다.
- 매서드 오버라이드 (Method Override)
* 서브클래스는 새로운 기능을 추가할 수 있을 뿐만 아니라, 상속한 매서드를 새로운 구현으로 대체할 수 있다.
특별한 문법 없이 단순히 해당 매서드를 구현파일에서 다시 작성하기만 하면 된다.
* 객체가 수신할 수 있는 메시지의 종류를 변경하는 것이 아니라, 메시지에 응답하기 위해 사용될 메서드의 구현을 변경해야 한다.
* 이처럼 각 클래스가 독자적인 버전의 매서드를 구현할 수 있는 능력을 다형성이라고 한다.
* 주의 할 점은 특정 클래스에서 매서드를 오버라이드하는 작업은 상위 클래스의 매서드 동작에 영향을 주지 않는 다는 것이다.
- 매서드 확장
* 한 클래스로부터 상속한 매서드를 오버라이드 하는 대신 그 매서드를 확장하는 것도 가능하다.
* 매서드를 확장하기 위해서는 새로운 구현파일에서 매서드를 오버라이드 하면서 상위 클래스의 같은 매서드를 호출하면된다.
* 즉 오버라이드 할 메서드의 첫 부분에서 [super 메서드이름]; 를 명시함으로써 매서드 오버라이드가 진행되기 전에 상위 매서드의 원 기능을 동작 시켜 주는 것이다.
- 객체 생성
* 클래스의 주요기능은 그 클래스가 정의하는 유형의 새로운 객체를 생성하는 것이다.
* 객체는 런타임시 2단계의 과정을 거쳐 생성된다.
1. 새로운 객체의 인스턴스 변수를 위해 메모리 할당
2. 생성된 인스턴스를 초기화
예문 // 두단계를 한꺼번에 수행
NewClass *newObject = [[NewClass alloc]init];
alloc 메서드는 NewClass의 새로운 인스턴스를 위해 메모리를 동적으로 할당하여 새로운 객체를 반환한다.
alloc 매서드를 사용하여 메모리를 할당한 뒤에 init 함수를 호출하여 객체를 초기화 하게 된다.
중요한 것은 init 함수를 사용하기 위해서는 NewClass 를 위한 메모리 공간이 할당된 상태여야 한다.
또한 객체는 초기화 되기 전까지는 사용할 수 없기 때문에 클래스의 인스턴스 객체를 사용하기 위해서는 alloc 다음에 반드시
init 과정을 수행해줘야 한다.
주의점 : 객체는 단 한 번만 초기화 되어야 한다.
* 생성된 객체(메모리 할당 및 초기화 이후)는 독자적인 변수를 보유하여 완전한 기능을 하는 클래스의 일원이 된다.
(메시지를 수신(=클래스에 정의된 함수를 호출할 수 있음), 인스턴스 변수에 값을 저장할 수 있다.
* init 매서드의 서브클래스 버전 (initWith.. 같은)은 정상적으로 초기화 된 이후 새로운 객체인 self를 돌려보내야 한다.
초기화 될 수 없다면 메서드는 객체를 해제하여 nil을 돌려보내야 한다.
* 어떤 경우에는 init 메서드는 새로운 객체를 해제하고 대체물을 돌려보낼 수 있다.
따라서, 프로그램은 init이 돌려보낸 객체를 사용해야 하고 alloc 함수에 의해 돌려보낸 객체를 반드시 사용할 필요는 없다.
* 또한, init 의 서브클래스 버전은 super 에 메시지를 보냄으로써, 상속받은 클래스의 초기화 코드를 통합해야 한다.
예시
- init {
if(self = [super init]){
// class-specific initialization goes here
}
return self;
}
super로 보내지는 메시지는 메서드에 추가된 초기화 코드 앞에 있어야 하고, 이는 초기화가 상속받은 순서대로 수행되어야 함을 의미 한다.
- 자주 사용하는 문법
- 셀렉터 (Selector)
* 셀렉턴는 SEL 이라는 타입을 가진다.
* 개념적으로는 함수 포인터와 같다고 볼 수 있다.
참고 사이트
'개발 > Objective-C' 카테고리의 다른 글
#pragma (0) | 2010.09.01 |
---|---|
기초부터 다시 시작 - 반(1) Objective - c (0) | 2010.09.01 |
* <- 이넘의 정체 (0) | 2010.08.31 |
[Objective-C] Convert NSString to char * (0) | 2010.08.29 |
[Objective-C] Get html data from web (0) | 2010.08.29 |