프로토콜을 준수한다는 것

- 프로토콜을 준수한다는 것, 즉 conform한다는 것은, 한 클래스나 객체가 그 프로토콜의 모든 method를 다 구현하고 있다는 것이다.
 > NOTE : Class가 protocol을 formal하게 채용하지 않아도, method로써 protocol method들을 구현하면 자동으로 그 protocol을 준수하게 된다.
  -> @interface ClassName : ItsSuperclass < protocol list > 이런 식으로 formal 하게 protocol을 채용하지 않더라도 class가 protocol의 method를 구현하면 준수하게 된다고?
  -> 아닌 듯... 안되는데?? 소스 참고.



[Objective C] Protocol Object

프로그래밍/iPhone Dev 2009. 11. 26. 16:41 Posted by galad
Protocol Object

- runtime시에 protocol이 어떻게 표현되는지에 대해서 알아보자. 클래스는 class object로, 함수는 selector로 runtime시에 나타나듯이, formal protocol은 Protocol 클래스로 나타난다. protocol을 다루는 코드는 Protocol 오브젝트를 참조해야만 한다.

- protocol 객체를 참조하는 방법은 @protocol() 디렉티브를 이용해서 한다.

Protocol *counter = @protocol(ReferenceCounting);

- class 이름과 다른 점은, protocol 이름은 @protoco()안에 있는 것을 제외하고는, 어떤 한 객체를 의미하지는 않는다.
  -> ReferenceCounting 만으로는 아무것도 가리키지 않는다는 듯. class일 경우엔 class 이름만으로 class object를 가리키지만.

- 컴파일러는 다음의 경우에만 protocol 객체를 만든다.
  > 클래스에 의해서 adopt 되었을 때,
  > 소스코드에서 다른 어딘가를 참조했을 때 ( @protoco()을 사용해서.. )
즉 선언은 되었지만 사용되지 않는 protocol은 runtime시에 protocol 객체로 나타나지 않는다.
Protocol의 종류 : Formal Protocol

- formal protocol은 다음과 같이 @protocol이라는 directive를 이용해서 선언한다.

@protocol ReferenceCounting
- (int)refCount;
- incrementCount;
- decrementCount;
@end

- protocol 이름은 global하게 access할 수가 없다. protocol은 클래스랑 연관을 시켜서 써주는 것이지 단독적으로 쓰는것이 아니기 때문이다. 즉 어떤 protocol을 쓰려면 class가 그 프로토콜을 “준수”해야 하는 것이다. 즉 그 프로토콜을 adopt 혹은 채용해야 한다.

@interface ClassName : ItsSuperclass < protocol list >
@interface ClassName ( CategoryName ) < protocol list > // 카테고리가 protocol을 받으려면
@interface ClassName ( CategoryName ) < protocol_1, protocol_2 >

- 단 protocol을 받아들일 클래스나 카테고리 파일에서는, 반드시 그 프로토콜이 선언된 헤더파일을 import해야만 한다. 그리고 그렇게 받아들여진 프로토콜안에 정의된 method들은 클래스나 카테고리 인터페이스의 다른 부분에서 또 선언되면 안된다.
  -> 당연한 듯..

- 물론 프로토콜 method외엔 아무 method도 가지지 않는 클래스도 있을 수 있다.

@interface Formatter : NSObject < Formatting, Prettifying >
@end

- 한 클래스나 카테고리가 일단 어떤 프로토콜을 준수하겠다고 한다면,그 클래스나 카테고리는 준수하고자 하는 프로토콜에 선언된 모든 method를 다 구현해야 한다. 그렇지 않으면 컴파일러가 경고를 하게 된다.

- formal protocol은 선언시에 @protocol이란 특별한 directive를 써 놓았을 뿐, 그 구현은 역시 informal protocol과 마찬가지로 class내에서 한다.
Category는 어떤가? 그 선언과 구현이 별도로 되어 있다. 즉 클래스에 종속되어 있지 않다.
그렇다면 Category와 Protocol의 궁극적인 차이는 무엇일까?
Objective-C의 장점이 알지 못하는 객체에다가 메시지를 보내고, 그 헤더파일이 없는 클래스를 상속이 아닌 수평적인 수준에서 새 함수를 더 넣을 수 있다는 것이다. Category는 이미 있는 클래스를 수평적으로 확장을 할 때, 유용하다. 단 그때, 그 클래스의 소스코드가 없을 수 있다. 그러므로 별도의 장소에서 다음과 같이 한다.

@implementation ClassName ( CategoryName )
method definitions
@end

하지만 protocol인 경우엔 저 “ClassName” class 자체에서 하게 된다. 즉 protocol인 경우엔, 내가 지금 만드는 클래스가 어떤 것을 다 가져야 할지 이미 알고 있는 경우란 소리다.
Category와 Protocol 개념은 클래스들을 그 행위로서 구분지어줄 수 있는 것들이지만, 이렇듯 미리 무엇을 할지 알고 있느냐 없느냐에 따라 용례가 갈린다고 볼 수 있다. 또한 Category의 경우에는 뭔가 해당 클래스의 versioning이라는 점이다. 결국 보면 protocol과 비슷해질 수는 있겠지만, 개념적으론 좀 차이가 있다.
 -> Category는 (소스코드가 없는) 클래스를 수평적으로 확장(Versioning 개념).
     Protocol은 소스를 알고 있는 여러 클래스들에게 동일한 처리 규약(처리내용은 다르겠지만 인터페이스는 같다)을 주는 것. 정도 일려나?
     사용례를 봐야 좀 확실하게 개념이 잡힐 듯. Category는 그렇다처도 Protocol은 애매하다...
