BLOG ARTICLE Xcode 2/Objective-C | 3 ARTICLE FOUND

  1. 2008.09.25 Objective-C 코딩 스타일 6
  2. 2008.03.17 Objective-C class의 특징 2
  3. 2007.05.14 C/C++ 사용자를 위한 간단한 Objective-C 소개 17

새로운 언어를 익힐 때는 보통 해당 언어의 코딩스타일 가이드라인을 찾아 보거나, 원 제작자(사)의 문서나 예제에 사용된 소스의 코딩 스타일을 사용하는 경우가 많습니다.

저는 아직까지 Objective-C에 익숙하지 않아서 시작할 때 부터 애플에서 많이 사용하고 권장하는 형식으로 습관을 들일려고 하고 있습니다. 그런데 사소한 것이지만 애플의 샘플코드나 Xcode에서 생성해 주는 소스코드를 보면 조금씩 차이를 보이며, 이는 같은 코드 내에서도 스타일이 조금씩 다른 경우를 볼 수 있습니다.

* 스타일 1
/Developer/Examples/QuickTime/QTKit/QTKitPlayer/MovieDocument.m
-  (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename
{
    if (isDir)
    {
        return YES;
    }
}
'{'과 '}'이 한라인을 차지하는 스타일은 Xcode의 C++을 사용하는 카본에서도 많이 볼 수 있습니다. 샘플 코드중에도 MS VC++의 MFC를 보는 듯한 코드를 자주 볼 수 있습니다. 아마 애플의 기존 C++ 프로그래머가 작성하지 않았나 하는 추측이 듭니다.

하지만 애플의 일반적인 코코아에 관련된 문서나 Xcode에서 생성해 주는 코드에는 이런 스타일은 흔하지 않은 것 같습니다.

* 스타일 2
/Developer/Examples/Quartz Composer/Applications/ImageFX/AppController.m
-  (BOOL)  performDragOperation:(id<NSDraggingInfo>)sender
{
    if(_sourceRef) {
        if(imageRef = CGImageSourceCreateImageAtIndex(_sourceRef, 0, NULL)) {
        }
    }
}
메소드의 '{'는 한 라인을 차지하고 메소드 내에서의 '{'는 같은 라인에서 사용하고 있습니다. if문 바로 뒤에 '('를 공백없이 사용하는 것과 타입(BOOL)과 함수명 사이에 공백을 둔 것을 제외하면 흔히 볼 수 있는 스타일 같습니다. 많은 예제와 Xcode가 생성한 코드, 그리고 많이들 보시는 Cocoa Programming for mac os x의 소스에서도 이 스타일을 사용하고 있습니다.

* 스타일 3
-  (void)setTitle:(NSString *)newTitle {

    if (instanceVar != newTitle) {
        [instanceVar release];

        instanceVar = [newTitle copy];
    }
}
요즘 나오는 예제들과 코드에서는 주로 위와 같이 사용하는 경우가 많은 것 같습니다. 위의 코드는 Coding Guidelines for Cocoa 문서의 뒷부분에서 발췌한 것인데, 아마 애플에서는 저런 스타일을 지향하는 것 같습니다.

제가 주의 깊게 보는 곳은 메소드의  '-' 뒤에 공백이 오는지와 반환타입과 메소드명 사이에 공백이 오는지의 여부인데요. 위와 같이 '-' 뒤에는 공백을 두고 나머지는 공백 없이 쓰는 것이 더 많은 것 같습니다. 
 
개인적으론 2번째 스타일에다 if 뒤에 공백을 하나 두는 방식이 편한 것 같은데, 더 알아 보면서 남들이 많이 쓰는 형식으로 쓸려고 합니다.

Xcode의 샘플이나 생성된 코드의 스타일에 조금씩 차이를 보이는 것이 애플이 아주 소소한 부분은 개발자들의 취향에 맡기는 것인지, 각자의 고집이 반영된 것인지는 잘 모르겠습니다.

코코아에서 Objective-C의 코딩스타일 가이드라인은 ADCCoding Guidelines for Cocoa 문서를 참조하실 수 있습니다.

'Xcode 2 > Objective-C' 카테고리의 다른 글

Objective-C class의 특징  (2) 2008.03.17
C/C++ 사용자를 위한 간단한 Objective-C 소개  (17) 2007.05.14
AND

Objective-C class의 사용에서 눈여겨 볼만한 점들이 세가지 정도 있었습니다.  바로  Protocol, Category와 NSObject의 poseAsClass입니다.

Protocol 자바, C#의  interface와 유사하며 선언된 메소드들은 프로토콜을 사용하는 클래스에서 반드시 구현되어야 합니다. 사용하는 이유는 C++의 abstract class와 비슷하지만 Objective-C, 자바, C#과 같이 다중상속을 지원하지 않는 언어에서 다중상속의 단점은 버리고 장점은 취하기 위해서입니다. (이에 관한 자세한 설명은 디자인 패턴에 관한 책이나 deadly diamond of death로 구글 검색에서 찾으실 수 있습니다.)

Category는 서브클래스를 구현하지 않더라도 이미 구현되어 있는 클래스에 새로운 메소드들을 추가할 수 있는 방법을 제공합니다. category의 메소드들은 해당 클래스의 변수와 메소드를 사용할 수 있으며 메소드 이름이 같을 경우에는 오버라이드되니 주의하셔야 합니다. Categtory는 서브클래스를 만드는 것 보다 더 간단한 방법으로 문제를 해결할 수 있습니다.

PoseAsClass는 가장 흥미로운 부분입니다.  짧은 제 영어로 번역하자면 '사칭'이 가까운 것 같습니다. 상위 클래스로 선언되었지만 하위 클래스의 PoseAsClass를 이용하면 그 인스턴스는 하위 클래스로 동작하게 됩니다.

아래의 소스를 실행해 보시면 확인해 보실 수 있습니다.

#import <Foundation/Foundation.h>

/** Protocol **/
@protocol MyData
-(void) print;
@end

/** MyNumber class **/
@interface MyNumber: NSObject <MyData>
{
    int number;   
}

-(void) setNumber: (int)n;
-(void) print;
@end

@implementation MyNumber;
-(void) setNumber: (int)n
{
    number = n;
}

-(void) print
{
    NSLog(@"MyNumber:print > %d", number);
}
@end

/** Category **/
@interface MyNumber (NewPrint)
-(void) print;
-(void) printPlus;
@end

@implementation MyNumber (NewPrint)
-(void) print
{
    NSLog(@"NewPrint:print > %d", number);   
}

-(void) printPlus
{
    NSLog(@"NewPrint:printPlus > %d", number + 1);
}
@end

/** Posing **/
@interface MyNumberMinus: MyNumber
{
}
-(void) print;
@end

@implementation MyNumberMinus;
-(void) print
{
    NSLog(@"MyNumberMinus:print > %d", number - 1);
}
@end

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

    // insert code here...
    [MyNumberMinus poseAsClass: [MyNumber class]];
   
    MyNumber *number = [[MyNumber alloc] init];
   
    [number setNumber: 5];
   
    [number print];
    [number printPlus];
       
    [number release];
   
    [pool release];
    return 0;
}

