[Objective C] 10 Protocols

프로그래밍/iPhone Dev 2009. 11. 11. 17:47 Posted by galad
Protocols
자바의 인터페이스, C++의 버츄얼 클래스 같은 것
상속 받은 클래스에게 이것은 꼭 구현해야 한다고 규정 짓는 것. 말 그대로 프로토콜.

Printing.h
// 프로토콜 정의
@protocol Printing
-(void) print;
@end


Fraction.h
#import <Foundation/NSObject.h>
#import "Printing.h"

// <> 안이 프로토콜.
// NSObejct를 상속하고, Printing과 NSCopying을 프로토콜로 구현
@interface Fraction: NSObject <Printing, NSCopying> {
    int numerator;
    int denominator;
}

-(Fraction*) initWithNumerator: (int) n denominator: (int) d;
-(void) setNumerator: (int) d;
-(void) setDenominator: (int) d;
-(void) setNumerator: (int) n andDenominator: (int) d;
-(int) numerator;
-(int) denominator;
@end


Fraction.m
#import "Fraction.h"
#import <stdio.h>

@implementation Fraction
-(Fraction*) initWithNumerator: (int) n denominator: (int) d {
    self = [super init];

    if ( self ) {
        [self setNumerator: n andDenominator: d];
    }

    return self;
}

// Fraction.h에는 정의 되어 있지 않으나, 프로토콜 Printing에 정의되어 있는 것을 구현
-(void) print {
    printf( "%i/%i", numerator, denominator );
}

-(void) setNumerator: (int) n {
    numerator = n;
}

-(void) setDenominator: (int) d {
    denominator = d;
}

-(void) setNumerator: (int) n andDenominator: (int) d {
    numerator = n;
    denominator = d;
}

-(int) denominator {
    return denominator;
}

-(int) numerator {
    return numerator;
}

// print method와 마찬가지로 NSCopying 프로토콜을 구현
-(Fraction*) copyWithZone: (NSZone*) zone {
    return [[Fraction allocWithZone: zone] initWithNumerator: numerator
                                           denominator: denominator];
}
@end


Complex.h
#import <Foundation/NSObject.h>
#import "Printing.h"

@interface Complex: NSObject <Printing> {
    double real;
    double imaginary;
}

-(Complex*) initWithReal: (double) r andImaginary: (double) i;
-(void) setReal: (double) r;
-(void) setImaginary: (double) i;
-(void) setReal: (double) r andImaginary: (double) i;
-(double) real;
-(double) imaginary;
@end


Complex.m
#import "Complex.h"
#import <stdio.h>

@implementation Complex
-(Complex*) initWithReal: (double) r andImaginary: (double) i {
    self = [super init];

    if ( self ) {
        [self setReal: r andImaginary: i];
    }

    return self;
}

-(void) setReal: (double) r {
    real = r;
}

-(void) setImaginary: (double) i {
    imaginary = i;
}

-(void) setReal: (double) r andImaginary: (double) i {
    real = r;
    imaginary = i;
}

-(double) real {
    return real;
}

-(double) imaginary {
    return imaginary;
}

-(void) print {
//    printf( "%_f + %_fi", real, imaginary );
    printf( "%f + %f", real, imaginary );
}
@end


main.m
#import <stdio.h>
#import "Fraction.h"
#import "Complex.h"

int main( int argc, const char *argv[] ) {
    // create a new instance
    Fraction *frac = [[Fraction alloc] initWithNumerator: 3 denominator: 10];
    Complex *comp = [[Complex alloc] initWithReal: 5 andImaginary: 15];
    id <Printing> printable;
    id <NSCopying, Printing> copyPrintable;

    // print it
    printable = frac;
    printf( "The fraction is: " );
    [printable print];
    printf( "\n" );

    // print complex
    printable = comp;
    printf( "The complex number is: " );
    [printable print];
    printf( "\n" );

    // this compiles because Fraction comforms to both Printing and NSCopyable
    copyPrintable = frac;

    // this doesn't compile because Complex only conforms to Printing
    //copyPrintable = comp;

    // test conformance

    // true
    if ( [frac conformsToProtocol: @protocol( NSCopying )] == YES ) {
        printf( "Fraction conforms to NSCopying\n" );
    }
    else {
        printf( "Fraction doesn't conform to NSCopying\n" );
    }

    // false
    if ( [comp conformsToProtocol: @protocol( NSCopying )] == YES ) {
        printf( "Complex conforms to NSCopying\n" );
    }
    else {
        printf( "Complex doesn't conform to NSCopying\n" );
    }
   
    // test id type
    id f; // 그냥 id 타입으로 받아도 상관없음. 다만 엄격한 사용을 위해 위와 같이 protocol을 지정.
   
    f = frac;
    [f print];
    printf( "\n" );
   
    f = comp;
    [f print];
    printf( "\n" );

    // free memory
    [frac release];
    [comp release];

    system("PAUSE");
    return 0;
}


결과
The fraction is: 3/10
The complex number is: 5.000000 + 15.000000
Fraction conforms to NSCopying
Complex doesn't conform to NSCopying
3/10
5.000000 + 15.000000
계속하려면 아무 키나 누르십시오 . . .