출처: Objective‐C Language Reference 박종암 jongampark@gmail.com
protocol의 이용부분부터는 박종암님의 pdf를 보고 썼음.. 문제있으면 알려주세요~

Protocol의 종류 : Informal Protocol


Category 선언내에 method를 선언하면 그게 바로 informal protocol이 된다.

@interface NSObject ( RefCounting )
- (int)refCount;
- incrementCount;
- decrementCount;
@end

위의 예에서 볼 수 있듯이, informal protocol은 대개 NSObject 클래스에 대한 카테고리로써 선언한다. 그렇게 하면, NSObject에서 나온 새끼 클래스들이 모두 다 사용할 수 있기 때문이다. 물론 다른 클래스에 대한 category로써도 정의할 수 있다. 그럼 그 클래스에서 나온 클래스들만 사용할 수 있다.
여기서 주의할 점은, 카테고리 interface를, protocol을 선언하기 위해서 쓸때는, 거기에 상응하는 implementation을 가지지 않는다는 점이다. 대신에 그 method들을 가지는 protocol을 구현하는 클래스들이 그 자체의 interface 파일에서 다른 method들과 마찬가지로 구현하게 된다.
다시 말해보자. 생긴 걸로 봤을때, informal protocol은 Category와 완전히 같다. 단지 차이점은 Category는 구현에 대한 부분도 역시 카테고리로 만들어서 넣어주나, informal protocol일 경우엔 그 구현을 해당 클래스에서 method로 직접 해 준다는 것이다.
또한 informal protocol은 category를 선언하는 방식을 좀 바꿔서 method들의 리스트를 써 놓기는 하지만 어떤 특정 클래스나 구현에 고정시켜 놓지는 않는다.
informal protocol은 구현하기가 이렇게 편하다. 하지만 단점이 있게 마련이다. 편한거치고 단점없는게 어디 있겠는가? 즉 language로부터 그다지 많은 지원을 받지 못한다. 표로 정리해 보자.

compile time시  :  type checking을 하지 않는다
run time시   :   한 객체가 그 protocol을 conform(준수)하는지 알수 없다.

이런 단점이 싫다면 그때는 다음에서 알아볼 formal protocol을 사용하면 된다. 그럼 이런 informal protocol은 언제 쓰면 좋을까? 그건, protocol에 정의된 method들을 구현하는게 굳이 필요하지 않을때이다. 즉 delegate같은 것을 구현할 때 쓰면 편하다. 즉 다른 객체 대신에 일을 수행하는 객체를 만들때 좋다.

NOTE : 엄밀하게 보면 informal protocol은 없는 것으로 보인다. 이것은 Category의 다른 이용일 뿐이다. Category와의 차이점은 앞에서도 나와 있지만, 그 구현을 클래스내에 하느냐 아니면 따로 준비된 곳에 하느냐의 차이이다. 필자가 Objective-C를 처음 접하면서, 처음엔 참 쉽게 잘 만들어졌다고 생각하면서도 한동안 보지 않다가 다시 보면 헷갈리고, 복잡하다고 여긴 부분이 바로 이 부분때문이다. syntax로 category와 informal protocol이 명확하게 구별이 안된다.
또한 그 사용 목적도 그렇다. Category의 원목적이 이미 있는 클래스에 뭔가를 더 추가해 넣을때, 그리고 attribute/property가 아닌 behaviour로 클래스들을 구분할 때 쓰기 위함인데, 결국 protocol과 비슷하지 않은가? protocol도 그 protocol을 conform하는 것끼리의 구별하는 목적이 있다. Objective-C는 NeXT의 몰락과 함께 사실 그 명맥이 다했으며 C++처럼 활발한 개발과 확장, 혹은 정제가 이루어지지 않았다. 언어의 간결함에 대해선 C++보단 더 좋으나, 뭔가 아직 미완성인 듯한 느낌이 나는 부분이 바로 이 부분이다. 앞으로 Apple이 다시 이 Objective-C를 발전시켜 나가면 좋겠다.

--> 안 쓰는게 좋을 듯 함... category와 protocol의 명확한 사용을 위해서...
Protocol 이용 : 비계층적 유사성(Non-Hierarchical Similarities)

- 여러 클래스가 같은 method를 구현하게 된다면, 그런 클래스들은 한 abstract class 밑으로 들어갈 수 있으며, 그 abstract class에서 공통된 method들을 선언해 놓게 된다. 각각의 sub class에선 그런 method들을 각각의 용도에 맞게 구현하면 된다. 이렇게 함으로써 그런 클래스들은 상속에 의한 계층적 구조에서, 그리고 같은 abstract class를 그 조상으로 한다는 점에서 유사성을 가지게 된다.
- 하지만 때때로 abstract class에 공통적인 method를 다 잡아 넣을 수 없는 경우도 있다. 이를테면, 전혀 관련이 없는 클래스들이지만 유사한 method를 가지고 있을 수도 있는 것이다. 이럴 경우에 상속관계로 묶어 놓기엔 좀 곤란할 수 있을 것이다. 예를 들어, 완전히 다른 클래스들에 reference counting을 위해서 어떤 method를 구현해 넣는다던가 하는 것이 그런 예겠다.
- setRefCount:(int)count;
- (int)refCount;
- incrementCount;
- decrementCount;
이런 것들은 프로토콜로 묶어 놓을 수 있겠다. 그리고 저런 프로토콜이 필요한 클래스에선 그 프로토콜에 conform하게끔만 해 놓으면 되는 것이다.