사용자 삽입 이미지
실행결과는 좌측과 같습니다.


[MyNumberMinus poseAsClass: [MyNumber class]];
이 라인은 MyNumberMinus는 앞으로 MyNumber 클래스로 사칭하고 다닌다는 의미입니다. 그래서 아래의 MyNumber *number = [[MyNumber alloc] init]; 선언은 MyNumberMinus *number = [[MyNumberMinus alloc] init]; 와 동일한 의미로 해석될 수 있습니다. 이 라인을 주석처리하고 빌드 후에 결과를 비교하시면 쉽게 이해하실 수 있습니다.

poseAsClass가 유효하기 위해서는 상위클래스가 생성되기 전에 호출되어야 합니다.

'Xcode 2 > Objective-C' 카테고리의 다른 글

Objective-C 코딩 스타일  (6) 2008.09.25
C/C++ 사용자를 위한 간단한 Objective-C 소개  (17) 2007.05.14
AND

Objective-C는 1980년대 Stepstone사의 Brad Cox와 Tom Love에 의해 기존의 C에 SmallTalk의 객체지향 장점을 추가하여 만들어진 멋진 언어라고 합니다. 하지만 주위에선 별로 사용자를 볼 수가 없습니다.

개발환경이 특정 하드웨어나 OS로 한정되어 있고, 가장 많은 사용자를 가진 윈도우즈용 어플리케이션을 제작할 수 없어서(제가 아는 한에서 이며, 확실치 않습니다.) 인 것 같습니다.

보기에도 언뜻 Objective-C 소스를 보면 C와는 상관없는 전혀 별개의 언어로 보여, C/C++ 사용자가 접근이 힘들어 보입니다. 이유는 C에다 클래스와 메시지 전달 방식의 메소드 등 추가된 문법 때문입니다.

하지만 이 부분은 쉽게 배울 수 있으며, 어디선가 C를 알고 객체지향 프로그램에 대한 이해가 있다면 2시간 이면 Object-C를 배울 수 있다는 내용을 본적이 있습니다. 저같이 C/C++을 잘하지 못하는 사람도 대충 배워 가는 것을 보면, 틀린 얘기는 아니라고 생각됩니다.