- The methods that the protocol requires to be implemented are not required to be in the list of methods for the header file. As you can see, Complex.h doesn't have a definition for -(void) print, but it still implements it since it conforms to the protocol.
- One unique aspect of Objective-C's interface system is how you specify types. Rather than specifying it like Java or C++ as: Printing *someVar = ( Printing * ) frac; for example, you use the id type with a restricted protocol: id <Printing> var = frac; This allows you to dynamically specify a type that requires multiple protocols, all with one variable. Such as: id <Printing, NSCopying> var = frac;
- Much like using @selector for testing an object's inheritance, you can use @protocol to test for conformance of interfaces. [object conformsToProtocol: @protocol( SomeProtocol )] returns a BOOL if the object conforms to that protocol. This works the same for classes as well: [SomeClass conformsToProtocol: @protocol( SomeProtocol )].

'프로그래밍 > iPhone Dev' 카테고리의 다른 글

[Objective C] 12 Dealloc  (1) 2009.11.12
[Objective C] 11 Retain and Release  (0) 2009.11.12
[Objective C] 09 Posing  (0) 2009.11.10
[Objective C] 08 Categories  (0) 2009.11.09
[Objective C] 07 DynamicTypes  (0) 2009.10.30

[Objective C] 09 Posing

프로그래밍/iPhone Dev 2009. 11. 10. 14:35 Posted by galad
Posing
자세 잡기. 상속 관계에 있는 클래스 중에서 하위 클래스에게 상위 클래스에 대한 메시지에 응답하라고 설정하는 것.

Fraction.h
#import <Foundation/NSObject.h>

@interface Fraction: NSObject {
    int numerator;
    int denominator;
}

-(Fraction*) initWithNumerator: (int) n andDenominator: (int) d;
-(Fraction*) initWithNumerator: (int) n denominator: (int) d;
-(void) print;
-(void) setNumerator: (int) n;
-(void) setNumerator: (int) n andDenominator: (int) d;
-(void) setDenominator: (int) d;
-(int) numerator;
-(int) denominator;
@end


Fraction.m
#import "Fraction.h"
#import <stdio.h>

@implementation Fraction
-(Fraction*) initWithNumerator: (int) n andDenominator: (int) d {
    self = [super init];
   
    if(self) {
        [self setNumerator: n andDenominator: d];
    }
   
    return self;
}

-(Fraction*) initWithNumerator: (int) n denominator: (int) d {
    self = [super init];
   
    if(self) {
        [self setNumerator: n andDenominator: d];
    }
   
    return self;
}

-(void) print {
    printf( "%i/%i", numerator, denominator );
}

-(void) setNumerator: (int) n {
    numerator = n;
}

-(void) setNumerator: (int) n andDenominator: (int) d {
    numerator = n;
    denominator = d;
}

-(void) setDenominator: (int) d {
    denominator = d;
}

-(int) denominator {
    return denominator;
}

-(int) numerator {
    return numerator;
}
@end


FractionB.h
#import "Fraction.h"

@interface FractionB: Fraction
-(void) print;
@end


FractionB.m
#import "FractionB.h"
#import <stdio.h>

@implementation FractionB
-(void) print {
    printf( "(%i/%i)", numerator, denominator );
}
@end


main.m
#import <stdio.h>
#import "Fraction.h"
#import "FractionB.h"

int main( int argc, const char *argv[] ) {
    Fraction *frac = [[Fraction alloc] initWithNumerator: 3 denominator: 10];

    // print it
    printf( "The fraction is: " );
    [frac print];
    printf( "\n" );

    // make FractionB pose as Fraction
    [FractionB poseAsClass: [Fraction class]];

    Fraction *frac2 = [[Fraction alloc] initWithNumerator: 3 denominator: 10];

    // print it
    printf( "The fraction is: " );
    [frac2 print];
    printf( "\n" );

    // free memory
    [frac release];
    [frac2 release];

    system("PAUSE");
    return 0;
}


결과
The fraction is: 3/10
The fraction is: (3/10)
계속하려면 아무 키나 누르십시오 . . .

- The method poseAsClass is part of NSObject. This allows a subclass to pose as a superclass.

'프로그래밍 > iPhone Dev' 카테고리의 다른 글

[Objective C] 11 Retain and Release  (0) 2009.11.12
[Objective C] 10 Protocols  (0) 2009.11.11
[Objective C] 08 Categories  (0) 2009.11.09
[Objective C] 07 DynamicTypes  (0) 2009.10.30
[Objective C] 06 Inheritance  (1) 2009.10.30

[Objective C] 08 Categories

프로그래밍/iPhone Dev 2009. 11. 9. 17:25 Posted by galad
Categories

Fraction.h
#import <Foundation/NSObject.h>

@interface Fraction: NSObject {
    int numerator;
    int denominator;
}

-(Fraction*) initWithNumerator: (int) n andDenominator: (int) d;
-(Fraction*) initWithNumerator: (int) n denominator: (int) d;
-(void) print;
-(void) setNumerator: (int) n;
-(void) setNumerator: (int) n andDenominator: (int) d;
-(void) setDenominator: (int) d;
-(int) numerator;
-(int) denominator;
@end


Fraction.m
#import "Fraction.h"
#import <stdio.h>

@implementation Fraction
-(Fraction*) initWithNumerator: (int) n andDenominator: (int) d {
    self = [super init];
   
    if(self) {
        [self setNumerator: n andDenominator: d];
    }
   
    return self;
}

