외규장각 도서 환수 모금 캠페인
BLOG main image
분류 전체보기 (45)
컴퓨팅환경 (18)
프로그래밍 (18)
놀이 (2)
잡담 (7)
«   2008/12   »
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31      
10,199 Visitors up to today!
Today 0 hit, Yesterday 28 hit
daisy rss
meet me at me2DAY
나눔글꼴 내려받기
tistory
'2008/12'에 해당되는 글 9건
2008/12/31 17:59

Mac OS X 10.5부터 NSOperationQueue를 이용하여 Thread Pool 기반의 Worker Thread Model을 아주 쉽게 만들 수 있게 되었다. 이것은 priority, dependency까지 설정할 수 있기 때문에 범용으로 사용할 수 있을 뿐 아니라 인터페이스도 매우 쉽게 되어 있다.

내가 필요한 것은 백그라운드 쓰레드 하나가 그냥 기다리고 있다가 특정 메소드 호출만 처리해 주는 단순한 것인데, NSOperationQueue는 좀 뒤뚱한 짓을 하고 있었다. MaxConcurrentOperationCount를 1로 주었는데, 내부적으로 쓰레드풀을 관리한답시고 고작 쓰레드 하나를 만들었다가 없앴다가 반복하는 등...

하지만, 제대로 만들기 위해 메세지큐 만들고, condition lock하고 signal 보내고... 싫었다. 그래서, NSRunLoop을 이용해서 정말 간단하게 하나 만들어 보았다.

#import <Foundation/Foundation.h>


@interface WorkerThread : NSObject
{
    NSThread *thread;
    NSLock   *lock;
}

- (void)start;
- (void)stop;

- (void)performSelector:(SEL)aSelector target:(id)target withObject:(id)anObject;

@end

@implementation WorkerThread

- (id)init
{
    self = [super init];

    if (self)
    {
        thread = [[NSThread alloc] initWithTarget:self selector:@selector(main) object:nil];
        lock   = [[NSLock alloc] init];
    }

    return self;
}

- (void)dealloc
{
    [thread release];
    [lock release];
    [super dealloc];
}

- (void)main
{
    NSAutoreleasePool *mainPool;
    NSAutoreleasePool *loopPool;

    mainPool = [[NSAutoreleasePool alloc] init];

    [lock lock];

    [self performSelector:@selector(self) onThread:thread withObject:nil waitUntilDone:NO];

    while (![thread isCancelled])
    {
        loopPool = [[NSAutoreleasePool alloc] init];
        [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
        [loopPool release];
    }

    [lock unlock];

    [mainPool release];
}

- (void)start
{
    [thread start];
}

- (void)stop
{
    [thread cancel];

    [lock lock];
    [lock unlock];
}

- (void)performSelector:(SEL)aSelector target:(id)target withObject:(id)anObject
{
    [target performSelector:aSelector onThread:thread withObject:anObject waitUntilDone:NO];
}

@end

NSRunLoop은 input source로부터 input을 기다리게 되는데, 만약, input source가 없다면 run loop이 종료된다. 48번째 라인은 자기자신에게 self를 호출하는 의미없는 라인처럼 보이지만, 이 때 message send에 대한 input source가 등록된다.

한가지 깔끔하지 못한 것은 48번째 라인에 의해 등록된 input source를 해제할 방법이 없다는 것이다. 그렇기 때문에, 플래그로 1초마다 쓰레드를 종료해야할 지 말지 결정한다. 만약, 이 쓰레드가 프로그램이 종료할 때까지 멈출 필요가 없다면 while문을 없애고 다음과 같이 바꿔도 무방하다.

[[NSRunLoop currentRunLoop] run];

 

이 글은 스프링노트에서 작성되었습니다.

2008/12/29 19:21

다음 코드를 실행한 결과는 무엇일까?

#import <Foundation/Foundation.h>


@interface Sequence : NSObject
{
    int value;
}

@end

@implementation Sequence

- (Sequence *)next
{
    value++;
    return self;
}

- (NSString *)description
{
    return [NSString stringWithFormat:@"%d", value];
}

@end


int main(int argc, const char *argv[])
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    Sequence          *seq  = [[Sequence alloc] init];

    NSLog(@"%@", seq.next.next.next);

    [seq release];
    [pool drain];
    return 0;
}

참고사항: GCC 4.0과 GCC 4.2가 서로 다른 결과를 출력함

 

이 글은 스프링노트에서 작성되었습니다.

2008/12/26 11:54

헤더파일과 소스파일을 서로 왔다갔다 자주 하는 경우에 그것도 여러 소스파일을 걸쳐서 작업할 경우 매번 find-file 하는 것이 상당히 귀찮을 수 있다. 헤더파일이 열려있을 때에는 소스파일을 소스파일이 열려있을 때에는 헤더파일을 열어주는 명령으로 Xcode 에디터에는 Go to Counterpart 라는 것이 있다. 이와 똑같은 것을 Emacs에서도 만들어봤다.