- 객체는 class가 아니라 protocol로써 type을 구분할 수도 있다는 점
예를들어, NSMatrix가 그 서브 객체인 cell들과 뭔가 주고 받아야 한다고 하자. NSMatrix는 각 서브 객체들이 NSCell 라는 종류이어야만 하고, 그러므로 각 서브 객체들이 NSMatrix에서 오는 메시지에 반응하는 method를 가진, NSCell에서 상속을 받어야 할 것이다.
하지만 여기서 protocol의 개념을 이용하면, 굳이 같은 NSCell 클래스에서 나온 class가 아니더라도, 어떤 특정의 메시지에 반응하도록 만들어진 method를 conform하도록만, 서브 객체인 cell들이 구성되어 있다면 상당히 유연한 코딩이 가능해진다.

  -> 결국 전혀 관련 없는 클래스들 - 부모클래스 등이 같지 않는, 전혀 다른 상속 관계에 있는 - 도 protocol을 이용하면 묶을 수 있다는 점.
   굳이 상속하지 않아도 되므로 유연하다는 점 등이 장점인 듯.
Protocol 이용 : 익명의 객체를 위한 Interface를 선언하기

Protocol 이용 : 익명의 객체를 위한 Interface를 선언하기
  -> 뭔 소린가 했는데, 익명의 객체-클래스-를 만들 때 반드시 구현해야할 메소드를 선언 가능하다는 얘기인 듯.
자바의 interface로 보면 될 듯.

- 라이브러리나 framework 제작자는 클래스 이름이나 인터페이스 파일로는 알 수 없는 객체를 만들수 있다. 그런 이름이나 클래스 인터페이스가 없다면, 그런 것을 가져다 사용하는 사람들 입장에선 도무지 그런 클래스의 instance를 만들래야 만들수가 없겠다. 아니면 원제작자는 이미 만들어 놓은 instance를 제공해 주어야만 한다. 보통의 경우, 다른 클래스에 정의된 method는 사용할 수 있는 객체를 리턴해주게 끔 만든다.
   id formatter = [receiver formattingService];
이 예에 있는 객체, 즉 반환된 객체는 그 class identity가 없다. 아니면 적어도 그 원 제작자가 바깥에 공개하기를 꺼리는 것일 거다. 이런 객체를 쓰게 하려면, 적어도 몇몇개의 메시지에는 반응을 하도록 만들어야 한다. 그러므로 이런 것은 protocol로써 정의된 method들과 연결시켜 놓을 수 있다.
 -> id 타입으로 반환된, 공개하길 꺼리는 object를 protocol로 정의된 method와 연관시켜 놔서, 그 object를 사용할 수 있게 해야한다는 말인 듯.

- 각 프로그램들은 각자의 구조와, 클래스, 그리고 동작하는 논리가 있지만, 다른 프로그램이 어떻게 움직일지 여러분이 꼭 알아야 하는 것은 아니다. 그 프로그램의 바깥에 있는 사람 입장에선, 어떤 메시지를 보낼 수 있고, 어디다 보내야 할지를 알면 그만이다. 이것은 결국protocol과 receiver이 무엇이냐를 알기만하면 된다는 것이다.
다른 곳에서 오는 메시지를 받아 처리할 객체를 공개하는 프로그램은, 반드시 그 메시지에 반응할 method또한 공개해야 한다. 메시지를 보내는 측에선 해당 객체의 클래스가 뭐냐에 대해선 알 필요가 없고, 그 클래스를 내부적으로 이용할 필요도 없다. 단지 필요한 것은 protocol뿐이다.
  -> 맞는 말인데 protocol과의 관련성은 잘 모르겠다...ㅠ.ㅠ


[Objective C] Protocol

프로그래밍/iPhone Dev 2009. 11. 20. 15:35 Posted by galad
Protocol

- 클래스랑 관련 없는, method 선언의 모음
- protocol로 선언된 method를 써야 할 필요가 있다면, 그 클래스는 해당 protocol을 쓰겠다고 하고(adopt), 그 method를 자체 내에서 구현하면 된다.
- 보통 protocol은 관련있는 메소드들 끼리 모아 놓게 된다. 그러므로 어떤 클래스가 그 protocol을 사용하겠다고 하면, 그 클래스는 그 "통신 규약"을 이해하는 클래스가 된다.
  -> protocol에 정의된 메소드들이 통신 규약이 되고, 그 protocol을 사용하는 클래스는 반드시 정의된 메소드들을 구현해야 되므로 통신 규약을 이해하는 클래스가 된다.

- dynamic language의 입장에서 볼때, class를 inheritance와 category에 의거한 "어디에서 상속을 받았나"로 클래스들을 구분해 놓는 것 뿐만 아니라, protocol로 인해서 어떤 클래스들이 서로 비슷한 행동을 하나라는 동적인 구분까지 가능하게 해 준다.
- 아예 클래스의 종류가 다른 것까지도 서로 protocol을 지키면 해당 메시지를 처리하는 것도 가능.