-(Fraction*) initWithNumerator: (int) n denominator: (int) d {
    self = [super init];
   
    if(self) {
        [self setNumerator: n andDenominator: d];
    }
   
    return self;
}

-(void) print {
    printf( "%i/%i", numerator, denominator );
}

-(void) setNumerator: (int) n {
    numerator = n;
}

-(void) setNumerator: (int) n andDenominator: (int) d {
    numerator = n;
    denominator = d;
}

-(void) setDenominator: (int) d {
    denominator = d;
}

-(int) denominator {
    return denominator;
}

-(int) numerator {
    return numerator;
}
@end


FractionMath.h
#import "Fraction.h"

@interface Fraction (Math)
-(Fraction*) add: (Fraction*) f;
-(Fraction*) mul: (Fraction*) f;
-(Fraction*) div: (Fraction*) f;
-(Fraction*) sub: (Fraction*) f;
@end


FractionMath.m
#import "FractionMath.h"

@implementation Fraction (Math)
-(Fraction*) add: (Fraction*) f {
    return [[Fraction alloc] initWithNumerator: numerator * [f denominator] +
                                                denominator * [f numerator]
                             denominator: denominator * [f denominator]];
}

-(Fraction*) mul: (Fraction*) f {
    return [[Fraction alloc] initWithNumerator: numerator * [f numerator]
                             denominator: denominator * [f denominator]];

}

-(Fraction*) div: (Fraction*) f {
    return [[Fraction alloc] initWithNumerator: numerator * [f denominator]
                             denominator: denominator * [f numerator]];
}

-(Fraction*) sub: (Fraction*) f {
    return [[Fraction alloc] initWithNumerator: numerator * [f denominator] -
                                                denominator * [f numerator]
                             denominator: denominator * [f denominator]];
}
@end


main.m
#import <stdio.h>
#import "Fraction.h"
#import "FractionMath.h"

int main( int argc, const char *argv[] ) {
    // create a new instance
    Fraction *frac1 = [[Fraction alloc] initWithNumerator: 1 denominator: 3];
    Fraction *frac2 = [[Fraction alloc] initWithNumerator: 2 denominator: 5];
    Fraction *frac3 = [frac1 mul: frac2];

    // print it
    [frac1 print];
    printf( " * " );
    [frac2 print];
    printf( " = " );
    [frac3 print];
    printf( "\n" );

    // free memory
    [frac1 release];
    [frac2 release];
    [frac3 release];

    system("PAUSE");
    return 0;
}


결과
1/3 * 2/5 = 2/15
계속하려면 아무 키나 누르십시오 . . .

- There can only be one category with the same name. Additional cateogies may be added on with different but unqiue names.
- 카테고리는 인스턴스 변수를 가질 수 없음.
- Categories are useful for creating private methods. Obejctive-C에는 java의 private/protected/public 같은 개념이 없어서 카테고리로 그런 기능을 할 수 있다. 단 class의 private 메소드를 .m 파일에 옮겨놓을 것. 다음이 좋은 예.

MyClass.h
#import <Foundation/NSObject.h>

@interface MyClass: NSObject
-(void) publicMethod;
@end


MyClass.m
#import "MyClass.h"
#import <stdio.h>

@implementation MyClass
-(void) publicMethod {
    printf( "public method\n" );
}
@end

// private methods
@interface MyClass (Private)
-(void) privateMethod;
@end

@implementation MyClass (Private)
-(void) privateMethod {
    printf( "private method\n" );
}
@end


main.m
#import "MyClass.h"

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

    // this compiles
    [obj publicMethod];

    // this throws errors when compiling
    //[obj privateMethod];

    // free memory
    [obj release];
   
    return 0;
}


- private 메소드로 쓰는 건 좋으나 그 외에는 솔직히 어디다 써야할 지 감이 안 옴...

'프로그래밍 > iPhone Dev' 카테고리의 다른 글

[Objective C] 10 Protocols  (0) 2009.11.11
[Objective C] 09 Posing  (0) 2009.11.10
[Objective C] 07 DynamicTypes  (0) 2009.10.30
[Objective C] 06 Inheritance  (1) 2009.10.30
[Objective C] 05 The Id Type  (1) 2009.10.29

[Objective C] 07 DynamicTypes

프로그래밍/iPhone Dev 2009. 10. 30. 16:49 Posted by galad
DynamicTypes

Rectangle.h
#import <Foundation/NSObject.h>

@interface Rectangle2: NSObject {
    int width;
    int height;
}

-(Rectangle2*) initWithWidth: (int) w height: (int) h;
-(id) initWithWidth2: (int) w height: (int) h;
-(void) setWidth: (int) w;
-(void) setHeight: (int) h;
-(void) setWidth: (int) w height: (int) h;
-(int) width;
-(int) height;
-(void) print;
@end


Rectangle.m
#import "Rectangle.h"
#import <stdio.h>

@implementation Rectangle2
-(Rectangle2*) initWithWidth: (int) w height: (int) h {
    self = [super init];
   
    if(self) {
        [self setWidth: w height: h];
    }
   
    return self;
}

-(id) initWithWidth2: (int) w height: (int) h {
    self = [super init];
   
    if(self) {
        [self setWidth: w height: h];
    }
   
    return self;
}

-(void) setWidth: (int) w {
    width = w;
}

-(void) setHeight: (int) h {
    height = h;
}

