본문 바로가기

General/iOS

[iPhone] Object - creating / initializing / getting

CS 193P iPhone Application Development


2010년 가을학기 스탠포드 아이폰 애플리케이션 개발
강의 자료를 바탕으로 작성합니다.
 강의동영상 및 강의자료(Lecture-4)의 내용들입니다.

강의자료는 위 링크를 통해 다운 받을 수 있습니다.
(강의자료를 그대로 옮겨적은 것에 불과할 수 있습니다.)
(의역/오역된 내용이 있을 수 있습니다. 그런 경우 말씀해주세요.)


 Object

 Creating Objects

Object(객체)를 만들기 위해서는 alloc(Allocating)init(Initializing)의 두 단계를 거칩니다.
- Allocating : NSObject의 클래스 메소드 alloc
- Initializing : init으로 시작하는 메소드들

alloc은 class의 instance variables(클래스의 인스턴스 변수들)을 위한 영역(또는 공간)을 heap에 할당 받는 것입니다.

또 모든 클래스들은 designated init(initializer) method를 가지고 있습니다.
(관례에 의해서, 모든 init method들은 init 이라는 네 글자로 메소드 이름이 시작됩니다.)



 Initializing Objects

Subclass의 designated initializer에서 Superclass의 designated initializer를 호출하는 방법은 아래와 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@implementation MyObject
- (id)init
{
    if ([super init])
    {
        // initialize our subclass here
        return self;
    }
    else
    {
        return nil;
    }
}
@end
 
1
2
3
4
5
6
7
8
9
10
@implementation MyObject
- (id)init
{
    if (self = [super init])
    {
        // initialize our subclass here
    }
    return self;
}
@end



이 두가지 코드는 모두 사용이 가능한 코드 입니다.

왼쪽의 경우를 보면 if 의 조건안에 [super init]이 있습니다.
superclass의 designated initializer를 호출해서
nil(또는 zero)가 아닌 경우에 subclass에서 initialization 할 것들을 처리하고 self(자신)을 return 합니다.
[super init]을 통해서 만족하지 않는 값이 되돌아 온 경우 nil을 return 합니다. 
(YES가 아닌 값이 return 되거나, zero가 아닌 값이 return되지 않은 경우)

오른쪽의 경우를 보면 if 의 조건안에서 self = [super init] 이라는 이상한 문법을 사용하였습니다만, 제대로 처리 되는 문장입니다.
어쨋든, 여기서는 self(자신, 즉 여기서봤을 때 subclass)에 superclass의 designated initializer의 처리 결과를 받습니다.
그리고 self 를 if 의 조건으로 확인하는 것이죠.
self 가 만족스러운 값을 받았다면 subclass에서 initialization 하려는 내용들이 처리될 것이고, 그렇지 않은 경우에는 단지 self를 return 해줍니다.


여기서 중요한건, [super init]을 통해 nil 이 return 되어 온 경우에 대해서 입니다.
우리는 nil 이 return 되거나, zero 가 return 되는 경우에, 처리가 실패(fail)했다고 가정하게 됩니다.
하지만 nil (subclass가 nil 이라고 했을 경우)에 message를 send 하는 것은 절대로!! fail이 아닙니다.
그냥 아무것도 아닌게 되지요.

아래와 같은 경우 self 는 nil 일 수 있습니다. 

1
2
self = [super init];
return self;

[super init]에서 반환된 값이 nil 이라면 self 도 nil이 될 수 있습니다.

if-else문을 사용하여 nil 이 return 되는 것이나 initialize를 실패하는 경우를 체크한것이라 생각하겠지만, 실제로 상관없을 수 있다는 것입니다.

위의 두 문장을 비교해 보자면, 결론적으로 오른쪽처럼 사용하는 것이 지향될 수 있겠군요.



아래는 Convenience initializer를 사용한 예입니다.

1
2
3
4
5
6
7
8
@implementation CalculatorBrain
- (id)initWithValidOperations:(NSArray *)anArray
{
    self = [self init];
    self.validOperations = anArray; // will do nothing if self == nil
    return self;
}
@end

self = [self init];

이 코드를 보면 super init 이 아니라 self를 사용합니다. 코드에 나와 있지는 않지만,
subclass의 init 메소드는(subclass의 designated initializer) superclass의 designated initializer를 호출할 것이기 때문입니다.