- 언제 protocol을 사용하게 되는지 정리해 보자.
  > 다른 프로그래머들이 구현하게 될 메소드의 선언 (Apple 문서에서 따온 말). 하지만 이 말보다는 "다른 프로그래머들에게 구현하라고 유도해야 할 메소드들의 선언"이라고 하는 편이 더 맞겠다.
  > 오브젝트의 클래스 자체는 숨기면서 그에 대한 인터페이스를 만들어야 할 때.
  > 계층적으로는 전혀 상관 없는 클래스들이지만 뭔가 상호연관성을 가지게 해야 할 필요가 있을때.

- 어떤 한 객체 A 가 있는데, 그것이 helpout이라는 메시지를 다른 객체 B에 보내서 어떤 일을 수행해 달라고 요청한다고 하자. 이때 현재 코딩하고 있는 객체 A에 다음과 같은 함수를 이용해서 객체 B를 기록해 놓을 수 있겠다.
- setAssistant:anObject
{
    assistant = anObject;
}
그리고 이 객체에 메시지를 보낼때는, 그 메시지를 처리하는 측에선 자신이 그 메시지를 처리할 수 있는지를 검사하는 루틴을 넣어야겠다.
- (BOOL)doWork
{
    ...
    // helpOut이라는 메시지에 해당 객체, 즉 assistant가 가르키고 있는 객체가 반응을 하는지를 알 수 있다.
    if ( [assistant respondsTo:@selector(helpOut:)] )
    {
        [assistant helpOut:self];

        return YES;
    }
    return NO;
}
  -> 객체B, 즉 assistant가 protocol을 따르도록 하라는 건지? 이 예제만으로는 protocol과는 관련이 없는데?
  > Apple의 문서에서는 protocol을, 정의되어 있지 않은 객체에 메시지를 전달하고자 할 때 쓸 수 있다라고 되어 있다. 즉 아직 만들지 않은, 혹은 다른 사람이 작업하고 있어서, 그 클래스의 구조를 정확하게 모를때, 그 클래스에 어떤 메시지를 전달하는 여러분의 루틴을 만들 수 있다는 말이다. Objective-C는 이렇듯 대상에 대한 정보가 없어도 되는 메커니즘을 가지고 있다.
  -> 이거에 대한 예제라면 OK. 객체B, 즉 assistant의 구조를 몰라도 메시지-helpOut-에 반응하는지를 동적으로 체크해서 전달 가능.


[Objective C] Root Class

프로그래밍/iPhone Dev 2009. 11. 20. 14:34 Posted by galad
Root Class

- Root 클래스라 함은 상속 관계에서 가장 부모 node의 클래스
- super로 메시지를 보낼 수 없다.
  -> 당연...
- class object는 root class에 정의된 instance method를 억세스할 수 있다.
(보통은 class 오브젝트는 instance method를 실행할 수 없지만 root class의 경우엔 예외이다. )

[Objective C] Category

프로그래밍/iPhone Dev 2009. 11. 18. 08:59 Posted by galad
Category

- Objective-C를 보자. B가 ClassC를 확장하는데 Category를 사용했고 그 카테고리 이름을 CategoryB라고 지었다고 하자. 그리고 A는 ClassC를 확장하는데 CategoryA라고 이름을 지었다고 하자. 수평적 확장으므로 레이어가 ClassA에서 두개밖에 없을 뿐 아니라, B가 추가한 부분이 A가 확장한 ClassC 클래스엔 들어 있지 않다!
아.. 얼마나 좋은가? 멋지지 않은가?
 -> Category를 쓰면 수평적 확장이 가능하단 얘기.
 즉, 
                  ClassA
                  I         I
       CategoryA    CategoryB

- 카테고리 안에 정의된 method는 ClassC의 원래 method의 이름과 같은게 있을 수도 있다. 이때는 기존의 것을 override하는 것이 된다!
하지만 이때는 상속 받은 것과 달리 기존의 것을 새로운 것이 invocation을 할 수가 없다.
그냥 완전히 새걸로 바꾸는 것이다. 이것은 장점이기도 하고 경우에 따라 단점이기도 하다.
 -> 같은 이름의 method로 override하면 완전히 대체하는 것이 된다...

- 같은 클래스에 대한 다른 카테고리에 있는 것을 override하는 것은 안된다.
 ->당연하겠지. 수평적 확장이니까.

- 기존의 클래스에 변수를 추가해야 할 경우엔?
 -> 상속을 써야함... Category는 method만 추가 가능.

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

[Objective C] Protocol  (0) 2009.11.20
[Objective C] Root Class  (0) 2009.11.20
[Objective C] 15 NSDictionary  (0) 2009.11.13
[Objective C] 14 NSArray  (0) 2009.11.13
[Objective C] 13 Autorelease Pool  (1) 2009.11.13

[Objective C] 15 NSDictionary

프로그래밍/iPhone Dev 2009. 11. 13. 15:44 Posted by galad
NSDictionary
java의 map.
NSMutableDictionary 도 소트는 안되는 듯.

main.m
#import <Foundation/NSString.h>
#import <Foundation/NSAutoreleasePool.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSEnumerator.h>
#import <Foundation/Foundation.h>
#import <stdio.h>