-(void) setWidth: (int) w height:(int) h {
    width = w;
    height = h;
}

-(int) width {
    return width;
}

-(int) height {
    return height;
}

-(void) print {
    printf("width = %i, height = %i", width, height);
}
@end


Square.h
#import "Rectangle.h"

@interface Square: Rectangle2
-(Square*) initWithSize: (int) s;
-(void) setSize: (int) s;
-(int) size;
+(void) setSize; // respondsToSelector 테스트용
@end


Square.m
#import "Square.h"

@implementation Square
-(Square*) initWithSize: (int) s {
    self = [super init];
   
    if(self) {
        [self setSize: s];
    }
   
    return self;
}

-(void) setSize: (int) s {
    width = s;
    height = s;
}

-(int) size {
    return width;
}

-(void) setWidth: (int) w {
    [self setSize: w];
}

-(void) setHeight: (int) h {
    [self setSize: h];
}

+(void) setSize {
}
@end


main.m
#import "Square.h"
#import "Rectangle.h"
#import <stdio.h>

int main(int argc, const char* argv[]) {
    Rectangle2* rec = [[Rectangle2 alloc] initWithWidth: 10 height: 20];
    Square* sq = [[Square alloc] initWithSize: 15];
   
    // is Member of class
    printf("\nis Member of class\n");
    // 1: sq instance가 Square 클래스의 멤버인가 - true
    if([sq isMemberOfClass: [Square class]] == YES) {
        printf("Square is member of Square class\n");
    }
    else {
        printf("Square is NOT member of Square class\n");
    }

    // 2: sq instance가 Rectangle2 클래스의 멤버인가 - flase
    if([sq isMemberOfClass: [Rectangle2 class]] == YES) {
        printf("Square is member of Rectangle2 class\n");
    }
    else {
        printf("Square is NOT member of Rectangle2 class\n");
    }

    // 3: sq instance가 NSObject 클래스의 멤버인가? - flase
    if([sq isMemberOfClass: [NSObject class]] == YES) {
        printf("Square is member of NSObject class\n");
    }
    else {
        printf("Square is NOT member of NSObject class\n");
    }
   
    // 4: Square 클래스가 NSObject 클래스의 멤버인가? - flase
    if([Square isMemberOfClass: [NSObject class]] == YES) {
        printf("Square class is member of NSObject class\n");
    }
    else {
        printf("Square class is NOT member of NSObject class\n");
    }
   
    // isMemberOfClass는 대상 class/instance가 비교 클래스일 때만 true.
    // supre class는 모두 무시.
   
    // is Kind of class
    printf("\nis Kind of class\n");
    // 5: sq instance가 Square 클래스의 종류인가? - true
    if([sq isKindOfClass: [Square class]] == YES) {
        printf("Square is kind of Square class\n");
    }
    else {
        printf("Square is NOT kind of Square class\n");
    }

    // 6: sq instance가 Rectangle2 클래스의 종류인가? - true
    if([sq isKindOfClass: [Rectangle2 class]] == YES) {
        printf("Square is kind of Rectangle2 class\n");
    }
    else {
        printf("Square is NOT kind of Rectangle2 class\n");
    }

    // 7: sq instance가 NSObject 클래스의 종류인가? - true
    if([sq isKindOfClass: [NSObject class]] == YES) {
        printf("Square is kind of NSObject class\n");
    }
    else {
        printf("Square is NOT kind of NSObject class\n");
    }
   
    // 8: Square 클래스가 NSObject 클래스의 종류인가? - true
    if([Square isKindOfClass: [NSObject class]] == YES) {
        printf("Square class is kind of NSObject class\n");
    }
    else {
        printf("Square class is NOT kind of NSObject class\n");
    }
   
    // isKindOfClass는 대상 class/instance가 비교 클래스이거나
    // 비교 클래스의 child class일 때만 true.
   
    // respondsToSelector
    printf("\nrespondsToSelector\n");
    // 9: sq instance는 selector(setSize:)에 응답하는가? - true
    if([sq respondsToSelector: @selector(setSize:)] == YES) {
        printf("Square responds to setSize: method\n");
    }
    else {
        printf("Square DOESN'T respond to setSize: method\n");
    }
   
    // 10: sq instance는 selector(setSize)에 응답하는가? - false
    if([sq respondsToSelector: @selector(setSize)] == YES) {
        printf("Square responds to setSize method\n");
    }
    else {
        printf("Square DOESN'T respond to setSize method\n");
    }
   
    // setSize: 와 setSize 의 차이점은? -> parameter를 받는 method와 그렇지 않은 method
    // parameter 없는 setSize method를 만들었을 경우엔 @selector(setSize)에도 응답한다.

    // 11: Square class는 selector(setSize:)에 응답하는가? - false
    if([Square respondsToSelector: @selector(setSize:)] == YES) {
        printf("Square class responds to setSize: method\n");
    }
    else {
        printf("Square class DOESN'T respond to setSize: method\n");
    }
       
    // 12: Square class는 selector(setSize)에 응답하는가? - true
    if([Square respondsToSelector: @selector(setSize)] == YES) {
        printf("Square class responds to setSize method\n");
    }
    else {
        printf("Square class DOESN'T respond to setSize method\n");
    }
   
    // instance method(-붙은 method)는 instance에만 응답하고
    // class method(+붙은 method)는 class에만 응답한다.
   
    // 13: sq instance는 selector(nonExistant)에 응답하는가? - false
    if([sq respondsToSelector: @selector(nonExistant)] == YES) {
        printf("Square responds to nonExistant method\n");
    }
    else {
        printf("Square DOESN'T respond to nonExistant method\n");
    }
   
    // 14: Square class는 selector(alloc)에 응답하는가? - true
    if([Square respondsToSelector: @selector(alloc)] == YES) {
        printf("Square class responds to alloc method\n");
    }
    else {
        printf("Square class DOESN'T respond to alloc method\n");
    }
   
    // 15: sq instance는 selector(alloc)에 응답하는가? - false
    if([sq respondsToSelector: @selector(alloc)] == YES) {
        printf("Square responds to alloc method\n");
    }
    else {
        printf("Square DOESN'T respond to alloc method\n");
    }
   
    // instancesRespondToSelector
    // 말 그대로 클래스의 instance가 selector에 응답하는가를 체크하는 method
    printf("\ninstancesRespondToSelector\n");
    // 16: Rectangle2의 instances는 selector(setSize:)에 응답하는가? - false
    if([Rectangle2 instancesRespondToSelector: @selector(setSize:)] == YES) {
        printf("Rectangle2 instances respond to setSize: method\n");
    }
    else {
        printf("Rectangle2 instances DON'T respond to setSize: method\n");
    }
   
    // 17: Square의 instances는 selector(setSize:)에 응답하는가? - true
    if([Square instancesRespondToSelector: @selector(setSize:)] == YES) {
        printf("Square instances respond to setSize: method\n");
    }
    else {
        printf("Square instances DON'T respond to setSize: method\n");
    }
   
    // 18: Square의 instances는 selector(setSize)에 응답하는가? - false
    if([Square instancesRespondToSelector: @selector(setSize)] == YES) {
        printf("Square instances respond to setSize method\n");
    }
    else {
        printf("Square instances DON'T respond to setSize method\n");
    }

    [rec release];   
    [sq release];

    system("PAUSE");
    return 0;
}