그래서 C/C++, 또는 윈도우즈에서 VC++을 사용해 보시고, Objective-C 경험이 전혀 없으신 분들을 위해 이 블로그의 제목처럼 맛만 보실 수 있도록 부실한 내용을 시작 하겠습니다.
 
우선 Xcode에서 cocoa application 프로젝트를 만들면, Xcode에서 자동으로 생성해주는 main.m를 확인해 보겠습니다. (만드는 방법은 튜토리얼 분류쪽의 포스트에 있습니다.)

우선 Objective-C에서는 헤더파일은 .h로 같은 이름을 사용하지만, 소스파일명에는 .c 대신에 .m 확장자를 사용함을 알 수 있습니다. 소스를 보겠습니다.

#import <Cocoa/Cocoa.h>

int main(int argc, char *argv[])
{
    return NSApplicationMain(argc,  (const char **) argv);
}

MS처럼 윈도우로 넘어 오면서 C/C++ 고유의 main이란 이름을 가만히 놔두지 않는 것에 비하여, 전형적이고 친숙한 C의 main이 보입니다.

import만 생소하고 모두 C와 동일합니다. import는 Objective-C에서 추가된 전처리 명령어로 include와 유사하지만, 중복될 경우에는 한번만 include합니다. import대신 include의 사용도 가능합니다. 하지만 중복되어 인클루드될 경우를 대비해 헤더파일에
#ifndef _HEADER_NAME_H
#define _HEADER_NAME_H
.........
#endif  //_HEADER_NAME_H
과 같은 처리가 필요하지만 import를 사용하게 되면, 중복 오류를 신경 쓸 필요가 없습니다.

우선 테스트를 위해 아래와 같이 C코드를 추가하고 컴파일을 해봅니다.
void copy_str(char* des, char* src)
{
    strcpy(des, src);
}

int main(int argc, char *argv[])
{
    char buffer[12];
    char* ptr;
    
    copy_str(buffer, "Hellow!");
    ptr = buffer;
    
    printf("str: %s\n", ptr);
    
    return NSApplicationMain(argc,  (const char **) argv);
}

main을 봐도 알수 있듯이 C문법이 오류없이 컴파일 되며, 실행을 하게 되면 빈 윈도우와 함께 로그 윈도우에 str: Hello!를 출력합니다.

이 예를 들은 이유는 Objective-C가 C와 전혀 별개의 언어가 아닌 C언어의 확장으로 봐도 무방할 것 같습니다. 위에 언급한 바와 같이 C언어 또는 C++ 사용이 가능하면, 작은 노력으로도 쉽게 Objective-C를 사용할 수 있습니다.

이제 다른 소스를 확인해 보겠습니다. 아래는 Xcode에서 제공되는 샘플소스의 헤더 파일과 소스파일의 일부분입니다.

@interface DotView : NSView {
    float radius;
}
// Standard view create/free methods
- (id)initWithFrame:(NSRect)frame;
- (void)dealloc;
@end

#import <Cocoa/Cocoa.h>
#import "DotView.h"

@implementation DotView

- (void)mouseUp:(NSEvent *)event {
    NSPoint eventLocation = [event locationInWindow];
    center = [self convertPoint:eventLocation fromView:nil];
    [self setNeedsDisplay:YES];
}
@end

위에 테스트로 작성된 소스와는 다르게 객체를 사용하기 때문에, 전혀 C와는 관련 없는 소스로 보입니다.

C++이나 Java등을 사용하신 분들은 class의 선언과 구현인지 대충 짐작이 가시겠지만, C 문법이라고 보기엔 import, -, @, [], : 등 많은 부분이 눈에 거슬립니다. 아래에서 확연히 다른 점을 간단히 다루어 보겠습니다.

1) "@" 예약어

우선 "@"으로 시작되는 것은 Objective-C에서 추가된 예약어 입니다.

위에서 예를 보면 클래스의 선언부분은  @interface, @end로 구현부분은 @implementation, @end의 사이에 위치합니다.

또한 "hello"는 char* 형의 문자열을 의미하지만, @"hello"는 NSString에서 사용하는 문자열(참고로 @""는 아스키 코드만 가능하며, 한글은 UTF8로 처리해야 합니다.)을 의미하는 것과 같이 기존 C의 문법과 구별이 필요할 때에 사용한다고 보시면 됩니다..

2) 함수 선언

헤더파일을 보면 클래스의 메소드 선언 시 C++/Java와는 달리 클래스 선언 구역({})의 외부에서 선언 됩니다.