void print( NSDictionary *map ) {
    NSEnumerator *enumerator = [map keyEnumerator];
    id key;

    while ( key = [enumerator nextObject] ) {
        printf( "%s => %s\n",
                [[key description] cString],
                [[[map objectForKey: key] description] cString] );
    }
}

int main( int argc, const char *argv[] ) {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSDictionary *dictionary = [[NSDictionary alloc] initWithObjectsAndKeys:
        @"one", [NSNumber numberWithInt: 1],
        @"two", [NSNumber numberWithInt: 2],
        @"three", [NSNumber numberWithInt: 3],
        nil];
    NSMutableDictionary *mutable = [[NSMutableDictionary alloc] init];

    // print dictionary
    printf( "----static dictionary\n" );
    print( dictionary );

    // add objects
    [mutable setObject: @"Tom" forKey: @"tom@jones.com"];
    [mutable setObject: @"Bob" forKey: @"bob@dole.com" ];

    // print mutable dictionary
    printf( "----mutable dictionary\n" );
    print( mutable );

    // free memory
    [dictionary release];
    [mutable release];
    [pool release];

    system("PAUSE");
    return 0;
}


결과
----static dictionary
1 => one
3 => three
2 => two
----mutable dictionary
bob@dole.com => Bob
tom@jones.com => Tom
계속하려면 아무 키나 누르십시오 . . .

- Foundation Framework classes의 references만 찾아봐도 양이 상당할 듯...ㅎㄷㄷ

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

[Objective C] Root Class  (0) 2009.11.20
[Objective C] Category  (0) 2009.11.18
[Objective C] 14 NSArray  (0) 2009.11.13
[Objective C] 13 Autorelease Pool  (1) 2009.11.13
[Objective C] 12 Dealloc  (1) 2009.11.12

[Objective C] 14 NSArray

프로그래밍/iPhone Dev 2009. 11. 13. 15:36 Posted by galad
Foundation framework classes
The Foundation framework is similar to C++'s Standard Template Library. Although since Objective-C has real dynamic types, the horrible cludge that is C++'s templating is not necessary. This framework contains collections, networking, threading, and much more.

NSArray

main.m
#import <Foundation/NSArray.h>
#import <Foundation/NSString.h>
#import <Foundation/NSAutoreleasePool.h>
#import <Foundation/NSEnumerator.h>
#import <stdio.h>

void print( NSArray *array ) {
    NSEnumerator *enumerator = [array objectEnumerator];
    id obj;

    while ( obj = [enumerator nextObject] ) {
        printf( "%s\n", [[obj description] cString] );
    }
}

int main( int argc, const char *argv[] ) {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSArray *arr = [[NSArray alloc] initWithObjects: @"Me", @"Myself", @"I", nil];
    NSMutableArray *mutable = [[NSMutableArray alloc] init];

    // enumerate over items
    printf( "----static array\n" );
    print( arr );

    // add stuff
    [mutable addObject: @"One"];
    [mutable addObject: @"Two"];
    [mutable addObjectsFromArray: arr];
    [mutable addObject: @"Three"];

    // print em
    printf( "----mutable array\n" );
    print( mutable );

    // sort then print
    printf( "----sorted mutable array\n" );
    [mutable sortUsingSelector: @selector( caseInsensitiveCompare: )];
    print( mutable );
   
    // free memory
    [arr release];
    [mutable release];
    [pool release];

    system("PAUSE");
    return 0;
}


결과
----static array
Me
Myself
I
----mutable array
One
Two
Me
Myself
I
Three
----sorted mutable array
I
Me
Myself
One
Three
Two
계속하려면 아무 키나 누르십시오 . . .

- There are two kinds of arrays (and of usually most data oriented Foundation classes) NSArray and NSMutableArray. As the name suggests, Mutable is changable, NSArray then is not. This means you can make an NSArray but once you have you can't change the length.
  NSArray는 길이 변경 불가.
- You initialize an array via the constructor using Obj, Obj, Obj, ..., nil. The nil is an ending delimiter.
  nil이 끝을 의미. 반드시 필요하겠군..
- In the print method, I used the method description. This is similar to Java's toString. It returns an NSString representation of an object.
  description 메소드가 NSString을 반환하고, cString이 그 내용을 반환.
- NSEnumerator is similar to Java's enumerator system. The reason why while ( obj = [array objectEnumerator] ) works is because objectEnumerator returns nil on the last object. Since in C nil is usually 0, this is the same as false.
  ( ( obj = [array objectEnumerator] ) != nil ) might be preferable.

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

[Objective C] Category  (0) 2009.11.18
[Objective C] 15 NSDictionary  (0) 2009.11.13
[Objective C] 13 Autorelease Pool  (1) 2009.11.13
[Objective C] 12 Dealloc  (1) 2009.11.12
[Objective C] 11 Retain and Release  (0) 2009.11.12
Autorelease Pool
- When you want to start doing more programming using NSString and other Foundation framework classes you need a more flexible system. This system is using Autorelease pools.
- When developing Mac Cocoa applications, the auto release pool is setup automatically for you.

main.m
#import <Foundation/NSString.h>
#import <Foundation/NSAutoreleasePool.h>
#import <stdio.h>