결과
is Member of class
Square is member of Square class
Square is NOT member of Rectangle2 class
Square is NOT member of NSObject class
Square class is NOT member of NSObject class

is Kind of class
Square is kind of Square class
Square is kind of Rectangle2 class
Square is kind of NSObject class
Square class is kind of NSObject class

respondsToSelector
Square responds to setSize: method
Square DOESN'T respond to setSize method
Square class DOESN'T respond to setSize: method
Square class responds to setSize method
Square DOESN'T respond to nonExistant method
Square class responds to alloc method
Square DOESN'T respond to alloc method

instancesRespondToSelector
Rectangle2 instances DON'T respond to setSize: method
Square instances respond to setSize: method
Square instances DON'T respond to setSize method
계속하려면 아무 키나 누르십시오 . . .

* Dev-C에 확실히 버그가 있음. 기존 .h, .m 파일 자체를 복사해서 프로젝트에 추가하면 인식이 안됨. 컴파일 시 클래스를 못찾음...
 꼭 프로젝트->New File로 추가해줘야 링크가 걸림.

- There are several methods for working with dynamic types in Objective-C
      -(BOOL) isKindOfClass: classObj is object a descendent or member of classObj
      -(BOOL) isMemberOfClass: classObj is object a member of classObj
      -(BOOL) respondsToSelector: selector does the object have a method named specifiec by the selector
      +(BOOL) instancesRespondToSelector: selector does an object created by this class have the ability to respond to the specified selector
      -(id) performSelector: selector invoke the specified selector on the object
- isMemberOfClass는 대상 class/instance가 비교 클래스일 때만 true. supre class는 모두 무시.
- isKindOfClass는 대상 class/instance가 비교 클래스이거나 비교 클래스의 child class일 때만 true.
- setSize: 와 setSize 의 차이점은? -> parameter를 받는 method와 그렇지 않은 method
- instance method(-붙은 method)는 instance에만 응답하고 class method(+붙은 method)는 class에만 응답한다.
- instancesRespondToSelector는 말 그대로 클래스의 instance가 selector에 응답하는가를 체크하는 method. 타겟은 당연히 class.

'프로그래밍 > iPhone Dev' 카테고리의 다른 글

[Objective C] 09 Posing  (0) 2009.11.10
[Objective C] 08 Categories  (0) 2009.11.09
[Objective C] 06 Inheritance  (1) 2009.10.30
[Objective C] 05 The Id Type  (1) 2009.10.29
[Objective C] 04 Exceptions  (0) 2009.10.28

[Objective C] 06 Inheritance

프로그래밍/iPhone Dev 2009. 10. 30. 11:38 Posted by galad
Inheritance

Rectangle.h
#import <Foundation/NSObject.h>

@interface Rectangle2: NSObject {
    int width;
    int height;
}

-(Rectangle2*) initWithWidth: (int) w height: (int) h;
-(id) initWithWidth2: (int) w height: (int) h;
-(void) setWidth: (int) w;
-(void) setHeight: (int) h;
-(void) setWidth: (int) w height: (int) h;
-(int) width;
-(int) height;
-(void) print;
@end


Rectangle.m
#import "Rectangle.h"
#import <stdio.h>

