Window-based Application은 XCODE에서 아이폰용 어플리케이션 프로젝트 템플릿을 선택할때 코코아터치 클래스기반으로 작성할 수 있는 가장 기본적인 템플릿이다. XCode 프로젝트 화면을 처음으로 접했을때 Window-based Application과 View-based Application 의 차이점이 도대체 무엇인가 하고 느낀적이 있을 것이다. Window-based Application로 우선 프로젝트를 생성한후 뷰를 하나 추가하면 View-based Application로 선택한 것과 똑같이 만들어 지는데 수작업을 통하여 뷰를 넣어 봄으로써 그 차이점을 알아보자.

<그림1> 템플릿 선택

 그림1과 같이 Windows-based Application을 선택한후  Sample이라 프로젝트명으로 프로젝트를 생성하면 다음과 같은 파일이 생성된다. 

<그림2> 자동으로 생성된 파일

주목해서 보아야 하는 것은 다음 3가지이며 3개의 파일이 한세트이다..

  • SampleAppDelegate.h
  • SampleAppDelegate.m
  • MainWindow.xib
  • 여기서 뷰를 하나 추가하면 View-based Application 이 되는데 수동으로 View를 추가해 보기로 하겠다.

    상단의 메뉴에서 File > New File 을 선택한다. Cocoa Touch Class 를 선택한 다음 UIViewController subclass를 선택한다. 그리고 아래의 옵션에서 With XIB for user interface 를 같이 첵크해서 클래스파일을 생성할때 XIB 파일도 같이 자동으로 추가되도록 한다. 클래스 이름을 SampleViewController 라고 입력한 다음 Finish 버튼을 누른다.

    추가되어진 다음 3가지이며 역시 3개의 파일이 한세트이다..

  • SampleViewController.h
  • SampleViewController.m
  • SampleViewController.xib
  • View Class 파일을 추가했지만 파일만 생성되어 있을뿐 아직까진 메인 윈도우와 어떠한 연결 고리도 가지고 있지 않다. 이제 SampleAppDelegate.h 과 SampleAppDelegate.m 파일을 수정해서 뷰를 수작업으로 연결을 시켜야 된다.

    SampleAppDelegate.h파일을 열어보자

    @class SampleViewController;


    @interface SampleAppDelegate : NSObject <UIApplicationDelegate> {

        UIWindow *window;

        SampleViewController *viewController;

    }


    @property (nonatomic, retain) IBOutlet UIWindow *window;

    @property (nonatomic, retain) IBOutlet SampleViewController *viewController;


    @end

    클래스 정의부 안에 파란색으로 칠해진 부분이 새로 추가된 부분이다. SampleViewController를 선언해주고 있다.

    SampleAppDelegate.m 파일을 수정하자..

    #import "SampleAppDelegate.h"

    #import "SampleViewController.h"


    @implementation SampleAppDelegate


    @synthesize window;

    @synthesize viewController;



    #pragma mark -

    #pragma mark Application lifecycle


    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    

        

        // Override point for customization after application launch.


        // Add the view controller's view to the window and display.

        [self.window addSubview:viewController.view];

        [self.window makeKeyAndVisible];


        return YES;

    }


    - (void)dealloc {

        [viewController release];

        [window release];

        [super dealloc];

    }


    어플리케이션의 실행준비를 완료되었을때 호출되는 didFinishLaunchingWithOptions 에서 AddSubView 함수를 호출해서 ViewController의 View를 추가한다. 그리고 윈도우가 종료될때 window를 release 해주기 전에 viewController를 종료하는 부분을 추가해주어야 한다. 윈도에서 뷰를 추가하는 위의 코드는 기타 다른 뷰를 추가하는 코드에서 자주 응용되는 코드이니 눈여겨 볼 필요가 있다.

    아울렛 연결

    SampleAppDelegate.h의 내용중 viewController는 인터페이스 빌터에서 작성하는 MainWindow.xib 파일에서 참조할 수 있도록 IBOutlet 참조자가 추가 되어 있다. 즉, 인터페이스 빌터에서 연결작업을 한번더 해주어야 한다.

     @property (nonatomic, retain) IBOutlet Sample2ViewController *viewController;

    Groups & Files 에서 MainWindow.xib 를 더블클릭하면 인터페이스 빌더가 열려진다.


    중간의  ViewMode를 변경하면 Type를 알기쉽게 보여준다.


    4개의 객체중 Sample App Delegate 를 한번 클릭한 후 두번째 탭 우화살표모양의 Connections 화면을 본모습이다. Sample App Delegate는 현재 SampleAppDelegate 자기 자신을 가르키며 단 멤버 함수 viewController와 window를 볼수 있다. 이 두가지 멤버는 IBOutlet 지시자를 표기했기 때문에 인터페이스 빌더에서 아래의 화면과 같이 Outlet 으로 인지하고 있다. 현재 window 랑 MainWindow.xib 파일에 Window 랑 연결되어 있으나 새로 추가한 ViewController 는 연결이 되어 있지 않다.


    라이브러리 윈도우에서 Classes 를 열어서 찾아보면 위에서 작성한 뷰 클래스의 SampleViewController 를 찾을 수 있다. SampleViewController를 클릭한후 마우스로 드랙해서 MainWindow.xib 화면에 추가하면 아래와 같이 Sample view Controller가 추가된다.
     

    MainWindows.xib의 Sample App Delegate 를 클릭한후 Connections 창을 열고 viewController 의 오른쪽 동그라미에 마우스를 오버 시키면 + 모양으로 바꾼다. 이것을 드래그해서 다시 왼쪽 윈도우 MainWindow.xib에서 Sample View Controller 객체 윗부분에 드롭시키면 아래의 그림과 같이 viewController 는 라이브러리에서 추가한 Sample View Controller 와 연결이 완료된다.


    이제 인터페이스 빌더에서 작업이 끝났기 때문에 Xcode 화면으로 복귀하기 전에 반드시 저장을 시켜야 된다.
    이렇게 수작업한 코드는 프로젝트 템플릿중 Sample이란 프로젝트명으로 View-based Application을 선택한 프로젝트랑 똑같다.
    Posted by tklee

    댓글을 달아 주세요

    잠자기

    해킨토시 2011.01.26 01:29
    잠자기는 해킨의 거의 마지막 코스이다...
    NullPowerManagement와 dsdt 로 제대로된 잠자기를 구성해놓지 않는다면 힘들다...
    최대의 고비인 자기 시스템의 dsdt를 추출해서 편집해야만 된다....그래서 다들 포기하고 사는 분들이 많다 ㅎㅎ

    약간의 쉬운 방법이 있다면 SleepEnabler.kext 를 Extras/Extension 에 설치하는 방법!!

    SleepEnabler는 커널버전에 32/64비트 버전에 아주 민감하기 때문에 제대로된 버전을 받아야 된다.

    다음은 10.6.6에서 비교적(?) 잘 동작하는 SleepEnabler 이다.
    32비트 전용이다.

    SleepEnabler.kext.zip

    단, 제대로 잔다고해서 전원이 꺼져있는 상태에서 제대로 깨어난다는 보장은 없다. ㅋㅋㅋ
    뭐가 문제인지 몰라도 P6X58D-E 이놈의 보드 자긴 자도 다시 깨어나는걸 본적이 없다.
    깨어나면서 화면이 쓰레기가 가득 쩝...

    '해킨토시' 카테고리의 다른 글

    잠자기  (0) 2011.01.26
    윈도우에서 HFS+ 읽고/쓰기  (0) 2011.01.07
    Snow Leopard NTFS 쓰기 가능하게 하기  (0) 2011.01.07
    Posted by tklee

    댓글을 달아 주세요

    MacDrive8을 그냥 설치하면 된다. 윈도우7 64비트도 완벽지원

    http://thepiratebay.org/torrent/5823868/macdrive_8.0.6.52-Keygen.included.zip

    '해킨토시' 카테고리의 다른 글

    잠자기  (0) 2011.01.26
    윈도우에서 HFS+ 읽고/쓰기  (0) 2011.01.07
    Snow Leopard NTFS 쓰기 가능하게 하기  (0) 2011.01.07
    Posted by tklee

    댓글을 달아 주세요

    맥에서는 NTFS 파티션을 사용하기 위해 여러가지 방법이 있다.
    Snow Leopard는 NTFS 파티션을 read 만 가능하게 마운트 되기 때문에 쓰기/수정/삭제가 불가능하다.
    디폴트를 ReadOnly로 해 놓은 이유는 모르겠지만 아래와 같이 살짝 수정해주면 쓰기도 가능해진다.

    1. 터미날을 열고 Application/Utilities/Terminal

    2. sudo mv /sbin/mount_ntfs /sbin/mount_ntfs.orig

    3. sudo vi /sbin/mount_ntfs
    #!/bin/sh
    /sbin/mount_ntfs.orig -o rw "$@"

    4. sudo chown root:wheel /sbin/mount_ntfs
       sudo chmod 755 /sbin/mount_ntfs

    Reboot!

    이제 보니 별로 추천하고 싶지 않은 방법이다. 간혹  NTFS 파티션을 망치는 경우가 있어서 듀얼부팅되어 있는 경우 윈도로 부팅할 경우 매번 디스크 스캔을 하게 된다. 시간 엄청 잡아먹어서...

    '해킨토시' 카테고리의 다른 글

    잠자기  (0) 2011.01.26
    윈도우에서 HFS+ 읽고/쓰기  (0) 2011.01.07
    Snow Leopard NTFS 쓰기 가능하게 하기  (0) 2011.01.07
    Posted by tklee

    댓글을 달아 주세요


    메소드를 클래스간에서 공유한다

    프로토콜이라고 하는 용어는, 비교적 네트워크용어로서 이용되고 있습니다만, Objective-C 에 한해서는, 복수의 클래스에서 실장되는 동일한 이름의 메소드를 공유하기 위한 메소드의 선언을 가리킵니다. Java 언어나 C# 언어에서는 인터페이스로 불리는 개념에 가까운 존재입니다.

    프로토콜이 이용되는 것은, 프로그램의 설계상의 문제로, 클래스가 특정의 메소드를 구현하고 있는 것을 보장하기 위한 수단으로서 사용됩니다. 예를 들면, 특정의 라이브러리 사양에 근거한 클래스는 지정한 프로토콜에 준거시키지 않으면 안 된다는관계를 쌓아 올릴 수 있습니다. 프로토콜이 제공하는 것은 선언된 메소드만으로, 메소드의 구현이 어떠한 처리를 실행할까는 자유입니다.

    프로토콜을 선언하려면 @protocol 컴파일러 지시문을 이용합니다. 이 안에서, 프로토콜이 규약으로서 지정하는 메소드를 선언합니다.

    @protocol 프로토콜명 <친프로토콜1 , ...>
    프로토콜 본체
    ...
    @end

    프로토콜은, 클래스의 계승 관계와 같이 부모프로토콜을 지정해 계승할 수 있습니다. 프로토콜의 계승에 대해서는 잠시 후에 자세하게 해설합니다만, 프로토콜의 계승은 단순하게 부모프로토콜이 정하는 메소드에, 새로운 메소드를 추가하는 의미 밖에 가지지 않습니다. 프로토콜명은, 클래스명등과 같이 C 언어의 명명 규칙에 근거해 프로토콜을 식별하기 위한 이름을 지정합니다. 프로토콜 본체에서는, 메소드의 선언만을 실시할 수 있습니다.

    선언한 프로토콜은, 클래스에 채용할 수 있습니다. 프로토콜이 지정되어 있는 클래스는, 그 프로토콜을 채용하고 있다고 부를 수 있습니다. 프로토콜을 채용하고 있는 클래스는, 그 프로토콜로 선언되고 있는 메소드를 반드시 구현하지 않으면 안됩니다. 덧붙여서,  프로토콜로 선언되고 있는 메소드를, 프로토콜을 채용하는 클래스의 선언부에서 재차 선언할 필요는 없습니다. 프로토콜을 클래스에 채용시키려면 , 다음과 같이 클래스를 선언합니다.

    @interface 클래스명 : 슈퍼 클래스명 <프로토콜1, ...>

    프로토콜은 슈퍼 클래스의 지정의 직후에 < > 그리고 프로토콜명을 둘러싸 지정합니다. 슈퍼 클래스는 1 개 밖에 지정할 수 없습니다만, 프로토콜은 콤마 , 그리고 단락지어 복수의 프로토콜을 채용시킬 수 있습니다. 프로토콜을 채용하는 경우, 반드시 프로토콜로 선언되고 있는 메소드를 @implementation 부에서 구현하지 않으면 안됩니다. 즉,  채용하는 것은 그 클래스가 프로토콜로 선언되고 있는 메소드의 구현을 보장하는 것입니다.

    #import <stdio.h>
    #import <objc/Object.h>
    
    @protocol ClassNameToString
    - (id) ToString;
    @end
    
    @interface A : Object 
    {
    	char *name;
    }
    - (id) init;
    - (id) free;
    @end
    
    @interface B : Object 
    @end
    
    @implementation A
    - (id) init {
    	[super init];
    	name = (char *)malloc(255);
    	sprintf(name , "%s . A@%d" , __FILE__ , self);
    	return self;
    }
    - (id) free {
    	free(name);
    	return [super free];
    }
    - (id) ToString { return (id) name; }
    @end
    @implementation B
    - (id) ToString { return (id)"This is Object of B Class"; }
    @end
    
    int main() {
    	id objA = [A new];
    	id objB = [B new];
    	printf("objA = %s\n" , [objA ToString]);
    	printf("objB = %s\n" , [objB ToString]);
    	[objA free];
    	[objB free];
    
    	return 0;
    }

    이 프로그램은, 클래스의 이름을 나타내는 문자열을 돌려준다 ToString 메소드를 선언한다 ClassNameToString 프로토콜을 선언하고 있습니다. 이 프로토콜을 채용한다 A 클래스와 B 클래스는, 반드시 ToString 메소드를 실장하지 않으면 안됩니다. A 클래스와 B 클래스의 계승 관계에서는 무슨 연결도 없습니다만, 프로토콜을 채용하는 것으로, 이러한 클래스에 ToString 메소드가 실장되고 있는 것을 확실히 보장할 수 있습니다.

    실천에서는, 클래스의 실장에 의존하지 않는 완전하게 추상화 된 형태로서 프로토콜이 이용되는 것 외에, 포인터를 사용하지 않고 메소드를 콜백 시키는 방법이라고 해도 이용됩니다. 특히 GUI 환경의 이벤트 처리등에는, 이 프로토콜이 사용되겠지요.

    프로토콜은 형태로서 독립하고 있었습니다만, 형태 선언으로, 형명의 직후에 < > 그리고 프로토콜을 지정하는 것으로, 변수가 지정한 프로토콜을 채용하고 있는 것을 명시적으로 선언할 수 있습니다.

    형명 <프로토콜명> 변수명 ...

    이것은, 함수나 메소드의 인수 선언에서도와 같이 지정할 수 있습니다.

    #import <stdio.h>
    #import <objc/Object.h>
    
    @protocol InstanceListener
    - (void) InstanceFree:(id)object;
    @end
    
    @interface Test : Object
    {
    	id <InstanceListener> listener;
    }
    - (id) init;
    - (id) free;
    - (void)SetInstanceListener:(id <InstanceListener>)l;
    - (id <InstanceListener>)GetInstanceListener;
    @end
    
    @implementation Test
    - (id) init {
    	[super init];
    	listener = NULL;
    	return self;
    }
    - (id) free {
    	if (listener) [listener InstanceFree:self];
    	return [super free];
    }
    - (void)SetInstanceListener:(id <InstanceListener>)l {
    	listener = l;
    }
    - (id <InstanceListener>)GetInstanceListener {
    	return listener;
    }
    @end
    
    @interface WriteInstanceFree : Object <InstanceListener>
    @end
    
    @implementation WriteInstanceFree
    - (void) InstanceFree:(id)object {
    	printf("%X:인스턴스가 해제되었습니다\n" , object);
    }
    @end
    
    int main() {
    	id obj1 = [Test new] , obj2 = [Test new];
    	id <InstanceListener> listener = [WriteInstanceFree new];
    	[obj1 SetInstanceListener:listener];
    	[obj2 SetInstanceListener:listener];
    	[obj1 free];
    	[obj2 free];
    
    	return 0;
    }

    이 프로그램에서는, 인스턴스가 해방된 타이밍에 불려 가는 콜백 전문의 InstanceFree 메소드를 선언한다 InstanceListener 프로토콜을 선언하고 있습니다. Test 클래스에서는, 이 프로토콜을 채용하는 인스턴스를 SetInstanceListener 메소드로부터 설정할 수 있어Test 클래스의 인스턴스가 해방되기 직전에 프로토콜의 메소드를 호출합니다. GUI 의 이벤트 처리는 이것과 같은 원리로 처리되겠지요.

    변수를 선언할 때, 형명의 직후에 프로토콜을 지정하면, 그 오브젝트가 지정한 프로토콜을 채용하지 않으면 안 된다고 하는 것을 명시합니다. 그 때문에,Test 클래스의 listener 인스턴스 변수는, 확실히 목적의 메소드를 콜백 할 수 있습니다.


    프로토콜의 계승

    프로토콜은 클래스와 같이 계승할 수 있습니다. 프로토콜을 계승은, 클래스의 계승과는 성질이 달라, 실제로는 기본이 되는 프로토콜에 새로운 메소드 선언을 추가할 뿐입니다. 또, 슈퍼 클래스는 반드시 1 개 밖에 지정할 수 없었습니다만, 프로토콜은 복수의 프로토콜을 계승시킬 수 있습니다.

    #import <stdio.h>
    #import <objc/Object.h>
    
    @protocol ProtocolA
    - (void) MethodA;
    @end
    
    @protocol ProtocolB
    - (void) MethodB;
    @end
    
    @protocol ProtocolC <ProtocolA>
    - (void) MethodC;
    @end
    
    @interface Test : Object <ProtocolB, ProtocolC>
    @end
    
    @implementation Test
    - (void) MethodA { printf("This is MethodA\n"); }
    - (void) MethodB { printf("This is MethodB\n"); }
    - (void) MethodC { printf("This is MethodC\n"); }
    @end
    
    int main() {
    	id obj = [Test new];
    	[obj MethodA];
    	[obj MethodB];
    	[obj MethodC];
    	[obj free];
    	return 0;
    }

    이 프로그램의 ProtocolC 프로토콜은,ProtocolA 프로토콜을 계승하고 있습니다. 그 때문에,ProtocolC 그럼,MethodA와 MethodC 하지만 선언되고 있다고 생각할 수 있습니다.

    또,Test 클래스는,ProtocolB 와 ProtocolC 를 동시에 채용하고 있는 점에도 주목해 주세요. 선언 밖에 보유하고 있지 않는 프로토콜은 메소드명이 충돌해도 프로그램의 메소드 검색에는 영향이 없기 때문에 가능합니다. 다만, 다루려는 의도의 메소드가 프로토콜의 계승 관계 중에서 충돌했을 경우는 주의할 필요가 있겠지요.

    Posted by tklee

    댓글을 달아 주세요


    클래스에 메소드를 추가한다

    Objective-C 에는 클래스를 복수의 파일에 분할해 선언, 및 정의하기 위한 기능을 제공하고 있습니다. 이것을 카테고리라고 부르고, 카테고리를 이용하는 것으로 클래스의 메소드 선언을 분할할 수 있습니다. 예를 들면, 복수의 개발자가 공동으로, 동시 평행으로 동일 클래스를 구현하는 경우, 각각의 개발자에게는 클래스 가운데, 개발자가 담당하는 메소드를 카테고리에 분할합니다.

    카테고리를 선언하려면 , 반드시 메인이 되는 클래스의 인터페이스 선언이 필요합니다. 클래스의 카테고리를 선언, 및 정의하는 경우는, 다음과 같은 구문을 지정합니다.

    @interfae 클래스명 (카테고리명) { ...

    @implementation 클래스명 (카테고리명) { ...

    여기서 지정하는 카테고리명은, C 언어의 식별자의 명명 규칙에 따릅니다. 카테고리화하는 클래스는 반드시 먼저 메인이 되는 본체가 선언되어 있지 않으면 안됩니다.

    카테고리는 통상의 클래스의 선언이나 정의를 닮아 있습니다만, 인스턴스 변수를 선언할 수 할 수 없기 때문에 주의해 주세요. 카테고리를 선언할 수 있는 것은, 인스턴스 메소드와 클래스 메소드 뿐입니다.

    #import <stdio.h>
    #import <objc/Object.h>
    
    @interface Test : Object
    - (void)WriteA;
    @end
    
    @interface Test (Fate)
    - (void)WriteB;
    @end
    
    @implementation Test
    - (void)WriteA {
    	printf("I am the bone of my sword.\n");
    }
    @end
    
    @implementation Test (Fate)
    - (void)WriteB {
    	printf("몸은 검으로 되어있다\n");
    }
    @end
    
    int main() {
    	id obj = [Test new];
    	[obj WriteA];
    	[obj WriteB];
    	[obj free];
    
    	return 0;
    }

    이 프로그램은 Test 클래스를 메인과 Fate라는 이름을 가지는 카테고리에 분할하고 있습니다. 실행 결과는, 상상대로입니다. Test 클래스의, 분할 떠날 수 있어 모든 카테고리는, 최종적으로 동일한 클래스로서 통합됩니다. 물론, 카테고리는 다른 파일로 선언해도 괜찮습니다. 그 경우, 카테고리를 선언하는 파일은 메인이 되는 클래스를 선언하는 헤더 파일을 인클루드 하고 있을 필요가 있습니다.

    카테고리는, 기존의 완성하고 있는 클래스에 기능을 추가한다고 하는 목적으로 사용할 수도 있습니다. 다만, 인스턴스 변수를 추가하는 것은 할 수 없기 때문에, 기존의 클래스의 역할의 범위로의 확장이라는 것이 됩니다.

    #import <stdio.h>
    #import <objc/Object.h>
    
    @interface Object (Write)
    - (void)Write;
    @end
    
    @implementation Object (Write)
    - (void)Write {
    	printf("I am the bone of my sword.\n");
    }
    @end
    
    int main() {
    	id obj = [Object new];
    	[obj Write];
    	[obj free];
    
    	return 0;
    }

    이 프로그램은 루트 클래스인 Object 클래스에 새로운 카테고리를 추가하고 있습니다. 덧붙여서, 카테고리의 메소드가 기존 클래스의 메소드와 충돌했을 경우, 카테고리의 메소드가 우선되어 기존의 메소드는 은폐 되어 버립니다. 계승에 의한 오버라이드(override)와는 다르기 위해, 은폐 된 메소드를 호출하는 수단이 없어져 버릴테니 주의해 주세요.

    Posted by tklee

    댓글을 달아 주세요


    메소드를 함수로서 호출한다

    실은, Objective-C 의 메소드의 실태는 C 언어의 함수와 같습니다. 평상시는 은폐 되고 있습니다만, 메소드와는 최초의 인수에 자신의 클래스를 참조하는 오브젝트를 받는 함수입니다.

    메소드의 실체가 함수이다고 하는 사실은 C 언어와의 친화성이 지극히 높은 것을 의미하고 있습니다. 순수 C 언어로 기술된 라이브러리로부터 Objective-C 의 메소드를 호출하거나 오브젝트를 이용하는 것도 무리한 이야기는 아닙니다.

    Objective-C 의 메소드는, 항상 IMP 형태로 정의됩니다. IMP 형태는, 헤더 파일로 다음과 같이 정의되고 있습니다.

    typedef id (*IMP)(id, SEL, ...);

    이 정의로부터도 알 수 있듯이, Objective-C에 선언된 모든 메소드는, 암묵적으로 id 형태와 SEL 형태의 인수를 가집니다. 제일 인수는, 메소드를 호출한 오브젝트를 나타내는 변수 self 입니다. 제2 인수는, 이 메소드의 실렉터를 나타내는 변수 _cmd 입니다. 모든 메소드에는, 이러한 숨겨진 인수가 반드시 존재합니다. 그 다음은, 메소드의 선언에 따라 인수가 결정되겠지요.

    C 언어나, 어떠한 이유로 Objectiver-C로부터, 메소드를 함수로서 호출할 필요가 있는 경우, 메소드를 참조하는 함수에의 포인터를 취득하면 좋습니다. 메소드를 참조하는 IMP 형태의 포인터는 Object 클래스의 instanceMethodFor 클래스 메소드, 또는 methodFor 인스턴스 메소드로부터 얻을 수 있습니다.

    + (IMP)instanceMethodFor:(SEL)aSel;

    - (IMP)methodFor:(SEL)aSel;

    aSel 에는 대상 메소드의 IMP, 즉 함수에의 포인터를 취득하고 싶은 실렉터를 지정합니다. 메소드는 aSel로 지정된 실렉터가 특정하는 메소드의 포인터를 돌려줍니다.

    포인터를 취득할 수 있으면, C 언어로부터에서도 인스턴스 메소드를 호출할 수 있게 됩니다. 함수의 포인터로서 직접 호출하기 위해, 메세지 통신보다 고속으로 되는 점도 특징이지요.

    #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;
    	IMP func;
    
    	obj = [Test new];
    	method = @selector(Write);
    	func = [Test instanceMethodFor:method];
    	func(obj , method);
    
    	return 0;
    }

    이 프로그램은 Test 클래스의 인스턴스 메소드 Write 에의 포인터를 취득해, IMP 형태의 변수 func 에 보존하고 있습니다. [Test instanceMethodFor:method] 과 같은 메세지 부분은, [obj methodFor:method] 이라고 해도 의미는 같습니다. 이 메세지식이 돌려주는 IMP 형태의 메소드의 포인터를 사용하고, 직접 메소드를 호출하고 있습니다.

    화상 처리나 멀티미디어 등, 루프 처리의 고속화등이 필요한 경우, 메세지 통신이나 실렉터 통신은 부담이 커집니다. 속도를 고집하는 프로그램이면, 필요에 따라서 IMP 를 취득해, 포인터로부터 메소드를 호출해도 좋을 것입니다.

    Posted by tklee

    댓글을 달아 주세요


    메소드의 내부 표현

    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 메소드는, 주어진 실렉터로부터 실행해야 할 메소드를 산출해 실행합니다. 이 성질을 능숙하게 이용하면, 실행시에 호출해야 할 메소드를 상황에 따라 바꾸는 프로그램을 실현할 수 있습니다.

    Posted by tklee

    댓글을 달아 주세요


    클래스를 나타내는 오브젝트

    변수 선언등에서 클래스명을 지정해, 그 클래스형의 변수를 준비할 수 있었습니다. 그러나, 클래스 메소드를 호출하기 위해서, 메세지식에서 지정한 클래스명은, 실은 클래스의 형태를 나타내고 있는 것은 아닙니다. 메세지식에서 지정하는 것은, 항상 메세지를 송신하는 앞의 오브젝트입니다. 즉, 메세지에서 지정해 있던 클래스명은 오브젝트입니다.

    클래스가 인스턴스를 생성하도록 올바르게 컴파일 된 클래스는, 스스로의 정보를 나타내는 클래스 오브젝트를 보유하고 있습니다. 이 클래스 오브젝트는 Class 형태로서 변수에 보존하는 것도 가능합니다. 클래스 오브젝트가 존재하지 않는 값은 Nil 이라고 하는 정수로 표현됩니다. 통상,Nil은 NULL 와 같게 0 을 나타냅니다.

    클래스 오브젝트를 취득하려면 , 인스턴스가 존재하는 경우는, Object 클래스의 class 인스턴스 메소드의 반환값으로부터 취득합니다. 인스턴스가 존재하지 않는 상태로, 클래스명이 판명되어 있는 경우, 클래스명을 직접 지정해 클래스 오브젝트를 취득할 수도 있습니다. 통상, 클래스명은 이름으로서 인식됩니다만,  메세지 송신지 오브젝트의 지정으로 클래스명을 지정했을 경우에 한해서, 이 클래스의 클래스 오브젝트로서 인식됩니다. 메세지에서, 클래스명으로 지정한 클래스 오브젝트를 돌려주게 하려면 , 클래스 오브젝트에 class 메세지를 송신합니다. 이 메소드는 다음과 같이 선언되고 있습니다.

    - (Class)class;

    이 메소드가 돌려준 값이 Class 형태의 오브젝트입니다. 클래스 오브젝트는 변수에 보존할 수 있기 위해, 클래스 메소드를 호출하는 메세지도, 변수에 보존되고 있는 오브젝트에 따라서 결과가 다른 다양성을 도입할 수 있습니다.

    #import <stdio.h>
    #import <objc/Object.h>
    
    @interface Test : Object
    + (void)Write;
    - (id)init;
    @end
    
    @implementation Test
    + (void)Write {
    	printf("I love you... so please do not love me.\n");
    }
    - (id)init {
    	printf("You can be whatever.\n");
    	return [super init];
    }
    @end
    
    int main() {
    	Class testClass = [Test class];
    
    	[testClass Write];
    	[[testClass new] free];
    	[testClass free];
    
    	return 0;
    }

    이 프로그램의 [Test class] 의 Test는, 실은 클래스형은 아니고 클래스 오브젝트를 메세지식으로 지정하고 있습니다. 메세지식은 Test 클래스의 클래스 오브젝트를 돌려주어, testClass 변수에 오브젝트를 보존하고 있습니다. 이후, 이 testClass 변수는, 메세지식에서 Test 클래스 오브젝트와 같은 의미를 가집니다.

    이 위력은, 그 후,Write 클래스 메소드를 실행하기 위해서 [testClass Write] 메세지식에서 실현될 수 있다고 하는 부분에서 확인할 수 있겠지요. 클래스 오브젝트를 가리키는 변수로부터 Write 메세지를 송신하고 있기 때문에, 이 호출은 지극히 동적인 호출이 됩니다. 물론,alloc이나 new 메세지를 송신하고, 클래스 오브젝트로부터 인스턴스를 작성할 수 있습니다. 변수를 바꿔 넣는 것만으로, 호출하는 클래스 메소드나 생성하는 인스턴스를 변경할 수 있기 위해, 유연한 프로그램을 실현하기 위해서 이용할 수 있습니다.

    Posted by tklee

    댓글을 달아 주세요


    클래스에 직결한 메소드

    인스턴스 변수나 인스턴스 메소드는, 인스턴스가 존재하지 않으면 호출할 수 없습니다. 이것들은, 인스턴스에 관련된 메모리를 개별적으로 할당할 수 있기 위해 당연한 일입니다.

    클래스 메소드는, 반대로 인스턴스에는 관계없이, 클래스 그 자체에 직결하고 있는 메소드입니다. 메소드는 인스턴스와 관련하고 있지 않기 때문에, 사실상 글로벌 함수랑 다름이 없습니다. 다른 점은, 호출할 때 메세지를 사용하고 메세지를 개입시켜 호출할 필요가 있는 점입니다.

    클래스 메소드를 선언하는 것은, 메소드의 선언시로 지정하고 있던 마이너스 기호 - 가 플러스 기호 + 로 바뀌는것 외에 인스턴스 메소드와 같습니다. 다만, 클래스 메소드에는 인스턴스가 존재하지 않기 때문에, 암묵의 self 오브젝트를 사용할 수 없습니다.

    메세지식으로부터 클래스 메소드를 호출하려면 , 인스턴스가 아닌 클래스명을 지정합니다. 지금까지, alloc 클래스 메소드를 사용해 온적이 있으므로, 클래스 메소드의 호출하는 방법에 대해서는 설명 불필요하겠지요.

    #import <stdio.h>
    #import <objc/Object.h>
    
    @interface Test : Object
    + (void)Write;
    @end
    
    @implementation Test
    + (void)Write {
    	printf("I love you... so please do not love me.\n");
    }
    @end
    
    int main() {
    	[Test Write];
    	return 0;
    }

    이 프로그램의 Test 클래스에서 선언되고 있는 Write 메소드는 + 가 지정되어 있기 때문에 클래스 메소드라고 판단할 수 있습니다. 클래스 메소드의 호출에는 인스턴스는 불필요해서 main() 함수에서는 [Test Write] 의 형태로 클래스에 대해서 메세지를 송신하고 있습니다.


    클래스 변수

    인스턴스 메소드에 대해서 클래스 메소드가 존재한다면, 인스턴스 변수에 대해서 클래스에 직결하는 클래스 변수가 있어도 괜찮다고 생각할지도 모릅니다. Java 프로그래머등에는 static 메소드, 정적인 메소드라고 표현하는 것이 알기 쉬울 것입니다.

    실은, Objective-C 에는 클래스 변수는 존재하지 않습니다. 그 클래스의 모든 인스턴스가 공유하는 변수라는 것을 갖고 싶은 경우, 클래스를 선언하는 헤더 파일내에 파일 스코프의 글로벌 변수를 준비해, 이 글로벌 변수를 인스턴스 메소드등에서 액세스 한다고 하는 형태로 클래스 변수를 실현합니다.

    Posted by tklee

    댓글을 달아 주세요