ObjectiveC Linux: Difference between revisions
Onionmixer (talk | contribs) (내용수정) |
(No difference)
|
Latest revision as of 10:41, 3 June 2013
- 책의 내용을 참고로한 Objective C for linux 관련문서
개요
이 문서는 프로그래밍 오브젝티브 C 2.0[1] 책을 보면서 공부하는 내용을 나름대로 정리하며 만드는 문서입니다. Linux 기반에서 내용을 진행하는것을 전제로 하고있으니 문서를 읽으시는분들은 참고해주시기 바랍니다.
기본조건
- Gentoo linux (정말 간단하군요 :D)
기초환경준비
gentoo linux 에서 Objective-C (이하 objc) 를 공부하기 위해서는 다음의 조건이 만족되어야 합니다.
- 현재 설치되어있는 gcc 에서 objc 를 지원하는가.
- Foundation 을 사용하기위한 gnustep-base 가 설치되어있는가.
이제부터 위의 2개 항목을 확인하는법을 간단하게 알아보도록 하겠습니다.
gentoo 에서 gcc 의 objc 지원여부 확인
아시는분들은 아시겠습니다만.. gentoo 는 portage 로 관리되고 있죠. 그럼 제 경우를 한번 예로 들어보겠습니다.
localhost ~ # emerge -pv sys-devel/gcc
These are the packages that would be merged, in order:
Calculating dependencies... done!
[ebuild R ] sys-devel/gcc-4.6.3:4.6 USE="cxx fortran gcj gtk mudflap (multilib) nls nptl objc objc++ objc-gc openmp (-altivec) -doc (-fixed-point) -graphite (-hardened) (-libssp) -multislot -nopie -nossp {-test} -vanilla (-bootstrap%) (-build%) (-go%)" 24 kB
이야.. gentoo 를 스시는 분들이라면 익숙한 화면입니다. 제 경우는 USE Flag 에 "objc objc++ objc-gc" 이 3개가 포함되어있군요. 이중 objc-gc 는 조금 중요한데 이 FLAG 를 사용하지 않으면 나중에 가비지콜렉터[2]를 사용할 수 없다고 합니다. 고로 꼭 넣어줘야겠죠? objc++FLAG 를 사용하지 않으면 Objective-C 내부에서 c++ 라이브러리를 사용할 수가 없다고 합니다. 일단 저 FLAG가 들어가있으면 상관이 없구요.... FLAG 가 포함되어있지 않다면 포함해서 compile을 진행해주시기 바랍니다.(gentoo의 portage 사용법에 대해서는 따로 설명은 하지 않겠습니다.)
gnustep-base 의 설치
간단합니다.
localhost ~ # emerge gnustep-base
아마도 저렇게 하면 아래와같이 2개 정도의 패키지가 설치될겁니다.
- gnustep-make
- gnustep-base
gnustep-base 는 이후 다루게될 Foundation Framework 을 가지고 있는 패키지로서 이게 없으면 대부분의 예제는 사용 불능상태라고 보시면 되겠습니다. 가볍게 설치해줍니다.
gnustep-base 의 설치후 작업
gnustep-base 를 설치하고 나면 터미널을 새로 열때마다 다음과같은 메세지를 보게됩니다.
2013-05-31 06:09:45.685 gdnc[9632] File NSBundle.m: 2600. In -[NSBundle localizedStringForKey:value:table:] Localisation file /usr/lib64/GNUstep/Libraries/gnustep-base/Versions/1.24/Resources/Korean.lproj/Localizable.strings not in portable encoding so I'm using the default encoding for the current system, which may not display messages correctly.
The file should be ASCII (using \U escapes for unicode characters) or Unicode (UTF16 or UTF8) with a leading byte-order-marker.
2013-05-31 15:09:45.685 gdnc[9632] File NSDictionary.m: 676. In -[NSDictionary initWithContentsOfFile:] Contents of file '/usr/lib64/GNUstep/Libraries/gnustep-base/Versions/1.24/Resources/Languages/Korean' does not contain a dictionary
localhost ~ #
어라 뭐가 없다네요?
- /usr/lib64/GNUstep/Libraries/gnustep-base/Versions/1.24/Resources/Languages/Korean
이 디렉토리가 없다는데 간단하게 해결을 보도록 하겠습니다.
localhost ~ # cd /usr/lib64/GNUstep/Libraries/gnustep-base/Versions/1.24/Resources/Languages
localhost Languages # cp -R English Korean
이 이후로는 경고따위는 나오지 않습니다 :D
First Application : Programming is fun!
이제부터 linux상에서 첫번째 프로그램을 compile 해보도록 하겠습니다.
프로그램의 실행조건은 다음과 같습니다.
▶ | Programming is fun! 이라는 문자열을 화면에 출력하기 |
입력해야하는 예제코드는 다음과 같습니다.
/*
*
* 첫번째 예제 프로그램
* Programming is fun! 이라는 문자열을 화면에 출력하기
*
*/
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSLog (@"Programming is fun!");
[pool drain];
return 0;
}
모르는게 좀 보이기는? 합니다만... 대략 구조는 그리 복잡하지 않은거같습니다.
위의 파일을 example_01.m 이라는 이름으로 저장하겠습니다.
이제 이 파일을 compile 하는 과정을 자동으로 진행하기 위한 Makefile 을 만들어보도록 하겠습니다.
Makefile 이야 워낙에 유명하니 별도로 설명을 할필요는 없겠죠?
# example_01.m 을 위한 makefile 예제
.SUFFIXES : .c .o .m
CC = gcc
EXAMPLE_NUMBER = 01
LIBS = `gnustep-config --base-libs`
OBJCFLAGS = `gnustep-config --objc-flags`
OBJS = example_$(EXAMPLE_NUMBER).o
SRCS = example_$(EXAMPLE_NUMBER).m
TARGET = example_$(EXAMPLE_NUMBER)
all : $(TARGET)
$(TARGET) : $(OBJS)
$(CC) -o $@ $(OBJS) $(LIBS)
clean :
rm -rf $(OBJS) $(TARGET) core
distclean :
rm -rf $(OBJS) $(TARGET) core *.d
new :
$(MAKE) clean
$(MAKE)
Makefle 이 쪼오금 복잡해보이기는 합니다. 일반적인 c 언어 또는 c++ 언어 등에서 사용하는 Makefile 과는 약간 틀린데요.. 뭐가 틀린지 잠시만 짚어보도록 하겠습니다.
- INC 가 없다 :: LIBS 에서 관련된 내용을 한꺼번에 다루게 됩니다.
- dep section 이 없다 :: 원래 그런지는 모르겠습니다만.... *.d 파일이 compile 과정에서 알아서 생성되는거같습니다.
- OBJCFLAGS 를 사용한다 :: 기존의 Makefile 에서는 CFLAGS 를 사용합니다. OBJCFLAGS 로 해야 에러없이 compile이 되더군요.
프로그램의 compile 및 실행
다음과같이 입력하면 compile 및 compile 된 프로그램의 결과를 볼 수 있습니다.
localhost project_objecivec_study # make
cc `gnustep-config --objc-flags` -c -o example_01.o example_01.m
gcc -o example_01 example_01.o `gnustep-config --base-libs`
localhost project_objecivec_study # ./example_01
2013-05-31 07:02:14.441 example_01[10130] File NSBundle.m: 2600. In -[NSBundle localizedStringForKey:value:table:] Localisation file /usr/lib64/GNUstep/Libraries/gnustep-base/Versions/1.24/Resources/Korean.lproj/Localizable.strings not in portable encoding so I'm using the default encoding for the current system, which may not display messages correctly.
The file should be ASCII (using \U escapes for unicode characters) or Unicode (UTF16 or UTF8) with a leading byte-order-marker.
2013-05-31 16:02:14.441 example_01[10130] Programming is fun!
흠.. 뭔가 많이 출력되는군요.. 내가 구경하고싶은건 한줄이었는데... 아마도 인코딩관련 부분에서 뭔가 문제가 있는거같습니다만.... 별일 아니니 무시하도록 하겠습니다. (사실 저 NSBuldle 의 class method 에 뭐가 들어가는지 예제를 만들어서 NSLog 로 찍으면 될거같은데.. 귀찮으니 pass)
중요한건 이 한줄입니다.
- 2013-05-31 16:02:14.441 example_01[10130] Programming is fun!
이야... 프로그램이 제대로 실행이 됐군요! 이제 Linux 에서 Foundation Framework 을 사용할 수 있는 준비는 되었다고 봐도 되겠습니다! :D
이제부터 Makefile 과 프로그램 소스에 대해 아주 쪼금 더 자세히 알아보겠습니다.
프로그램 소스코드 살펴보기
/*
*
* 첫번째 예제 프로그램
* Programming is fun! 이라는 문자열을 화면에 출력하기
*
*/
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSLog (@"Programming is fun!");
[pool drain];
return 0;
}
워매.. 겁나게 간단한 코드지라~
자.. 하나하나 살펴보도록 하겠습니다.
- 주석스타일은 C 언어 스타일이다.
- NS..... 로 시작하는것들은 다 Foundation Framwork 에서 나온것들이다.
- 때문에 위의 #import <Foundation/Foundation.h> 부분이 없다면 NS 로 시작하는것들은 올바른 동작을 하지 못한다.
[ ] 부분에 대한 설명
objective-c 는 Smalltalk 에서 컨셉을 가져온것 덕분에 객체와 메서드로 구분을 하게 되는데, [ ] 로 둘러쌓인 부분은 이렇게 객체를 이용한 작업에 사용되는 구문이라고 생각하면 됩니다. 기본 구조는 다음과 같습니다.
[Object instanceMethod]
그럼 위의 구조를 참고로 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 구문의 진행순서를 한번 짚어보겠습니다.
- NSAutoreleasePool * pool :: NSAutoreleasePool 클래스 타입으로 pool 이라는 객체를 생성한다.
- NSAutoreleasePool alloc :: pool 객체에 메모리를 할당한다.
- NSAutoreleasePool init :: pool 객체를 초기화한다.
쉽죠? 그럼 다음의 구문에서 참고해야될 부분을 살펴보겠습니다.
@로 시작하는 String에 대한 설명
NSLog 안에 들어가있는 내용에서 사실 중요한 부분은 (@"Programming is fun!") 입니다.
이 구문이 중요한 이유는 @"String" 형태가 NSString 이기 때문입니다.
일반적인 C 스타일의 문자열은 "string" 으로 묶입니다만.... Foundation Framework 에서는 @"String" 으로 문자열을 다루게 됩니다.
이후에 다루게 될 예제에서는 꾸준히 봐야하는 것들이니 기억해두면 좋은부분이라 생각합니다 :D
Makefile 살펴보기
# example_01.m 을 위한 makefile 예제
.SUFFIXES : .c .o .m
CC = gcc
EXAMPLE_NUMBER = 01
LIBS = `gnustep-config --base-libs`
OBJCFLAGS = `gnustep-config --objc-flags`
OBJS = example_$(EXAMPLE_NUMBER).o
SRCS = example_$(EXAMPLE_NUMBER).m
TARGET = example_$(EXAMPLE_NUMBER)
all : $(TARGET)
$(TARGET) : $(OBJS)
$(CC) -o $@ $(OBJS) $(LIBS)
clean :
rm -rf $(OBJS) $(TARGET) core
distclean :
rm -rf $(OBJS) $(TARGET) core *.d
new :
$(MAKE) clean
$(MAKE)
Makefile 은 사실 한번 익혀놓으면 앞으로도 도움이 꽤나 많이 되는 부분입니다. 상대적으로 소스코드에 비해 살펴볼 부분은 적기때문에 한가지만 살펴보도록 하겠습니다.
LIBS = `gnustep-config --base-libs`
OBJCFLAGS = `gnustep-config --objc-flags`
위의 2줄을 보면 gnustep-config 이라는 부분이 있습니다. 저 프로그램은 gnustep-make 패키지에 포함된 프로그램으로서 GNUStep 을 사용한 프로그램을 compile 하는경우 그에 따른 compile 시에 사용할 수 있는 옵션들을 얻을 수 있게 해줍니다. 조금 독특한 방법이기는 합니다만.... 이후에 linux에서 Objective-c 를 사용하려는 경우에 기억해야할 유용한 방법입니다. (맨 첫줄의 # 이 붙은 부분은 주석인건 다들 잠작 하실 수 있겠죠? :D)
Second Application : Objective-C 의 소스코드 구조
이번에 다루게될 예제는 숫자를 2개를 입력받아서 그걸 분수(fraction) 스타일로 출력하는 예제입니다. 원래 첵에서 나오는 설명은 한개의 파일에 모든 내용이 다 있습니다만 이 문서에서는 어떤식으로 구조가 이루어지는지를 같이 살펴보기 위해서 특별히 3개정도의 파일로 분리를 했습니다. 이번에는 살펴볼게 몇가지가 있겠군요. 일단 어떤 내용의 파일인지부터 살펴보겠습니다.
/*
*
* example_02.h
* @interface 에 대한 구현예제
*
*/
#import <Foundation/Foundation.h>
@interface Fraction: NSObject
{
int numerator;
int denominator;
}
-(void) print;
-(void) setNumerator: (int) n;
-(void) setDenominator: (int) d;
@end
/*
*
* example_02.m
* @implementation 에 대한 구현예제
*
*/
#import "example_02.h"
@implementation Fraction
-(void) print
{
NSLog (@"%i/%i", numerator, denominator);
}
-(void) setNumerator: (int) n
{
numerator = n;
}
-(void) setDenominator: (int) d
{
denominator = d;
}
@end
/*
*
* main.m
* 프로그램의 메인파트
*
*/
#import <Foundation/Foundation.h>
@class Fraction;
int main (int argc, char *argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Fraction *myFraction;
myFraction = [[Fraction alloc] init];
[myFraction setNumerator: 1];
[myFraction setDenominator: 3];
NSLog (@"The Value of myFraction is:");
[myFraction print];
[myFraction release];
[pool drain];
return 0;
}
# Makefile Example
.SUFFIXES : .c .o .m
CC = gcc
EXAMPLE_NUMBER = 02
LIBS = `gnustep-config --base-libs`
OBJCFLAGS = `gnustep-config --objc-flags`
OBJS = example_$(EXAMPLE_NUMBER).o main.o
SRCS = example_$(EXAMPLE_NUMBER).m main.m
TARGET = example_$(EXAMPLE_NUMBER)
all : $(TARGET)
$(TARGET) : $(OBJS)
$(CC) -o $@ $(OBJS) $(LIBS)
clean :
rm -rf $(OBJS) $(TARGET) core
distclean :
rm -rf $(OBJS) $(TARGET) core *.d
new :
$(MAKE) clean
$(MAKE)
각 파일의 주석에도 이미 적혀있습니다만... 각 파일의 역할을 다시한번 보겠습니다.
- example_02.h :: 프로그램에서 사용할 클래스의 interface 가 선언되어있다.
- example_02.m :: 프로그램에서 사용할 클래스의 implementation(구현) 이 선언되어있다.
- main.m :: 프로그램이 실행되는 시점에서 처음실행되는 main() 함수가 선언되어있다.
이 프로그램의 compile 후 실행결과는 다음과 같습니다.(사실 똑바로 compile 이 되지는 않습니다. 이유는 아래부분에서 따로 알아보도록 하겠습니다)
2013-05-31 22:09:04.552 example_02[14311] The Value of myFraction is:
2013-05-31 22:09:04.554 example_02[14311] 1/3
이제 두번째 예제에 대한 부분을 진행하기전에 Objective-C 의 지시어(지시자, Detective) 를 최소한으로 알아보고 이후 분석을 시작해보도록 하겠습니다.
Objective-C 의 지시어::@interface, @implementation
이번에 살펴볼 코드에서 나오는 Objective-C 의 지시어는 두가지입니다. 다른 언어들의 예를 같이 보면서 어떤 차이가 있는지를 살펴보겠습니다.
Class Example::Object Pascal
살짝~ 설명치고는 긴 느낌도 있습니다만.... Object Pascal 의 Class 관련 예제입니다.
// example from here :: http://www.tutorialspoint.com/pascal/pascal_classes.htm
{$mode objfpc} // directive to be used for defining classes
{$m+} // directive to be used for using constructor
program exClass;
type
Rectangle = class
private
length, width: integer;
public
constructor create(l, w: integer);
procedure setlength(l: integer);
function getlength(): integer;
procedure setwidth(w: integer);
function getwidth(): integer;
procedure draw;
end;
var
r1: Rectangle;
constructor Rectangle.create(l, w: integer);
begin
length := l;
width := w;
end;
procedure Rectangle.setlength(l: integer);
begin
length := l;
end;
procedure Rectangle.setwidth(w: integer);
begin
width :=w;
end;
function Rectangle.getlength(): integer;
begin
getlength := length;
end;
function Rectangle.getwidth(): integer;
begin
getwidth := width;
end;
procedure Rectangle.draw;
var
i, j: integer;
begin
for i:= 1 to length do
begin
for j:= 1 to width do
write(' * ');
writeln;
end;
end;
begin
r1:= Rectangle.create(3, 7);
writeln(' Darw Rectangle: ', r1.getlength(), ' by ' , r1.getwidth());
r1.draw;
r1.setlength(4);
r1.setwidth(6);
writeln(' Darw Rectangle: ', r1.getlength(), ' by ' , r1.getwidth());
r1.draw;
end.
간단하게 보자면 크게 두가지 부분으로 나눌 수 있습니다.
- 선언부 :: type 부분
- 구현부 :: function or procedure
Object Pascal 은 C언어의 prototype 선언(declearlation) 처럼, 상단부분에서 현재 파일내에서 clsss 의 구현에서 사용할 function 또는 procedure, 그리고 instance variables 등을 선언하는 선언부가 상단에 있습니다. 그리고 아래쪽에서 선언된 내용에 맞는 구현을 작성하는 구조로 되어있습니다.
C언어의 prototype 은 사실 이 이후에 설명할 Objective-C 의 내용과 비슷한 부분이 있는데요 내용은 여기 를 참고하시면 좀 더 명확한 내용을 확인할 수 있습니다.
Class Example::PHP
아래는 php 의 예제입니다.
class Vegetable {
var $edible;
var $color;
function Vegetable($edible, $color="green")
{
$this->edible = $edible;
$this->color = $color;
}
function is_edible()
{
return $this->edible;
}
function what_color()
{
return $this->color;
}
} // Vegetable 클래스 끝
php의 경우는 Object Pascal 보다는 좀 덜 복잡한편이죠. 별도로 선언부는 존재하지 않으며 그냥 편하게 막 쓰면 됩니다. Class를 선언하면서 구현의 작성까지 같이 가지고있는 경우라고 보시면 되겠습니다.
How do you define a Class?::Objective C
Objective-C 에서는 class 에서 사용하게될 element 의 interface 를 가지고 있는 @interface 부분과 선언된 메서드등을 구현하는 @implementation 부분으로 나눌 수 있습니다.
@interface
@interface 에서는 다음과 같은 것들을 선언할 수 있습니다.
- instance (객체) 상태에서 사용할 변수를 선언
- instance method 선언 :: (-) 기호를 사용합니다. 생성된 객체상태에서 사용할 수 있습니다. @class 지시어에 반응하지 않습니다.
- class method 선언 :: (+) 기호를 사용합니다. Class 에서 바로 사용할 수 있습니다. instance 변수에 접근할 수 없으며 @class 지시어로 사용할 수 있습니다.
이 부분을 참고해서 예제코드를 다시한번 읽으면 보이는것이 좀 틀려지겠죠? @interface 에서는 Class 의 흐름을 정의한다고 생각하시면 되겠습니다.
@interface Fraction: NSObject
{
int numerator;
int denominator;
}
-(void) print;
-(void) setNumerator: (int) n;
-(void) setDenominator: (int) d;
@end
이렇게 @interface 에서 선언되는 instance 변수들은 Class 가 실제로 객체(Object) 로 생성되는 경우에 메모리에 존재할 수 있기때문에 Class method 에서는 접근을 할 수 있는 방법이 없다는것을 참고해주시기 바랍니다.
@implementation
@implementation 에서는 다음과 같은것들을 작성할 수 있습니다.
- instance method 구현 :: (-) 기호를 사용합니다.
- class method 구현 :: (+) 기호를 사용합니다.
@implementation Fraction
-(void) print
{
NSLog (@"%i/%i", numerator, denominator);
}
-(void) setNumerator: (int) n
{
numerator = n;
}
-(void) setDenominator: (int) d
{
denominator = d;
}
@end
@implementation 의 경우는 method 에 대한 내부 구현등을 작성하게 됩니다 사실 별 내용이야 없습니다만...
위의 내용에서 쪼금 신경써서 봐야할 부분이 있습니다. setNumerator: 메서드의 내부 구현을 보면 numerator = n; 이라는 부분이 있습니다. 위에서도 설명했습니다만 instance method 는 Class 내부의 instance variables(변수) 에 접근이 가능하지만 Class method 는 접근이 불가능하다는걸 기억해주세요.
@class 지시어와 #import
이 부분에 대해서 알아보기전에 윗부분에서 소스코드를 나열할때 나왔던 main.m 파일의 내용을 한번 더 보도록 하겠습니다.
#import <Foundation/Foundation.h>
@class Fraction;
int main (int argc, char *argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Fraction *myFraction;
myFraction = [[Fraction alloc] init];
[myFraction setNumerator: 1];
[myFraction setDenominator: 3];
NSLog (@"The Value of myFraction is:");
[myFraction print];
[myFraction release];
[pool drain];
return 0;
}
이 파일의 내용을 적으면서 올바르게 compile 이 되지 않을거라고 한 얘기 혹시 기억이 나실지 모르겠습니다. 사실 compile 이 안된다기보다는 보기싫은 경고가 나오는 정도입니다만.... 일단 현재의 코드를 그대로 compile 하면 어떤 결과가 나오는지 보도록 하겠습니다.
localhost example02 # make
cc `gnustep-config --objc-flags` -c -o main.o main.m
main.m: In function ‘main’:
main.m:21:2: warning: no ‘-setNumerator:’ method found [enabled by default]
main.m:21:2: warning: (Messages without a matching method signature [enabled by default]
main.m:21:2: warning: will be assumed to return ‘id’ and accept [enabled by default]
main.m:21:2: warning: ‘...’ as arguments.) [enabled by default]
main.m:22:2: warning: no ‘-setDenominator:’ method found [enabled by default]
main.m:25:2: warning: no ‘-print’ method found [enabled by default]
gcc -o example_02 example_02.o main.o `gnustep-config --base-libs`
역시 경고 작렬입니다.. 내용인즉슨 말입니다.... 사실 메서드 못찾겠습니다...라는거죠.
문제는 @class 라는 지시어 덕분입니다. 위의 내용중 저 부분을 보도록 하겠습니다.
@class Fraction;
오호.. 저건 뭘믿고 저 내용밖에 없는걸까요? 이 부분을 이해하기 위해서는 잠시 C 언어를 살펴보는게 좋을거 같습니다.
C 언어에서의 extern
자세한 내용은 여기 를 참고해주세요.
사실 C에서 extern 이 문제가 되는 시점은 compile time 이 아니라 link time 입니다. extern 은 compile time 에서 굳이 문제가 안될거다.... 라고 compiler 에게 문제가 없다...라고 보장을 해주는게 되는거죠. 이 함수는 있는거니깐 걱정마 라는 의미가 되겠습니다.
Objective-C 에서의 @class
@class 는 c 에서 extern 과 비슷하게 사용됩니다. 하지만 사용할 수 있는것은 class 메서드 에 국한됩니다.
instance 메서드를 사용하려 한다면 반드시 해당되는 class 의 @interface 가 있는 파일이 #import 로 선언되어야 합니다[3][4].
간단하게 말하면 (+) 로 선언된 method 는 사용가능하지만 (-) 로 선언한 메서드를 사용하려면 #import 를 써야한다는 의미가 됩니다.
올바른 main.m 파일의 코드
경고없는 compile 을 원한다면 아래처럼 코드를 수정하면 됩니다.
#import <Foundation/Foundation.h>
#import "example_02.h"
int main (int argc, char *argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Fraction *myFraction;
myFraction = [[Fraction alloc] init];
[myFraction setNumerator: 1];
[myFraction setDenominator: 3];
NSLog (@"The Value of myFraction is:");
[myFraction print];
[myFraction release];
[pool drain];
return 0;
}
위처럼 코드를 수정하면 경고없는 깔끔한 compile 결과를 얻을 수 있습니다. :D
추가학습::Objective-C 에서의 다른 지시어들
@protocol
출처
http://soooprmx.com/wp/archives/2432
프로토콜의 개념 잡기
프로토콜은 “선언만 되고 구현되지 않은” 메소드를 말한다. 이 프로토콜은 역시나 “너무 단순해서 쉽게 감이 잡히지 않는” Objective-C의 기능이다. 프로토콜은 크게 다음 3가지 경우에 유용하게 사용된다.
- 다른 객체가 구현해주면 되는 메소드를 선언
- 클래스를 숨기고 인터페이스만 선언하고자 할 때
- 상속관계가 아니지만 비슷한 인터페이스를 만들고자 할 때
등이다. 1의 경우는 델리게이트 의 형태를 생각하면 된다. 즉 어떤 객체가 그 자신은 구현하지 않지만 델리게이트에게 위임하여 실행할 어떤 기능이 필요하다고 하면 프로토콜로 선언하고, 델리게이트가 그 프로토콜을 따르도록하여 실제 기능의 실행을 델리게이트에서 처리하도록 할 때 프로토콜을 사용한다.
여기서 실제 메소드의 선언을 해당 객체가 해주고, 다른 객체는 구현만한다는 측면에서 2, 3의 경우는 이해할 수 있을 것이다.
다른 객체가 구현해줄 인터페이스 선언하기
클래스 인터페이스나 카테고리는 특정한 클래스가 구현해야 하는 기능을 선언해준다. 프로토콜은 이와 반대로 (알 수 없을지도 모르는) 다른 객체가 구현해 줄 기능을 선언하는 것이다. 따라서 프로토콜은 단순히 메소드 선언의 묶음으로 구성된다. 예를 들어 어떤 이벤트를 받아 이를 처리해야 할 때, 대충 다음과 같은 모양의 메서드들이 필요할 수 있다.
-(void)mouseDown:(NSEvent *) theEvent;
-(void)mouseDragged:(NSEvent *) theEvent;
-(void)mouseUp:(NSEvent *) theEvent;
이 메소드를 해당 객체에서 직접 구현해도 되지만, 다른 객체가 그 작업을 대신 받아서 하도록 하려면 이를 프로토콜로 선언하고, 다른 객체가 그 프로토콜을 따르도록 하면 되는 것이다.
이 방식은 메소드의 선언부를 클래스 상속관계에서 완전히 분리시키게 된다. 프로토콜에서 선언된 메소드들은 어딘가에서 구현되겠지만, 이를 구현할 메소드의 클래스는 무엇이어도 상관없다. 따라서 전혀 다른 클래스 들이라도 같은 프로토콜을 따른다면 같은 메소드를 가지고 있다고 생각할 수 있다. (프로토콜은 어떤 메소드들의 선언이므로) 같은 프로토콜을 따르는 클래스들은 상속관계에서 서로 관련이 없다고 하더라도 기능적인 측면으로 분류할 때 유사한 그룹으로 묶을 수 있다.
다른 객체가 구현할 메소드
앞서 언급했지만 다른 객체에게 작업을 위임하거나 (델리게이트) 다른 객체로부터 정보를 받아와야 하는 경우 (데이터소스)에는 보통 해당 객체에게 메시지를 보내어 이를 처리하면 된다. 일련의 메시징은 양방향이므로, 이 때 메시지를 보내는 객체는 이 메시지를 받을 객체의 헤더 파일을 임포트하여 수신 객체에게 보낼 메시지의 셀렉터를 알 수 있다.
하지만 이를 수신할 객체가 아직 만들어지지 않은 상황이라면, 인터페이스 파일을 임포트할 수가 없다. 따라서 이 경우에는 아직 만들어지지 않은 객체가 받을 메시지를 정할 수 없게된다. 이 때 프로토콜을 사용하면 수신 객체의 헤더를 임포트하지 않고서도 수신 객체에게 메시지를 보낼 수 있게 된다. 즉 어떤 객체가 특정한 프로토콜을 따른 다는 것은 그 프로토콜에서 선언한 메소드를 구현하고 있다는 의미이므로 그 객체의 클래스가 뭔지 몰라도 (따라서 내부에 구현된 메소드가 뭔지 몰라도) 프로토콜에 선언된 메소드는 가지고 있다고 간주하게 되는 것이다.
만약 어떤 객체에게 helpOut: 이라는 메시지를 보내어 처리할 일이 있다고 한다면 다음과 같이 처리하게 될 것이다.
-setAssistant:anObject
{
assistant = anObject;
}
만약 이 객체를 assistant라는 프로퍼티라 생각한다면,
-setAssistant:anObject
{
assistant = anObject;
}
-(BOOL)doWork
{
...
if ( [self.assistant respondToSelector:@selector(helpOut:)]) {
[self.assistant helpOut:self];
return YES;
}
return NO;
}
위와 같이 해당 객체가 helpOut: 이라는 메시지에 응답할 수 있는지 확인한 다음 메시지를 보내게 된다. 하지만 이 객체가 특정한 프로토콜을 따른다면 이러한 검사 절차 없이 바로 호출할 수 있다.
이것의 단적인 예가 델리게이트이다. 보통 기본적으로 우리가 작성하는 코드에서는 델리게이트 입장에서 코드를 쓴다. 대표적으로 테이블 뷰 컨트롤러가 그 예이다. 데이터소스나 델리게이트 프로토콜을 따르는 객체를 만들어 그 프로토콜의 필수 메소드를 구현해주고 있는 것이다. 반대로 델리게이트에게 일을 시키는 객체에서는 다음과 같은 코드들을 볼 수 있다. 먼저 헤더 파일에서는 프로토콜을 선언하고, 그 프로토콜을 따를 델리게이트를 선언한다.
// SomeClassNeedsDelegate.h
import <Foundation/Foundation.h>
@protocol MyClassDelegateProtocol
-(void)helpOut:(id)sender
@end
@interface SomeClassNeedsDelegate : NSObject
@property (nonatomic, weak) id<MyClassDelegateProtocol> delegate;
@end
구현 부분에서는 델리게이트에게 메시지를 보낼 수 있다. 프로퍼티로 설정한 델리게이트는 만약 이 객체를 생성하거나 참조하는 쪽에서 델리게이트를 지정할 것이고, (하지 않았다면 nil) 따라서 바로 메시지를 보내도 문제는 없다.
[self.delegate helpOut:self]
델리게이트가 되는 객체는 인터페이스를 선언할 때 (내부든 외부든 무관하다) 이 프로토콜이 선언된 헤더를 임포트하고 인터페이스 시작 지점에 다음과 같이 쓴다.
// another object that will be the delegate of 'SomeClassNeedsDelegate'
import "SomeClassNeedsDelegate.h"
@interface MyAnotherClass : NSObject <MyClassDelegateProtocol>
<# 클래스 인터페이스 선언 #>
이와 관련한 자세한 내용은 다음 절에서 설명하겠다.
프로토콜 정의 방법
프로토콜은 헤더파일의 인터페이스 영역과 별개의 영역을 만들어 메소드의 선언을 하게 된다.
@protocol 프로토콜이름 : 상속받을 프로토콜
- 메소드 이름
@end
다음은 프로토콜 선언의 예시이다.
@protocol MyProtocol : NSObject // 프로토콜은 다른 프로토콜을 상속할 수 있다.
-(void)requiredMethod;
@optional
-(void)anOptionalMethod;
-(void)anotherOptionalMethod;
@required
-(void)anotherRequiredMethod;
@end;
@optional 키워드 다음에 나오는 메소드들은 모두 선택적으로 구현하면 되는 메소드들이며, 이런 키워드가 없는 경우에는 모두 필수적으로 구현해야 하는 메소드로 인식한다. @optional 키워드로 선언한 메소드들 뒤에 다시 필수 메소드를 추가로 선언하려면 @required 키워드를 다시 써주면 된다.
프로토콜 따르기
객체가 어떤 프로토콜을 따른다고 할 때에는 다음과 같은 절차를 따른다.
- 프로토콜이 선언된 인터페이스 파일을 임포트한다. 이 파일은 프로토콜에 선언된 메소드를 필요로하는 객체의 헤더 파일인 경우가 많으나, 별도의 인터페이스 파일에 프로토콜을 선언할 수도 있다. 많은 프로토콜이 혼재하는 프로젝트의 경우라면, 별도의 프로토콜 인터페이스 파일을 따로 설정하는 것이 편리할 수 있다.
- 인터페이스 블럭 시작줄에 <프로토콜이름> 이라고 꺾은 괄호로 둘러싸고 프로토콜 이름을 명시해준다.
- 해당 프로토콜에서 필수로 선언한 메소드들을 구현한다.
Formatter라는 클래스가 Formatting, Prettifying 이라는 두 개의 각각 다른 프로토콜을 따른다고 할 때 다음과 같이 인터페이스를 작성하면 된다.
@interface Formatter : NSObject <Formatting, Prettifying>
<# 인터페이스 선언 #>
@end
또한, 객체를 선언할 때 그 객체가 특정 프로토콜을 따르고 있음을 명시할 때 (프로토콜을 따른 객체를 아직 작성하지 않아서 모를 때 사용함) 다음과 같이 타입 명 뒤에 프로토콜 명을 사용할 수 있다.
Formatter <Formatting> *aFormatterObject;
id<Prettifying> *aPrettifier;
비정규 프로토콜
비정규 프로토콜은 카테고리의 형식으로 프로토콜을 대체하는 방법이다. 코코아의 거의 모든 객체들은 NSObject를 상속받으므로, NSObject의 카테고리로 메소드들을 선언하면 별도의 프로토콜을 따른다는 명시 없이도 해당 메소드를 선언한 것과 같은 효과를 낼 수 있다. 이 방법은 모든 메소드가 선택적일 때 사용하면 명시적인 코드를 조금 더 줄일 수 있는 효과가 있다.
아직 선언되지 않은 프로토콜
만약 어떤 프로토콜 A에서 사용하는 타입이 B라는 프로토콜을 따라야 하는데, 이 B에 대한 프로토콜이 아직 작성되지 않았다면 프로토콜이 선언된 인터페이스 파일조차 임포트하지 못하는 경우가 있다.
이럴 때는 @class ClassName; 과 마찬가지로 @protocol someProtocolName; 과 같이 이런 프로토콜이 있을 것이라고 컴파일러에게 알려줄 수 있다. 이는 두 개의 프로토콜이 서로를 동시에 참조하는 경우에 사용하여 컴파일러가 에러를 내지 않도록 할 수 있다.
@property / @synthesize / @dynamic
출처
http://blog.outsider.ne.kr/604
다른 언어와 마찬가지로 Objective-C에서도 객체의 멤버변수를 외부에서 접근하도록 하려면 접근자 메서드(accessor method)인 getter와 setter를 만들어주어야 합니다. Objective-C에서 클래스는 @interface와 @implementation의 2부분으로 나누어지는데 @property는 @interface부분과 연결되고 @synthesize와 @dynamic은 @implementation부분과 연결됩니다. @property와 @synthesize는 preprocessor macro이라서 Xcode에서 Build and Run을 실행했을 때 prewritten/preformatted 코드블락으로 교체됩니다.
@property
//header file
@interface Example : NSObject
{
float value;
}
- (float)value;
- (void)setValue:(float)newValue;
위와같이 프로퍼티에 대한 getter와 setter 메서드를 선언해 줄 수 있습니다. 하지만 이 접근자 메서드를 만들어주는 것은 항상 같은 형태로 반복되는 지루한 작업이기 때문에 Objective-C 2.0에서부터는 @property 지시어를 사용해서 getter와 setter에 대한 코드를 자동으로 생성할 수 있도록 해줍니다.
//header file
@interface Example : NSObject
{
float value;
}
@property float value;
정확한 문법은 @property(attributes [, attribute2, ...]) type name;의 형태이고 앞의 코드에서 6,7라인의 코드대신에 위처럼 @property 지시어를 사용하면 자동으로 앞의 코드로 변환해 줍니다. 위에서 보는 것처럼 getter의 이름은 "프로퍼티이름"이 되고 setter의 이름은 "set프로퍼티이름"이 됩니다. 만약 getter의 이름을 바꾸려면 @property(getter=getValue) float value;와 같이 써주면 getter를 getValue 함수를 써서 사용할 수 있습니다. 프로퍼티를 IB아웃렛으로 지정하려면 @property (nonatomic, retain) IBOutlet NSButton *myButton;와 같이 IBOutlet 식별자를 사용할 수 있습니다.
(attributes [, attribute2, ...])부분에서 사용이 가능한 속성들은 아래와 같습니다.
- getter=getterName - getter의 이름을 getterName로 지정합니다.
- setter=setterName - setter의 이름을 setterName로 지정합니다.
- readwrite - 기본동작으로 getter와 setter를 모두 만듭니다. Mutually exclusive로 readwrite합니다.
- readonly - getter만 만듭니다. Mutually exclusive로 readwrite합니다. 값을 할당하려고 하면 컴파일 오류가 발생합니다.
- assign - 기본동작이며 setter가 간단한 할당을 사용합니다.(예 location = where;) 객체를 소유할 필요가 없을때 사용합니다.
- retain - assign과 비슷하지만 레퍼런스 카운트를 증가시킵니다. Mutually exclusive로 assign과 copy합니다. 포인터객체를 할당할 경우에는 외부에서 객체가 릴리즈되어 파괴된 객체를 참조하는 문제를 막기 위해서 클래스가 멤버객체를 소유하도록 레퍼런스카운트를 증가시킵니다.(이전 값을 release 합니다.)
- copy - 할당하는데 객체의 복사본을 사용합니다. Mutually exclusive로 assign과 retain합니다. 포인터객체의 경우 레퍼런스의 값이 바뀌어 프로퍼티의 값이 바뀌는 걸 막기 위해 setter에서 복사본을 만들어서 할당하며 copy를 사용하려면 NSCopying 프로토콜을 구현한 객체에서만 유효합니다.
- nonatomic - 엑세서들을 non-atomic으로 지정합니다. 멀티프로세서 환경해서는 지정해줘야 합니다. 이는 Mutually exclusive락으로 접근자 메서드를 보호하지 말라고 지시하는 것입니다. atomic이 기본동작입니다.
@synthesize
@property를 사용해서 프로퍼티들에 대한 getter와 setter를 선언했으면 @implementation에서 실제 코드를 추가해 주어야 합니다. @property를 사용한 것은 단지 컴파일러가 @implementation에서 getter와 setter메서드가 작성되었다는 것을 기대하도록 하는 것입니다.
//module file
@implementation Example
-(float) value
{
return value;
}
-(void) setValue: (float) newValue
{
value = newValue;
}
@end
위와 같이 실제 구현코드를 작성해주어야 합니다. 이는 아래코드처럼 @synthesize 지시어를 사용하면 다음과 같이 작성해 줄 수 있습니다.
//module file
@implementation Example
@synthesize value;
@end
@dynamic
@dynamic 지시어는 @synthesize대신에 사용할 수 있으며 getter와 setter메서드가 클래스 자신에 의해서 구현되지 않고 (슈퍼클래스같은)다른 어딘가에 구현되어 있다고 알려주어 getter/setter가 구현되어 있지 않아도 컴파일러 경고를 받지 않도록 해줍니다.
Super class :
//header file
@property (nonatomic, retain) NSButton *someButton;
//module file
@synthesize someButton;
Sub class :
//header file
@property (nonatomic, retain) IBOutlet NSButton *someButton;
//module file. @synthesize 를 대신한다
@dynamic someButton;
위와 같이 작성함으로써 someButton에 대한 구현책임이 델리게이트 되었음을 의미합니다. 혹은 CoreData의 NSManagedObject 클래스처럼 접근자 메서드들이 컴파일타임이 아닌 런타임시의 제공되는 경우에도 컴파일타임에 오류가 나지 않도록 하기 위해서 @dynamic 지시어를 사용할 수 있습니다.
@selector
출처
http://blog.daum.net/iamuzooin/33
이 강좌는, C 언어(ISO/IEC 9899:1990)를 이해하고 있는 것이 전제입니다.
출처 :: http://wisdom.sakura.ne.jp/programming/objc/index.html
메소드의 내부 표현
Objective-C 컴파일러는 메소드에 특별한 이름을 컴파일시에 내부 표현으로 변환합니다. 이, 메소드의 내부 표현을 셀렉터라고 부르고, 메세지의 송수신의 뒤에서는, 이 셀렉터가 교환되고 있습니다. 메소드를 특정짓기 위한 내부 표현에 대해서는 컴파일러에 의존하는 문제이며, 개발자가 알아야 할 범위가 아닙니다. 개발자에게 있어서 중요한 것은, 이 셀렉터를 SEL 형태로서 취급할 수 있다고 하는 사실입니다.
메소드가 어떠한 데이터에 변환되어 어떻게 식별되고 있을까는 문제가 아닙니다. 그러나, Objective-C는 이 내부 표현을 SEL 형태의 변수로서 취급하고 있습니다. 즉,SEL 형태의 변수에는, 메소드명을 식별하기 위해서 컴파일러가 할당한 특수한 코드를 저장합니다.
메소드를 특정하는 실렉터는 @selector 컴파일러 지시문을 이용해 취득할 수 있습니다.
@selector ( method )
method 에는, 실렉터를 취득하고 싶은 메소드의 이름을 지정합니다. 지정한 메소드의 이름이 존재할지 어떨지는, 메소드를 호출할 때, 실행시에 판정되기 때문에, 컴파일시에는 알 수 없을 것입니다.
그럼, 취득한 셀렉터의 값을 SEL 형태의 변수에 저장해서 어떻게 이용할 수 있는 것입니까. 실렉터가 메소드를 별도로 알고 있으므로 함수에서 사용하는 포인터처럼, 동적으로 메소드를 지정하는 방법이지요. 셀렉터로부터 메소드를 호출하는 기능을 제공하는 것은 루트 클래스입니다.
Object 클래스에는,SEL 형태의 값을 받는 perform 메소드가 선언되고 있습니다. 이 메소드는 인수로 받은 셀렉터는 특정한 메소드를 실행합니다.
- perform:(SEL)aSel;
- perform:(SEL)aSel with:anObject;
- perform:(SEL)aSel with:anObject1 with:anObject2;
aSel 에 호출하는 메소드의 셀렉터를 지정합니다. anObject,anObject1,anObject2 에는 메소드에 건네주는 인수를 지정할 수 있습니다.
#import <stdio.h>
#import <objc/Object.h>
@interface Test : Object
- (void)Write;
@end
@implementation Test
- (void)Write {
printf("I am the bone of my sword.\n");
}
@end
int main() {
id obj;
SEL method;
obj = [Test new];
method = @selector(Write);
[obj perform:method];
return 0;
}
이 프로그램에서는,Write 메소드를 나타내는 셀렉터를 SEL 형태의 변수 method 에 보존하고 있습니다. 그리고, Test 클래스의 인스턴스 obj 에 perform 메세지를 method를 인수로서 송신합니다. perform 메소드는 주어진 셀렉터로부터 실행해야 할 메소드를 산출해, 실행합니다. 이것을 잘 이용하면, 실행시에 호출해야 할 메소드를 상황에 따라 바꾸는 프로그램을 구현할 수 있습니다.
@private / @public
출처
http://blog.daum.net/iamuzooin/30
Objective-C 접근제어
이 강좌는, C 언어(ISO/IEC 9899:1990)를 이해하고 있는 것이 전제입니다.
출처 :: http://wisdom.sakura.ne.jp/programming/objc/index.html
인스턴스 변수의 공개 범위
클래스는 구조체와는 다른 역할이 있기 때문에, 데이터형의 제공으로서의 클래스라는 것은 존재해야 하는 것이 아닙니다. 그러나 RGB 형식의 색을 표현하기 위한 클래스나 좌표나 사이즈를 나타내는 클래스이면, 메세지로부터 값을 얻는등의 처리는 약간 장황해서, 직접 인스턴스 변수와 교환할 수 있는 편이 좋다고 생각하는 개발자도 있겠지요.
원칙으로서는, 클래스의 인스턴스 변수에 외부로부터 액세스 가능해야만 한 것은 아니고, 디폴트에서는 밖으로부터 인스턴스 변수에 액세스 할 수 없습니다. 기본적으로 인스턴스 변수를 선언하는 클래스의 메소드 이외로부터 인스턴스 변수가 조작되면, 변수에 보존되고 있는 값의 정합성을 유지할 수 없게 되기 때문입니다. 외부로부터 인스턴스 변수에 접근하려면 , 우선, 인스턴스 변수의 접근제어를 명확하게 하지 않으면 안됩니다.
인스턴스 변수의 접근제어은, 공개를 나타내는 public, 비공개를 나타내는 private, 서브 클래스로부터의 액세스를 허가한다 protected로 나누어집니다. public은 클래스의 외부나 서브 클래스를 포함해 모든 장소로부터 인스턴스를 통해 액세스 되는 변수를 나타냅니다. private는, 반대로 인스턴스 변수를 선언한 클래스의 메소드로부터 접근 가능한 변수입니다. 그리고,protected는 변수를 선언한 클래스와 그 클래스의 서브 클래스로부터 접근 할 수 있는 변수를 나타냅니다.
선언하는 인스턴스 변수에 이러한 접근제어를 지정하려면 컴파일러 지시문을 이용해 @public, @private, @protected 를 인스턴스 변수 선언 부분의 사이에 삽입합니다. 이러한 컴파일러 지시문이 쓰게 되면 그 이후에 선언된 인스턴스 변수는 모두, 지정된 접근제어로 설정됩니다. 접근제어가 지정되어 있지 않은 경우에 인스턴스 변수는 protected 로 설정됩니다.
@interface A : Object
{
int z; //protected
@public
int a; //public
@protected
int b; //protected
@private
int c; //private
int d; //private
}
@end
이와 같이, 접근제어를 지정한 후에 선언된 인스턴스 변수는, 그 접근제어를 가집니다. 접근제어를 지정하는 컴파일러 지시문의 출현 순서, 출현 회수에 제한은 없습니다.
public 으로 공개되고 있는 변수에, 클래스의 외부로부터 인스턴스를 통해 액세스 하려면 , 클래스형의 포인터 변수로부터 -> 연산자를 사용해 인스턴스 변수를 지정합니다. 이것은, 구조체에의 포인터로부터 멤버 변수에 액세스 하는 방법과 같습니다. 인스턴스 변수에의 액세스는 컴파일시에 형태 체크가 필요하기 위해 id 형태의 변수로부터 액세스 할 수 없습니다.
#import <stdio.h>
#import <objc/Object.h>
@interface A : Object
{
@public
int a;
@protected
int b;
@private
int c;
}
- (id)initWithA:(int)a int:(int)b int:(int)c;
- (void)WriteA;
@end
@interface B : A
- (void)WriteB;
@end
@implementation A
- (id)initWithA:(int)a int:(int)b int:(int)c {
self->a = a;
self->b = b;
self->c = c;
return self;
}
- (void)WriteA {
printf("[A Write Method, a=%d, b=%d, c=%d]\n", a , b , c);
}
@end
@implementation B
- (void)WriteB {
printf("[B Write Method, a=%d, b=%d]\n", a , b);
}
@end
int main() {
B * objb = [[B new] initWithA:1000 int:100 int:10];
printf("[main() scope, a=%d]\n" , objb->a);
[objb WriteB];
[objb WriteA];
[objb free];
return 0;
}
이 프로그램으로 선언하고 있는 A 클래스는, public 인스턴스 변수 a, protected 인스턴스 변수 b, private 인스턴스 변수 c 를 선언하고 있습니다. 그리고, 이것들에 액세스 할 수 있는 것을 증명하기 위해서,main() 함수로부터 B 형태의 포인터 변수 objb를 통하고, 공개되고 있는 변수 a 에 objc->a 그렇다고 하는 형태로 액세스 하고 있습니다.
A 클래스를 상속받는 서브 클래스 B에서는 WriteB라는 메소드를 선언하고, 이 메소드의 구현으로 공개되고 있는 변수 a 라고 서브 클래스로부터의 액세스가 허가되고 있는 변수 b를 표시합니다. 다만, 서브 클래스로부터도 private 변수에 액세스 할 수 없기 때문에 c를 표시할 수 없습니다. 모든 변수에 액세스 할 수 있는 것은, 인스턴스 변수를 선언한A 클래스 뿐입니다. A 클래스의 메소드 WriteA 에서는, 모든 인스턴스 변수를 표시하는 것이 가능합니다.
참고문서
- http://trans.onionmixer.net/mediawiki/index.php?title=PHPUnit_Manual
- http://www.roccoangeloni.it/wp/2008/08/07/objectivec-on-ubuntu-linux/
- 우분투 리눅스 ( Ubuntu Linux ) 에서 objective c 환경 구성하기
- Objective-C 에 대한 좋은글 모음 : http://seungngil.tistory.com/category/C/Objective%20C
Notes
- ↑ ISBN 9788991268654 or ISBN 899126865X 프로그래밍 오브젝티브 C 2.0/Programming in Objective C 2.0. 인사이트 출판.
- ↑ garbage collector. gentoo 에서는 boehm-gc 라는 패키지입니다.
- ↑ 사실 c에서 extern 이라고하면 function 을 사용하기 위한거지 class 문법도 없는 상태에서 존재하는거라 이 개념이 맞기는 하다.
- ↑ 미묘하지만 gcc 에서는 그냥 동작은 합니다. 일단은 말이죠... 이게 어떻게 된건지 확인은 해봐야 할거같기는 합니다.