@implementation Rectangle2
-(Rectangle2*) initWithWidth: (int) w height: (int) h {
    self = [super init];
   
    if(self) {
        [self setWidth: w height: h];
    }
   
    return self;
}

// 테스트용. child class에서 super class의 생성자를 이용해서 object 생성하기 위한 constructor.
-(id) initWithWidth2: (int) w height: (int) h {
    self = [super init];
   
    if(self) {
        [self setWidth: w height: h];
    }
   
    return self;
}

-(void) setWidth: (int) w {
    width = w;
}

-(void) setHeight: (int) h {
    height = h;
}

-(void) setWidth: (int) w height:(int) h {
    width = w;
    height = h;
}

-(int) width {
    return width;
}

-(int) height {
    return height;
}

-(void) print {
    printf("width = %i, height = %i", width, height);
}
@end


Square.h
#import "Rectangle.h"

@interface Square: Rectangle2
-(Square*) initWithSize: (int) s;
-(void) setSize: (int) s;
-(int) size;
@end


Square.m
#import "Square.h"

@implementation Square
-(Square*) initWithSize: (int) s {
    self = [super init];
   
    if(self) {
        [self setSize: s];
    }
   
    return self;
}

-(void) setSize: (int) s {
    width = s;
    height = s;
}

-(int) size {
    return width;
}

-(void) setWidth: (int) w {
    [self setSize: w];
}

-(void) setHeight: (int) h {
    [self setSize: h];
}
@end


main.m
#import "Square.h"
#import "Rectangle.h"
#import <stdio.h>

int main(int argc, const char* argv[]) {
    Rectangle2* rec = [[Rectangle2 alloc] initWithWidth: 10 height: 20];
    Square* squ = [[Square alloc] initWithSize: 15];

    // Use parent's constructor
    Square* squ2 = [[Square alloc] initWithWidth2: 15 height: 25]; // <-- 1
    Square* squ3 = [[Square alloc] initWithWidth: 45 height: 55]; // <-- 2
   
    // print em
    printf("Rectangle: ");
    [rec print];
    printf("\n");
   
    printf("Square: ");
    [squ print];
    printf("\n");
   
    // update square
    [squ setWidth: 20];
    printf("Square after change: ");
    [squ print];
    printf("\n");
   
    printf("Square2: ");
    [squ2 print];
    printf("\n");
   
    printf("Square3: ");
    [squ3 print];
    printf("\n");
   
    // free memory
    [rec release];
    [squ release];
    [squ2 release];
    [squ3 release];
       
    system("PAUSE");
    return 0;
}


결과
Rectangle: width = 10, height = 20
Square: width = 15, height = 15
Square after change: width = 20, height = 20
Square2: width = 15, height = 25
Square3: width = 45, height = 55
계속하려면 아무 키나 누르십시오 . . .

Rectangle이란 이름을 갖는 클래스가 있어서 Rectangle2로 변경.

- Objective-C의 상속은 자바랑 비슷. child class 구현 시 method를 새롭게 구현하는 것으로 super class의 method를 간단히 override 할 수 있음.
- child class - Squre - 생성 시, super class - Rectangle2 - 의 생성자를 사용하려면
super class의 생성자의 반환 타입이 id 이어야만 함.(<--1 참고)
 super class의 생성자의 반환타입이 Rectangle2여서 Squre로 받을 수 없어서 컴파일 오류... 라고 설명에 나와있었으나

One thing left out here that is worth nothing is what would happen if you attempted to call the constructor for rectangle like: Square *sq = [[Square alloc] initWithWidth: 10 height: 15]. The answer is it will throw a compile error. Since the return type of the rectangle constructor is Rectangle*, not Square* this would not work. In such a case if you want this to occur, that's what the id variable is good for. Just change the Rectangle* return type to id if you wish to use your parent's constructors in a subclass.

해보니 잘 되네 ㅡ.ㅡ;;; (<-- 2 참고)
Objective-C 버전이 올라가면서 허용된 건지 어떤건지는 잘 모르겠음?
아무튼 child class object 생성 시 super class의 생성자(반환타입이 id가 아니더라도)를 사용 가능함.

'프로그래밍 > iPhone Dev' 카테고리의 다른 글

[Objective C] 08 Categories  (0) 2009.11.09
[Objective C] 07 DynamicTypes  (0) 2009.10.30
[Objective C] 05 The Id Type  (1) 2009.10.29
[Objective C] 04 Exceptions  (0) 2009.10.28
[Objective C] 03 Class Level Access  (0) 2009.10.27

[Objective C] 05 The Id Type

프로그래밍/iPhone Dev 2009. 10. 29. 17:31 Posted by galad
The Id Type

void* 형처럼 움직임.
java나 c++과 다른 것은 object를 통해 method를 호출할 때 object 타입을 알아야만 할 필요가 없음.
단순히 method가 존재하기만 하면 됨.(Objective-C의 메시지 전달 방식)

Fraction.h
#import <Foundation/NSObject.h>

@interface Fraction: NSObject {
    int numerator;
    int denominator;
}

-(Fraction*) initWithNumerator: (int) n andDenominator: (int) d;
-(void) print;
-(void) setNumerator: (int) n;
-(void) setNumerator: (int) n andDenominator: (int) d;
-(void) setDenominator: (int) d;
-(int) numerator;
-(int) denominator;
@end


Fraction.m
#import "Fraction.h"
#import <stdio.h>