그래서 initWithValidOperations 는 Subclass에서 우리가 만든 designated initializer이긴 하지만, 여전히 NSObject로 부터 상속받은 init 이 될 수 있습니다.

또 위의 코드를 보았을 때 의문이 들 수 있습니다.

self = [self init];
self.validOperations = anArray; // will do nothing if self == nil

이렇게 된 경우 self가 nil 이면? crash인가!? 라는 생각이 들 수 있습니다.
(위에서 언급하고 오기는 했지만) 이런 경우 주석에 이미 나와 있듯이 그냥 아무것도 아닌게 되어 버립니다.
nil 에 어떤 message를 보내는 것(send)은 아무것도 아니라는 말이지요.
그냥 말 그대로 아무것도 일어나지 않습니다. 애플리케이션이 crash되지 않는다는 말이지요.

Convenience initializer를 정리해보자면,
superclass의 designated initializer를 subclass에서 호출(call)하고 있기 때문에,
initWithValidOperations라는 이름의 convenience init methodself = [self init]; 이라는 문장을 사용해 편리하게 init을 수행 할 수 있게 되는 것입니다.



아래와 같은 경우는 나쁘게 설계된 예 입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@implementation CalculatorBrain
- (id)initWithValidOperations:(NSArray *)anArray
{
    if (self = [super init])
    {
        if ([anArray count])
        {
            // probably should also check validity
            self.validOperations = anArray;
        }
        else
        {
            self = nil;
        }
    }
    return self;
}
 
- (id)init
{
    // ugh! but necessary since validOperations can’t be nil
    return nil;
}
@end



UIView를 상속한 subclass의 두가지 구현에 대한 예입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@implementation MyView
- (id)initWithFrame:(CGRect)aRect
{
    if (self = [super initWithFrame:aRect])
    {
        // initialize my subclass here
    }
    return self;
}
 
- (id)initToFit:(Shape *)aShape
{
    CGRect fitRect = [MyView sizeForShape:aShape];
    return [self initWithFrame:fitRect];
}
@end

이 경우는 initWithFrame 이라는 designated initializer를 그대로 이용하면서, 
initToFit 이라는 convenience initializer 를 만들어 사용한 경우입니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@implementation MyView
- (id)initWithFrame:(CGRect)aRect
{
    return [self initToFit:[self defaultShape]];
}
- (id)initToFit:(Shape *)aShape
{
    CGRect fitRect = [MyVew sizeForShape:aShape];
    if (self = [super initWithFrame:fitRect])
    {
        // initialize my subclass here
    }
    return self;
}
@end
 
이 경우는 initToFit 이 subclass의 designated initializer 로 만들어진 것이고,
initWithFrame 이 convenience initializer 로 만들어진 경우입니다.




 Getting Objects

위에서 설명한 alloc/init 이외에도 Object(객체)를 GET 할 수 있는 방법이 있습니다.
(여기서 get이라고 하는 것들은 alloc/init이 아니면서 object를 생성하거나 획득 하는 방법을 말하는 것입니다.)

1
2
3
4
5
NSString *newDisplay = [display.text stringByAppendingString:digit];
NSArray *keys = [dictionary allKeys];
NSString *lowerString = [string lowercaseString];
NSNumber *n = [NSNumber numberWithFloat:42.0];
NSDate *date = [NSDate date]; // returns the date/time right now

위에 나온 클래스들을 포함해 여러 클래스들이 Object를 GET 할 수 있도록 해줍니다.

하지만, alloc/init 과 위에 나온 클래스, 메소드들은 이렇게 GET한 object들의 메모리(heap에 할당된)를 frees(해제) 해주지 않습니다.

또한 garbage collection에 관해서는 iOS 플랫폼 자체에 포함된 영역이 아니구요.
(현재의 추세라면 iOS 버전업이 되면서 garbage collection 같은 개념이 나올거라 예상됩니다.)

(anyway,) 그런 이유로 모바일 디바이스에서 애플리케이션을 원활하게 작동시키기 위해 Object를 관리하는 기법들을 알아야 합니다. 이런 얘기들은 [iPhone] Memory Management 에서 하도록 하겠습니다.




 아이폰 개발 관련
 

 [iPhone] Methods Syntax
 [iPhone] Object - creating / initializing / getting
 [iPhone] Memory Management