위의 - (id)initWithFrame:(NSRect)frame; 선언을 예로 들어 보겠습니다.

함수 앞에는 "-" 표시가 있는데 이는 인스턴스 메소드를 나타내며, "+"일 경우에는 클래스 메소드를 나타냅니다. "+"는 C++/Java의 static 맴버함수와 유사하여 인스턴스의 생성 없이 바로 사용할 수 있는 메소드입니다.

(id) 는 반환될 타입입니다. ()로 처리된다는 것만 제외하고 C와 동일합니다. 참고로 id는 모든 오브젝트를 가리키는 포인터입니다.

":"는 다음에 인자를 의미하며 인자가 ":(타입)이름"과 같이 나온다는 것을 의미합니다. 인자가 2개 이상일  경우에는 스페이스로 구분하며 아래와 같이 선언 합니다.

- (NSPoint)convertPoint:(NSPoint)aPoint fromView:(NSView *)aView;
보면 fromView라는 것이 혼돈을 주는데 인자의 별칭(alias)이라고 생각하시면 되고, 실제 호출 시에는 아래와 같이 사용합니다.

[self convertPoint:eventLocation fromView:nil];
이렇게 함으로써 소스코드는 길어 지지만, 메소드와 인자의 용도를 명확하게 합니다.

여기에는 없지만 변수앞에 사용하는 IBOutlet과 메소드에 사용하는 IBAction란 예약어가 있습니다. 이는 인터페이스 빌더에서 참조를 위한 것으로 변수와 메소드 타입에 영향을 주지 않습니다.  VC의 AFX_ 류로 생각하시면 됩니다.


3) 메소드 호출

center = [self convertPoint:eventLocation fromView:nil];
소스파일에 있는 위의 코드를 C++로 변경하면 아래와 같습니다.

center = this->convertPoint(eventLocation, NULL);

Objective-C에서는 메소드 호출(정확히는 메세지 전달)시 에는 [object method]의 형태로 사용됩니다. [object method:[object method]]와 같이 중첩해서 사용이 가능하며, 초기에는 혼돈이 오지만 자주 보면 object->method(object->method)와 같이 친숙해 집니다.


4) 프레임워크

라이브러리와 유사한 의미로 프레임워크라는 용어를 사용합니다. cocoa 프로젝트를 생성하면 Xcode 좌측의 Groups & Files에서 FrameWorks란 폴더를 찾을 수 있습니다.

 하단의 Other Frameworks를 보면 AppKit.framework와 Foundation.framework를 확인하실 수 있습니다. 이는 VC에서 MFC 클래스 라이브러리와 유사합니다.

Foundation은 NSObject, NSString, NSArray등의 기본적인 클래스들로 구성이 되어 있으며, AppKit은 NSWindow, NSButton, NSImage등 사용자 UI에 관련된 클래스들로 구성되어 있습니다.
 
사용자 삽입 이미지
좌측과 같이 이 두 framework 아래의 headers를 클릭해 보시면 cocoa 개발을 위한 기본적인 클래스 목록들을 확인하실 수 있습니다.






5) 기타

- Objective-C는 C++/Java와는 달리 class에 생성자/소멸자가 없지만, 이를 대치해서 사용할 수 있는 메소드와 이벤트가 있습니다.

- retain이라는 사용 카운터를 사용하여 오브젝트가 메모리에서 삭제되는 시기를 결정합니다. 인스턴스가 추가(alloc)되면 retain이 증가되고, 사용이 완료되면 release라는 메소드로 retain을 감소 합니다. retain이 0이 되면 삭제됩니다.

- Nib 파일 - NeXT Interface Builder의 약자로 오브젝트, 클래스, 리소스, 컨넥션, UI등의 정보와 파일을 가지고 있는 cocoa 어플리케이션에서는 매우 중요한 역활과 의미를 가지고 있는  파일 입니다.

이상으로 마치며... 프로그래밍 언어를 한번 정도 다루어 보신 분들, 특히 C++, Java라면 Objective-C는 쉽게 접근할 수 있는 언어입니다. C에 객체지향을 더했다는 측면에서, C++과 만들어진 이유와 나온 시기도 비슷하여 서로 비교해 보는 것도 재밌습니다. Cocoa와 MFC, VC++과 Xcode도 그렇고요.

앞으로 맥사용자들이 많이 늘어, C++ 사용자 처럼 Objective-C 사용자들이 많이 늘었으면 하는 바램입니다.
 

'Xcode 2 > Objective-C' 카테고리의 다른 글

Objective-C 코딩 스타일  (6) 2008.09.25
Objective-C class의 특징  (2) 2008.03.17
AND