int main( int argc, const char *argv[] ) {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSString *str1 = @"constant string";
    NSString *str2 = [NSString stringWithString: @"string managed by the pool"];
    NSString *str3 = [[NSString alloc] initWithString: @"self managed string"];

    // print the strings
    printf( "%s retain count: %x\n", [str1 cString], [str1 retainCount] );
    printf( "%s retain count: %x\n", [str2 cString], [str2 retainCount] );
    printf( "%s retain count: %x\n", [str3 cString], [str3 retainCount] );

    // free memory
    [str3 release]; // alloc으로 생성한 것만 release. 나머지는 AutoreleasePool에서 처리

    // free pool
    [pool release];
   
    system("PAUSE");
    return 0;
}


결과
constant string retain count: 40387657
string managed by the pool retain count: 1
self managed string retain count: 1
계속하려면 아무 키나 누르십시오 . . .

- If you run this you'll notice a few things. One is that the retainCount of str1 is ffffffff.
  라는데 내 예제에선 이상한 숫자만... ㅡ.ㅡ;;
- The other is, I only release str3, yet this program is memory management perfect. The reason is the first constant string is added to the autorelease pool automatically. The other string is made using stringWithString. This method creates a string that is owned by NSString class, which also puts it in the auto release pool.
  str3 하나만 release 했지만 이 프로그램의 메모리 관리는 완벽하다는 군. str1, str2 생성 시의 방법이 string을 알아서 auto release pool에 넣는다는.
- It's important to remember, for proper memory management, that convience methods like [NSString stringWithString: @"String"] use autorelease pools, but alloc methods like [[NSString alloc] initWithString: @"String"] do not use autorelease pools for managing memory.
  stringWithString 같이 auto release pool을 사용하는 메소드를 쓰는 것이 편하다.
- There are two ways to manage memory in Objective-C: 1) retain and release or 2) retain and release/autorelease.
   For each retain, there must be one release OR one autorelease.


Auto Release Pool 사용 예제

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;
+(Fraction*) fractionWithNumerator: (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;
}

// Auto Release Pool
+(Fraction*) fractionWithNumerator: (int) n denominator: (int) d {
    printf( "Create object using auto release pool.\n");
   
    Fraction *ret = [[Fraction alloc] initWithNumerator: n denominator: d];
    [ret autorelease]; // retain count -1: 이후 auto release pool을 release 할 경우 자동 처리됨.

    return ret;
}

-(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;
}

// dealloc
-(void) dealloc {
    printf( "Deallocing fraction : %i\n", numerator );
}
@end


main.m
#import <Foundation/NSAutoreleasePool.h>
#import "Fraction.h"
#import <stdio.h>

int main( int argc, const char *argv[] ) {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    Fraction *frac1 = [Fraction fractionWithNumerator: 2 denominator: 5];
    Fraction *frac2 = [Fraction fractionWithNumerator: 1 denominator: 3];

    // print frac 1
    printf( "Fraction 1: " );
    [frac1 print];
    printf( "\n" );

    // print frac 2
    printf( "Fraction 2: " );
    [frac2 print];
    printf( "\n" );

    // this causes a segmentation fault
    //[frac1 release];

    // release the pool and all objects in it
    printf( "Auto Release Pool release.\n" );
    [pool release]; // 이 안에 담긴 object들이 모두 release 되면서 dealloc 됨.
   
    system("PAUSE");
    return 0;
}


결과
Create object using auto release pool.
Create object using auto release pool.
Fraction 1: 2/5
Fraction 2: 1/3
Auto Release Pool release.
Deallocing fraction : 2
Deallocing fraction : 1
계속하려면 아무 키나 누르십시오 . . .

- In this example, the method is a class level method. After the object is created, autorelease is called on it. Inside the body of the main method, I never call release on the object.
- The reason this works is because: for every retain, one release or autorelease must be called. The object's retain count starts out as 1, and I called autorelease on it once. This means 1 - 1 = 0. Once the autorelease pool is released, it counts the autorelease calls on all objects and decrements them with [obj release] with the same number of times autorelease was called per object.
- As the comment says, uncommenting that line causes a segment fault. Since autorelease was already called on the object, calling release on it, and then releasing the autorelease pool would attempt to call dealloc on an object that is nil, which is not valid. The end math is 1 (creation) - 1 (release) - 1 (autorelease) = -1.
  autorelease 하면 -1 되므로 생성 후 다시 release 하면 segmentation fault.
- Auto release pools can be dynamically created for large amounts of temporary objects. All one must do is create a pool, perform any large chunk of code that creates lots of temporary objects, then release the pool. As you may wonder, it this means it is possible to have more than one auto release pool at a time.
 Auto release pool도 동적으로 생성할 수 있다 -> 여러 개의 풀을 생성할 수 있다.

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

[Objective C] 15 NSDictionary  (0) 2009.11.13
[Objective C] 14 NSArray  (0) 2009.11.13
[Objective C] 12 Dealloc  (1) 2009.11.12
[Objective C] 11 Retain and Release  (0) 2009.11.12
[Objective C] 10 Protocols  (0) 2009.11.11

[Objective C] 12 Dealloc

프로그래밍/iPhone Dev 2009. 11. 12. 17:37 Posted by galad
Dealloc
When your object contains other objects, you must free them whenever you yourself dealloc. One of the nice advantages to Objective-C is you can pass messages to nil, so there isn't a lot of error checking to release an object.
객체가 다른 객체를 갖고 있으면, 객체가 dealloc될 때 포함하고 있던 다른 것도 free 시켜야함. Objective-C의 좋은 장점은 메시지를 nil에게 보낼 수 있기에 객체 release 시에 많은 에러 체킹이 없다는 점이라는 군.