c-mode-base-map에 F12키로 바인드해 놓고 써보니 편한 것 같다.

(defun find-counterpart-file ()
  (interactive)
  (if (string-equal (file-name-extension buffer-file-name) "h")
      (let ((filebase (file-name-sans-extension buffer-file-name)))
        (cond
         ((file-exists-p (concat filebase ".c"))
          (find-file (concat filebase ".c"))
          )
         ((file-exists-p (concat filebase ".cpp"))
          (find-file (concat filebase ".cpp"))
          )
         ((file-exists-p (concat filebase ".cc"))
          (find-file (concat filebase ".cc"))
          )
         ((file-exists-p (concat filebase ".m"))
          (find-file (concat filebase ".m"))
          )
         ((file-exists-p (concat filebase ".mm"))
          (find-file (concat filebase ".mm"))
          )
         )
        )
    (let ((filebase (file-name-sans-extension buffer-file-name)))
      (if (file-exists-p (concat filebase ".h"))
          (find-file (concat filebase ".h"))
        )
      )
    )
  )

 

이 글은 스프링노트에서 작성되었습니다.

2008/12/24 19:06

이번에는 Emacs에서 Xcode project를 build하는 것이다.

다음의 emacs lisp 코드를 .emacs 파일안에 넣어놓으면 M-x xcode-compile이나 F10키로 바로 컴파일이 가능하다.

(defvar xcode-compile-sdks nil)
(defvar xcode-compile-sdk-history nil)