@implementation Fraction
-(Fraction*) initWithNumerator: (int) n andDenominator: (int) d {
    self = [super init];
   
    if(self) {
        [self setNumerator: n andDenominator: d];
    }
   
    return self;
}

-(void) print {
    printf( "%i/%i", numerator, denominator );
}

-(void) setNumerator: (int) n {
    numerator = n;
}

-(void) setNumerator: (int) n andDenominator: (int) d {
    numerator = n;
    denominator = d;
}

-(void) setDenominator: (int) d {
    denominator = d;
}

-(int) denominator {
    return denominator;
}

-(int) numerator {
    return numerator;
}
@end


Complex.h
#import <Foundation/NSObject.h>

@interface Complex: NSObject {
    double real;
    double imaginary;
}

-(Complex*) initWithReal: (double) r andImaginary: (double) i;
-(void) setReal: (double) r;
-(void) setImaginary: (double) i;
-(void) setReal: (double) r andImaginary: (double) i;
-(double) real;
-(double) imaginary;
-(void) print;
@end


Complex.m
#import "Complex.h"
#import <stdio.h>

@implementation Complex
-(Complex*) initWithReal: (double) r andImaginary: (double) i {
    self = [super init];
   
    if(self) {
        [self setReal: r andImaginary: i];
    }
   
    return self;
}

-(void) setReal: (double) r {
    real = r;
}

-(void) setImaginary: (double) i {
    imaginary = i;
}

-(void) setReal: (double) r andImaginary: (double) i {
    real = r;
    imaginary = i;
}

-(double) real {
    return real;
}

-(double) imaginary {
    return imaginary;
}

-(void) print {
    printf("%f + %f", real, imaginary);
}
@end


main.m
#import <stdio.h>
#import "Fraction.h"
#import "Complex.h"

int main(int argc, const char* argv[]) {

    // create new instances
    Fraction* frac = [[Fraction alloc] initWithNumerator: 1 andDenominator: 10];
    Complex* comp = [[Complex alloc] initWithReal: 10 andImaginary: 15];
   
    id number;
       
    // print fraction
    number = frac;
    printf( "The fraction is: " );
    [number print];
    printf( "\n" );

    // print complex
    number = comp;
    printf( "The complex number is: " );
    [number print];
    printf( "\n" );

    // free memory
    [frac release];
    [comp release];

    return system("PAUSE");
    return 0;
}


결과
The fraction is: 1/10
The complex number is: 10.000000 + 15.000000
계속하려면 아무 키나 누르십시오 . . .

There are obvious benefits to this type of dynamic binding. You don't have to know the type of something to call a method on it. If the object responds to a message, it will invoke that method. Lots of nasty casting isn't involved in this either, such as in Java to call .intValue() on an integer object would involve casting first, then calling the method.

동적 바인딩의 명백한 장점은 method 호출 시 타입이 뭔지 몰라도 된다는 것과 많은 지저분한 형변환이 필요없다는 거란다.(자바처럼 ㅋ~)
object가 message에 반응하면 method를 실행시킨다는군. 반응 못하면? 예외인가?

'프로그래밍 > iPhone Dev' 카테고리의 다른 글

[Objective C] 07 DynamicTypes  (0) 2009.10.30
[Objective C] 06 Inheritance  (1) 2009.10.30
[Objective C] 04 Exceptions  (0) 2009.10.28
[Objective C] 03 Class Level Access  (0) 2009.10.27
[Objective C] 02 Access Privledges  (0) 2009.10.27

[Objective C] 04 Exceptions

프로그래밍/iPhone Dev 2009. 10. 28. 11:28 Posted by galad
Exceptions

CupWarningException.h
#import <Foundation/NSException.h>

@interface CupWarningException: NSException
@end


CupWarningException.m
#import "CupWarningException.h"

@implementation CupWarningException
@end


CupOverflowException.h
#import <Foundation/NSException.h>

@interface CupOverflowException: NSException
@end


CupOverflowException.m
#import "CupOverflowException.h"

@implementation CupOverflowException
@end


Cup.h
#import <Foundation/NSObject.h>

@interface Cup: NSObject {
    int level;
}

-(int) level;
-(void) setLevel: (int) i;
-(void) fill;
-(void) empty;
-(void) print;
@end


Cup.m
#import "Cup.h"
#import "CupWarningException.h"
#import "CupOverflowException.h"
#import <Foundation/NSException.h>
#import <Foundation/NSString.h>

@implementation Cup
-(id) init {
    self = [super init];
   
    if(self) {
        [self setLevel: 0];
    }
   
    return self;
}

-(int) level {
    return level;
}

-(void) setLevel: (int) i {
    level = i;
   
    if(level > 100) {
        // throw overflow
        NSException* e =
            [CupOverflowException
                exceptionWithName: @"CupOverflowException"
                reason: @"The level is above 100"
                userInfo: nil
            ];
       
        @throw e;
    }
    else if(level >= 50) {
        // throw warning
        NSException* e =
            [CupWarningException
                exceptionWithName: @"CupWarningException"
                reason: @"The level is above or at 50"
                userInfo: nil
            ];
           
        @throw e;
    }
    else if(level < 0) {
        // throw exception
        NSException* e =
            [NSException
                exceptionWithName: @"CupUnderflowException"
                reason: @"The level is below 0"
                userInfo: nil
            ];
       
        @throw e;
    }
}