아래 소스는 원 소스를 살짝 변경해서 retain/release를 이용해 인스턴스 변수의 dealloc이 자동으로 일어나는 것을 잘 보여줌.
원 소스는 파일 첨부

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;
}

// dealloc
-(void) dealloc {
    printf( "Deallocing fraction : %i\n", numerator );
}
@end


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

@interface AddressCard: NSObject {
    Fraction *first;
    Fraction *last;
    Fraction *email;
}

-(AddressCard*) initWithFirst: (Fraction*) f
                last: (Fraction*) l
                email: (Fraction*) e;
-(Fraction*) first;
-(Fraction*) last;
-(Fraction*) email;
-(void) setFirst: (Fraction*) f;
-(void) setLast: (Fraction*) l;
-(void) setEmail: (Fraction*) e;
-(void) setFirst: (Fraction*) f
        last: (Fraction*) l
        email: (Fraction*) e;
-(void) setFirst: (Fraction*) f last: (Fraction*) l;
-(void) print;
@end


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

@implementation AddressCard
-(AddressCard*) initWithFirst: (Fraction*) f
                last: (Fraction*) l
                email: (Fraction*) e {
    self = [super init];

    if ( self ) {
        [self setFirst: f last: l email: e];
    }

    return self;
}

-(Fraction*) first {
    return first;
}

-(Fraction*) last {
    return last;
}

-(Fraction*) email {
    return email;
}

-(void) setFirst: (Fraction*) f {
    [f retain]; // 새로 받는 것을 +1
    [first release]; // 기존 것을 -1. 이때 dealloc 됨
    first = f;
}

-(void) setLast: (Fraction*) l {
    [l retain];
    [last release];
    last = l;
}

-(void) setEmail: (Fraction*) e {
    [e retain];
    [email release];
    email = e;
}

-(void) setFirst: (Fraction*) f
        last: (Fraction*) l
        email: (Fraction*) e {
    [self setFirst: f];
    [self setLast: l];
    [self setEmail: e];
}

-(void) setFirst: (Fraction*) f last: (Fraction*) l {
    [self setFirst: f];
    [self setLast: l];
}

-(void) print {
    printf( "%i (%i) <%i>", [first numerator],
                                [last numerator],
                                [email numerator] );
}

// AddressCard가 dealloc될 때, 기존에 남아있던 클래스 변수들의 retain값을 모두 -1해서 dealloc 시킴
-(void) dealloc {
    [first release];
    [last release];
    [email release];

    [super dealloc];
}
@end


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

int main( int argc, const char *argv[] ) {
    Fraction *first =[[Fraction alloc] initWithNumerator: 1 andDenominator: 10];
    Fraction *last = [[Fraction alloc] initWithNumerator: 2 andDenominator: 20];
    Fraction *email = [[Fraction alloc] initWithNumerator: 3 andDenominator: 30];
    AddressCard *tom = [[AddressCard alloc] initWithFirst: first
                                            last: last
                                            email: email];

    // we're done with the strings, so we must dealloc them
    // 사실 여기서 dealloc되지는 않고 retainCount가 -1됨.
    printf( "Retain count. Before release - first: %i\n", [first retainCount] ); // AddressCard 생성 시에 +1됨
    [first release];
    [last release];
    [email release];
    printf( "Retain count. After release  - first: %i\n", [first retainCount] );
    system("PAUSE");

    // print to show the retain count
    printf( "Retain count: %i\n", [[tom first] retainCount] );
    [tom print];
    printf( "\n" );
    system("PAUSE");
   
    Fraction *first2 =[[Fraction alloc] initWithNumerator: 5 andDenominator: 50];
    printf( "Set first again\n" );
    [tom setFirst: first2]; // setFirst할 때 first2를 retain. first2 = 2. 그리고 tom에 있는 기존 first를 -1해서 dealloc시킴.
   
    [first2 release]; // 위와 마찬가지로 사용하지 않으므로 must delloc. first2 = 1.
    // setFirst 시에 retain했으므로 release해도 아직 1. delloac되지는 않았음
   
    printf( "Retain count: %i\n", [[tom first] retainCount] );
    [tom print];
    printf( "\n" );
   
    // free memory
    [tom release];

    system("PAUSE");
    return 0;
}


결과
Retain count. Before release - first: 2
Retain count. After release  - first: 1
계속하려면 아무 키나 누르십시오 . . .
Retain count: 1
1 (2) <3>
계속하려면 아무 키나 누르십시오 . . .
Set first again
Deallocing fraction : 1
Retain count: 1
5 (2) <3>
Deallocing fraction : 5
Deallocing fraction : 2
Deallocing fraction : 3
계속하려면 아무 키나 누르십시오 . . .

- 위의 Deallocing fraction : X 메시지에서 보다시피 setXXX 하면서 retain/release 하는 것과 dealloc될 때 release 하는 것으로
포함하고 있는 객체를 자동으로 dealloc 시킬 수 있음.
- This example shows not only how to make a dealloc method, as shown in AddressCard.m, but one way to do member variables.
 dealloc 메소드 사용법 뿐 아니라, 멤버 변수 취급 방식에 대한 예제임.