(dolist (line
         (split-string
          (with-temp-buffer
            (call-process "xcodebuild" nil t nil "-showsdks")
            (buffer-string))
          "\n" t)
         )
  (let ((comps (split-string line "-sdk " t)))
    (if (> (length comps) 1)
        (add-to-list 'xcode-compile-sdks (car (last comps)))
      )
    )
  )

(defun xcode-compile ()
  (interactive)
  (let ((command "xcodebuild -activeconfiguration -activetarget"))
    (setq command
          (concat
           command
           (if xcode-compile-sdks
               (let ((default-sdk (or (car xcode-compile-sdk-history) (car xcode-compile-sdks))))
                 (concat
                  " -sdk "
                  (completing-read
                   (format "Compile with sdk (default %s): " default-sdk)
                   xcode-compile-sdks
                   nil
                   t
                   nil
                   'xcode-compile-sdk-history
                   default-sdk
                   )
                  )
                 )
             )
           (let ((dir ".") (files nil))
             (while
                 (progn
                   (setq files (directory-files dir nil "\\.xcodeproj\\'"))
                   (and (not (string-equal "/" (expand-file-name dir))) (null files))
                   )
               (setq dir (concat (file-name-as-directory dir) ".."))
               )
             (unless (null files) (concat " -project " (file-name-as-directory dir) (car files)))
             )
           )
          )
    (compile (read-string "Compile command: " (concat command " ")))
    )
  )

(define-key global-map [f10] 'xcode-compile)

이 글은 스프링노트에서 작성되었습니다.

2008/12/24 11:45

C/C++/Objective-C 가 모두 섞여있는 프로젝트의 경우 소스코드 파일은 .c .cpp .m 등의 확장자로 언어를 확실히 구분할 수 있기 때문에 파일을 열때 Emacs가 해당하는 언어의 모드를 자동으로 선택해준다. 하지만, 헤더파일의 경우는 대부분 언어의 구분없이 .h 를 사용하기 때문에 확장자로만 언어를 판단하기 어렵다.

이를 해결하는 괜찮은 방법이 Opening a header file in Emacs » Brett Hutley’s Site에 소개되어 있어 가져왔다. 물론 코드를 좀 수정했다.

아이디어는 간단하다 .h 파일을 열때 같은 이름의 소스파일이 뭐가 있는지 확인해서 모드를 선택하는 방식이다.

(defun my-header-file-mode-hook ()
  (if (string-equal (file-name-extension buffer-file-name) "h")
      (let ((filebase (file-name-sans-extension buffer-file-name)))
        (cond
         ((file-exists-p (concat filebase ".c"))
          (c-mode)
          )
         ((file-exists-p (concat filebase ".cpp"))
          (c++-mode)
          )
         ((file-exists-p (concat filebase ".cc"))
          (c++-mode)
          )
         ((file-exists-p (concat filebase ".m"))
          (objc-mode)
          )
         ((file-exists-p (concat filebase ".mm"))
          (objc-mode)
          )
         (t
          (objc-mode)
          )
         )
        )
    )
  )
(add-hook 'find-file-hook 'my-header-file-mode-hook)

21라인에는 소스파일을 찾지못했을 때 사용할 모드를 넣어준다. 헤더파일만 있는 경우가 이에 해당될 것이다. 그리고, 헤더파일과 소스파일이 다른 디렉토리에 분리되어 존재하는 경우 파일 찾는 부분을 수정해야 할 것이다.

 

이 글은 스프링노트에서 작성되었습니다.

2008/12/23 19:00

Xcode와 Emacs를 함께 사용할 때 가장 불편한 것부터 하나씩 해결해 볼려고 한다.

오늘의 주제는 Emacs에서 breakpoint를 만드는 것. 방법은 Emacs의 AppleScript 실행기능을 이용하는 것이다.

사용한 AppleScript는 다음과 같다.

tell application "Xcode"
	set theFile to "/Users/han9kin/Prototype/MyDocument.m"
	set theLine to 25

	repeat with aProj in projects
		repeat with aFile in file references of aProj
			if full path of aFile = theFile then
				repeat with aBreak in file breakpoints of aProj
					if full path of file reference of aBreak = theFile and line number of aBreak = theLine then
						set aFile to null
						exit repeat
					end if
				end repeat
				if aFile is not equal to null then
					tell aProj
						set aBreak to make new file breakpoint with properties {line number:theLine}
						set file reference of aBreak to aFile
						set enabled of aBreak to true
					end tell
				end if
				exit repeat
			end if
		end repeat
	end repeat
end tell

.emacs 파일에 다음과 같이 넣어주면 F9를 눌렀을 때 해당 라인에 breakpoint를 설정해준다.

(defun xcode-break ()
  (interactive)
  (do-applescript
   (format
    (concat
     "tell application \"Xcode\" \r"
     "    set theFile to \"%s\" \r"
     "    set theLine to %d \r"
     "    repeat with aProj in projects \r"
     "        repeat with aFile in file references of aProj \r"
     "            if full path of aFile = theFile then \r"
     "                repeat with aBreak in file breakpoints of aProj \r"
     "                    if full path of file reference of aBreak = theFile and line number of aBreak = theLine then \r"
     "                        set aFile to null \r"
     "                        exit repeat \r"
     "                    end if \r"
     "                end repeat \r"
     "                if aFile is not equal to null then \r"
     "                    tell aProj \r"
     "                        set aBreak to make new file breakpoint with properties {line number:theLine} \r"
     "                        set file reference of aBreak to aFile \r"
     "                        set enabled of aBreak to true \r"
     "                    end tell \r"
     "                end if \r"
     "                exit repeat \r"
     "            end if \r"
     "        end repeat \r"
     "    end repeat \r"
     "end tell \r"
     )
    buffer-file-name
    (line-number-at-pos)
    ))
  )
(define-key global-map [f9] 'xcode-break)

AppleScript가 굉장히 난해한 언어라 애플의 김정현부장님 도움을 많이 받았다. (감사합니다. 가까이 계셨으면 식사라도 한끼 대접해 드리고 싶었으나... 제 마음 아시죠? ㅋㅋ)

오늘은 요기까지

 

이 글은 스프링노트에서 작성되었습니다.

2008/12/19 15:04

가끔 메소드가 어떻게 호출되고 있는지 로그로 남겨서 확인해 보고 싶을 때가 있다. 매번 확인하고 싶은 메소드 시작부분마다 메소드 이름을 로그로 출력해 주는 코드를 끼워넣는 것은 좀 귀찮은 일이다. 그래서 매크로를 한번 만들어 봤다.

Log.h

#define LogMethod()   LogMethodBody(self, _cmd, __FILE__, __LINE__)
#define LogFunction() LogFunctionBody(__PRETTY_FUNCTION__, __FILE__, __LINE__)

Log.m

#import <Foundation/Foundation.h>


void LogMethodBody(id self, SEL _cmd, const char *file, int line)
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    if (self == [self class])
    {
        NSLog(@"%@:%d: +[%@ %@]",
              [[NSString stringWithCString:file encoding:NSUTF8StringEncoding] lastPathComponent],
              line,
              NSStringFromClass(self),
              NSStringFromSelector(_cmd));
    }
    else
    {
        NSLog(@"%@:%d: -[%@ %@] (%p)",
              [[NSString stringWithCString:file encoding:NSUTF8StringEncoding] lastPathComponent],
              line,
              NSStringFromClass([self class]),
              NSStringFromSelector(_cmd),
              self);
    }

    [pool release];
}

void LogFunctionBody(const char *function, const char *file, int line)
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    NSLog(@"%@:%d: %s()",
          [[NSString stringWithCString:file encoding:NSUTF8StringEncoding] lastPathComponent],
          line,
          function);

    [pool release];
}

이렇게 하면 삽질을 좀 줄일 수 있다. 흐흐.

이전 포스트에서 Objective-C 메소드의 경우 method signature라는 것이 있다는 설명을 했었다. 그래서 Objective-C Runtime API를 이용하면 메소드 호출시 넘어온 인자들까지 확인가능하다. 이 부분은 생략하도록 하겠다. ㅋㅋ

 

이 글은 스프링노트에서 작성되었습니다.

2008/12/17 23:52

먼저 다음과 같은 메소드가 있다.

- (void)drawImage:(NSImage *)image withFrame:(NSRect)frame inView:(NSView *)view
{
    NSSize size = [image size];
    NSRect rect = NSMakeRect(0, 0, size.width, size.height);

    [view lockFocus];
    [image drawInRect:frame fromRect:rect operation:NSCompositeCopy fraction:1.0];
    [view unlockFocus];
}

"메소드"란 위의 소스 그 자체라 말할 수 있다. 즉, 메소드 이름, 인자 갯수, 각 인자들의 데이터타입, 리턴값의 데이터타입, 그리고 그 내용으로 구성된다. 위의 소스는 이 모든 것들을 모두 표현하고 있다. 그럼, 이 메소드의 구성 요소들을 용어로 정리해 보자.

  설명 예제 값
selector 메소드의 이름 drawImage:withFrame:inView:
method signature (type encoding) 인자들 및 리턴값의 타입정보 v@:{_NSRect={_NSPoint=ff}{_NSSize=ff}}@
implementation 실행코드 함수 포인터

자, 이제 이 메소드를 다음과 같이 호출해 보자.

    [myCell drawImage:myImage withFrame:NSMakeRect(0, 0, 32, 32) inView:myView];

이렇게 메소드를 호출하는 것을 Objective-C에서는 "메세지를 보낸다(send message)"라고 한다. 메세지는 다음과 같은 네가지로 구성된다.

  설명 예제 값
receiver 메세지를 받는 객체 myCell
selector 호출될 메소드의 이름 drawImage:withFrame:inView:
arguments 호출시 넘어가는 인자들 myImage, NSMakeRect(0, 0, 32, 32), myView
return value 호출후 넘어오는 값 void

Objective-C 런타임 및 Cocoa 프레임워크에서는 위의 용어들을 다음과 같이 추상화해 놓았다.

  Objective-C Runtime Cocoa Framework
selector SEL, @selector() SEL, @selector()
method signature (type encoding) const char * NSMethodSignature
implementation IMP IMP
send message objc_msgSend() NSInvocation,
[NSObject performSelector:withObject:]

 

이 글은 스프링노트에서 작성되었습니다.

2008/12/17 17:15

Objective-C는 C++에 비하면 상당히 간단한 객체지향언어라 할 수 있다. 특징은 C++이 메소드 바인딩(method binding)시에 기본적으로 정적 바인딩(static binding)을 하고 가상함수(virtual function)을 이용해 동적 바인딩(dynamic binding)을 할 수 있는데 반해, Objective-C는 기본적으로 동적 바인딩을 하고, 정적 바인딩은 지원하지 않는다는 것이다. (정적 바인딩을 지원하지 않기 때문에 보다 간단하다라고 주장하는 것인가? 설마... ㅋㅋ)

하지만, Objective-C가 C의 superset이라는 점에서 불가능할 것이 있겠는가? 어디 Objective-C에서도 정적 바인딩을 해볼까나? 흐흐.

#import <Foundation/Foundation.h>


@interface BaseObject : NSObject
- (void)sayHello;
@end

@interface MyObject : BaseObject
@end


@implementation BaseObject
- (void)sayHello
{
    NSLog(@"BaseObject: hello.");
}
@end

@implementation MyObject
- (void)sayHello
{
    NSLog(@"MyObject: hello.");
}
@end


void sayHelloDynamic(BaseObject *obj)
{
    [obj sayHello];
}

void sayHelloStatic(BaseObject *obj)
{
    IMP sayHello;

    sayHello = [[BaseObject class] instanceMethodForSelector:@selector(sayHello)];
    sayHello(obj, @selector(sayHello));
}

int main()
{
    BaseObject *obj = [[MyObject alloc] init];

    sayHelloDynamic(obj);
    sayHelloStatic(obj);

    [obj release];

    return 0;
}

뭐, 엄밀히 말하면 정말로 정적바인딩을 한 것은 아니라 할지 몰라도, 흉내는 내었다. 이런 걸 사용할 일이 없을 것이지만, 굳이 할려면 이렇게 하면 가능은 하다는 것이 요점이다. 이뭥미?

 

이 글은 스프링노트에서 작성되었습니다.

prev"" #1 next