-(void) fill {
    [self setLevel: level + 10];
}

-(void) empty {
    [self setLevel: level - 10];
}

-(void) print {
    printf("Cup level is: %i\n", level);
}
@end


main.m
#import "Cup.h"
#import "CupOverflowException.h"
#import "CupWarningException.h"
#import <Foundation/NSString.h>
#import <Foundation/NSException.h>
#import <Foundation/NSAutoreleasePool.h>
#import <stdio.h>

int main(int argc, const char* argv[]) {

    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
   
    Cup* cup = [[Cup alloc] init];
    int i;
   
    // this will work
    for(i = 0; i < 4; i++) {
        [cup fill];
        [cup print];
    }
   
    // this will throw exceptions
    for(i = 0; i < 7; i++) {
        @try {
            [cup fill];
        }
        @catch(CupWarningException* e) {
            printf("%s: ", [[e name] cString]);
        }
        @catch(CupOverflowException* e) {
            printf("%s: ", [[e name] cString]);
        }
        @finally {
            [cup print];
        }
    }
   
    // throw a generic exception
    @try {
        [cup setLevel: -1];
    }
    @catch(NSException* e) {
        printf("%s: %s\n", [[e name] cString], [[e reason] cString]);
    }
   
    // free memory
    [cup release];
    [pool release];

    system("PAUSE");
    return 0;
}


컴파일 시 -fobjc-exceptions 옵션을 추가하라고 해서 붙였더니 @throw 에서 컴파일러가 죽는 바람에 결과 확인 못함.
minGW의 gcc 컴파일러 문제인 듯.

Compiler: Default compiler
Building Makefile: "D:\Workspaces\ObjectiveC\05_Exceptions\Makefile.win"
Executing  make...
make.exe -f "D:\Workspaces\ObjectiveC\05_Exceptions\Makefile.win" all
gcc.exe -c Cup.m -o Cup.o -I"C:/GNUstep/GNUstep/System/Library/Headers"    -lobjc -fobjc-exceptions -lgnustep-base -fconstant-string-class=NSConstantString -enable-auto-import
Cup.m: In function `-[Cup setLevel:]':
Cup.m:34: internal compiler error: Segmentation fault
Please submit a full bug report,
with preprocessed source if appropriate.
See <URL:http://www.mingw.org/bugs.shtml> for instructions.

make.exe: *** [Cup.o] Error 1
Execution terminated

위는 컴파일 로그.

예제에 따르면 결과는 다음과 같다.
Cup level is: 10
Cup level is: 20
Cup level is: 30
Cup level is: 40
CupWarningException: Cup level is: 50
CupWarningException: Cup level is: 60
CupWarningException: Cup level is: 70
CupWarningException: Cup level is: 80
CupWarningException: Cup level is: 90
CupWarningException: Cup level is: 100
CupOverflowException: Cup level is: 110
CupUnderflowException: The level is below 0

- exception 생성 방식을 제외하면 던지고 잡는 것은 자바와 비슷. finally도 마찬가지.
- NSException을 반드시 상속해서 사용할 필요는 없다고. @catch ( id e ){..} 으로 해도 된다.
 근데 이 경우 예외끼리의 구분은 catch 안에서 클래스를 구분해야 할 듯.

Type Introspection
Type introspection이란 runtime시에 해당 인스턴스가 어떤 클래스의 인스턴스인지 등을 알아내는 것을 의미합니다. 좀더 일반적으로 말하자면 runtime시에 어떤 타입인지, 무엇을 할 수 있는지 등, 실행중에 변할 수 있는 부분을 알아낼 수 있는 메커니즘을 말합니다. NSObject에는 이런 용도로 isMemberOfClass와 같은 메소드가 정의되어 있습니다.
if( [anObject isMemberOfClass:someClass] )

라고 한다면 anObject라는 인스턴스가 someClass라는 클래스 타입인지 아닌지를 알 수 있습니다. 즉 return 값이 YES이면 someClass의 타입이란 것입니다.
좀더 포괄적인 것으로는 isKindOfClass가 있습니다.
if( [anObject isKindOfClass:someClass] )

이것은 anObject의 수퍼클래스중 someClass가 있는가를 알아낼 수있게 합니다. 즉 someClass의 한 종류인지를 알 수있게 해준다는 것입니다. isMemberOfClass 경우는 직접적인 parent class, 다른 말로는 superclass의 형인지 아닌지를 알려주는데 반해, 이것은 직접적인 superclass가 아니어도 된다는 것입니다.
이외에도 주어진 객체가 특정 메시지에 반응하는지 아닌지를 알아내는 것등 introspection의 예는 많습니다.

- @"CupOverflowException"와 같은 것들은 constant NSString object란다. cString은 String constant로 char* .
즉 예외 생성 시 @"XXX"로 넣은 constant String을 cString 메소드로 가져다가 출력. 주소참조해서 문자가져온 듯.

'프로그래밍 > iPhone Dev' 카테고리의 다른 글

[Objective C] 06 Inheritance  (1) 2009.10.30
[Objective C] 05 The Id Type  (1) 2009.10.29
[Objective C] 03 Class Level Access  (0) 2009.10.27
[Objective C] 02 Access Privledges  (0) 2009.10.27
[Objective C] 01 Creating Classes  (0) 2009.10.27