- The order of the 3 operations in each set method is very important. Lets say you'return passing a parameter of yourself to one of your methods (a bit of an odd example, but this can happen). If you release first, THEN retain you will destruct yourself! That's why you should always 1) retain 2) release 3) set the value.
 set 시에는 반드시 매개변수 retain 후에, 멤버변수 release 할 것.
 만약 넘겨 받은 매개변수를 return하는 경우에 release를 먼저하면 그 때 매개변수가 dealloc되어버린다.
 위의 예제에선 상관없으나 습관의 문제일 듯. 로직상으로도 먼저 retain하는 것이 옳고...
- Normally one wouldn't initialize variables with C strings because they don't support unicode. The next example, with NSAutoreleasePool shows the proper way to do strings and initializing.
- This is just one way of handling member variable memory management. One way to handle this is to create copies inside your set methods.
  이건 멤버변수 메모리 관리의 한 방법. 다른 방법은 set 메소드에서 카피를 생성하는 방법.

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

[Objective C] 14 NSArray  (0) 2009.11.13
[Objective C] 13 Autorelease Pool  (1) 2009.11.13
[Objective C] 11 Retain and Release  (0) 2009.11.12
[Objective C] 10 Protocols  (0) 2009.11.11
[Objective C] 09 Posing  (0) 2009.11.10
Retain and Release
Retain and release are two methods inherited from any object that has NSObject as a parent. Each object has an internal counter that can be used to keep track of the number references an object has. So if you have 3 referneces, you don't want to dealloc yourself. However once you reach 0, you should dealloc yourself. [object retain] increments the counter by 1 (which starts at 1) and [object release] decrements it by 1. If the [object release] invocation causes the count to reach 0, dealloc is then called.

alloc 하면 1, retain 하면 +1, release -1, 0 되면 자동 dealloc 콜.
주소만 참조하는 걸로는 +1 안됨.

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;
}

// dealloc
-(void) dealloc {
    printf( "Deallocing fraction\n" );
    [super dealloc];
}
@end


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

int main( int argc, const char *argv[] ) {
    Fraction *frac1 = [[Fraction alloc] init];
    Fraction *frac2 = [[Fraction alloc] init];

    // print current counts
    printf( "Fraction 1 retain count: %i\n", [frac1 retainCount] );
    printf( "Fraction 2 retain count: %i\n", [frac2 retainCount] );

    // increment them
    [frac1 retain]; // 2
    [frac1 retain]; // 3
    [frac2 retain]; // 2

    // print current counts
    printf( "Fraction 1 retain count: %i\n", [frac1 retainCount] );
    printf( "Fraction 2 retain count: %i\n", [frac2 retainCount] );

    // decrement
    [frac1 release]; // 2
    [frac2 release]; // 1

    // print current counts
    printf( "Fraction 1 retain count: %i\n", [frac1 retainCount] );
    printf( "Fraction 2 retain count: %i\n", [frac2 retainCount] );
   
    // release them until they dealloc themselves
    [frac1 release]; // 1
    [frac1 release]; // 0
    [frac2 release]; // 0
   
    // 다른 변수가 참조하면?
    Fraction *frac3 = [[Fraction alloc] init];
    printf( "Fraction 3 retain count: %i\n", [frac3 retainCount] );
    [frac3 retain]; // 2
    printf( "Fraction 3 retain count: %i\n", [frac3 retainCount] );
    Fraction* frac4 = frac3; // 참조해도 retainCount가 늘어나지는 않음.
    printf( "Fraction 3 retain count: %i\n", [frac3 retainCount] );
    [frac3 release]; // 1
    printf( "Fraction 3 retain count: %i\n", [frac3 retainCount] ); // 1
    printf( "Fraction 4 retain count: %i\n", [frac4 retainCount] ); // 1
    system("PAUSE");
    [frac4 release]; // 0 - frac3 또는 frac4 둘 중 하나만 release 해도 retainCount가 감소. 같은 주소이므로 당연.
    system("PAUSE"); // retainCount가 0이 되었으므로 dealloc 됨.
    printf( "Fraction 3 retain count: %i\n", [frac3 retainCount] ); // dealloc된 frac3을 참조하려하므로 런타임 예외 발생
//    system("PAUSE");
//    printf( "Fraction 4 retain count: %i\n", [frac4 retainCount] );
    system("PAUSE");
    [frac3 release]; // 0
   
    system("PAUSE");
    return 0;
}


결과
Fraction 1 retain count: 1
Fraction 2 retain count: 1
Fraction 1 retain count: 3
Fraction 2 retain count: 2
Fraction 1 retain count: 2
Fraction 2 retain count: 1
Deallocing fraction
Deallocing fraction
Fraction 3 retain count: 1
Fraction 3 retain count: 2
Fraction 3 retain count: 2
Fraction 3 retain count: 1
Fraction 4 retain count: 1
계속하려면 아무 키나 누르십시오 . . .
Deallocing fraction
계속하려면 아무 키나 누르십시오 . . .

The retain calls increment the counter. The release calls decrement it. One can get the count as an int by calling [obj retainCount]. Once the retainCount reaches 0, both objects dealloc themselves and you can see this when both print out "Deallocing fraction."

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

[Objective C] 13 Autorelease Pool  (1) 2009.11.13
[Objective C] 12 Dealloc  (1) 2009.11.12
[Objective C] 10 Protocols  (0) 2009.11.11
[Objective C] 09 Posing  (0) 2009.11.10
[Objective C] 08 Categories  (0) 2009.11.09

[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