[iPhone] iOS6 회전 지원하기

아이폰 2013. 1. 25. 01:17



iOS6.0 부터 회전을 지원하는 delegate 메소드가 조금 변경되었습니다.


iOS6.0 업데이트 후 회전이 올바로 동작하지 않는 것을 보고 적잖이 당황했었습니다. 


관련내용을 찾아보니 의외로 어렵지 않게 6.0에서도 회전에 대응할 수 있었습니다~ 


그럼 6.0 이상 및 하위버전에서 회전을 올바로 지원하기 위한방법에 대해서 알아봅시다





홈 버튼을 기준으로 왼쪽으로 회전, 오른쪽으로 회전, 거꾸로 뒤집었을 때 등 각 방향을 지칭하게 됩니다!








먼저 기존에는 회전을 지원하기 위해 아래와 같은 스텝을 거쳤습니다.



1. Window에 View 추가 


[self.window addSubview:viewController.view]




2. UIViewController 에 지원 회전방향 delegate 구현 


// 자동회전 지원하는 방향 
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    return (interfaceOrientation == UIInterfaceOrientationPortrait || 
               interfaceOrientation == UIInterfaceOrientationLandscapeLeft ||            
               interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}



3. UIViewController 에서 회전하기 전/후 호출되는 delegate 구현 


- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
    // 회전하기 전에 호출됩니다.
}

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
   // 회전 후에 호출됩니다.
}



shouldAutorotateToInterfaceOrientation의 UIInterfaceOrentation 파라미터 타입은 다음과 같이 

enum 타입 상수로  선언되어 있습니다.


typedef NS_ENUM(NSInteger, UIInterfaceOrientation) {
    UIInterfaceOrientationPortrait           = UIDeviceOrientationPortrait,
    UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
    UIInterfaceOrientationLandscapeLeft      = UIDeviceOrientationLandscapeRight,
    UIInterfaceOrientationLandscapeRight     = UIDeviceOrientationLandscapeLeft
};




iOS 6.0 에서는 무엇을 바뀌었을까요?



iOS6.0에서는 1번과 2번 스텝이 변경되었습니다. 




1. Window에 rootViewController 추가 


self.window.rootViewController = viewController;


기존에는 Window의 View를 추가했지만, iOS6.0 부터는 반드시 Window의 rootViewController로 지정해야 합니다. 





2. UIViewController 에 지원 회전방향 delegate 구현 


//  자동회전 지원 유무 
- (BOOL) shouldAutorotate {
      return YES; 
}
// 지원하는 회전방향 
- (NSUInteger)supportedInterfaceOrientations {
    return (1 << UIInterfaceOrientationPortrait) | (1 << UIInterfaceOrientationLandscapeLeft) | (1 << UIInterfaceOrientationLandscapeRight);
}




기존에 2가지 역할을 하던 메소드가 자동회전 유무와 회전하는 방향을 반환하는 메소드로 각각 분리되었습니다.


또 지원하는 회전방향의 경우,  NSUInterger 반환값으로 변경되었는데 이 값은 지원하는 회전방향의 비트가 셋팅된


비트 플래그 값입니다.


typedef NS_OPTIONS(NSUInteger, UIInterfaceOrientationMask) {
    UIInterfaceOrientationMaskPortrait = (1 << UIInterfaceOrientationPortrait),
    UIInterfaceOrientationMaskLandscapeLeft = (1 << UIInterfaceOrientationLandscapeLeft),
    UIInterfaceOrientationMaskLandscapeRight = (1 << UIInterfaceOrientationLandscapeRight),
    UIInterfaceOrientationMaskPortraitUpsideDown = (1 << UIInterfaceOrientationPortraitUpsideDown),
    UIInterfaceOrientationMaskLandscape = (UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight),
    UIInterfaceOrientationMaskAll = (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft |
                                                   UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskPortraitUpsideDown),
    UIInterfaceOrientationMaskAllButUpsideDown = (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | 
                                                                         UIInterfaceOrientationMaskLandscapeRight),
};



1을 UIInterfaceOrentation 상수값 만큼 왼쪽으로 시프트해주면, 해당하는 방향의 비트를 셋팅할 수 있지요.



또 한가지 중요한 사항은, 커스텀 UINavigationController와 UITabBarController를 사용한 경우(상속해서)


이 커스텀 클래스의 회전 지원 delegate 메소드에서는 topViewController의 회전지원 delegate 메소드로 


위임해야 한다는 것입니다. 아래와 같이 말이죠!



...
- (BOOL) shouldAutorotate {
    return [topViewController shouldAutorotate];
}
...







다음은 iOS6.0 이상 및 미만에서 회전을 올바로 처리하기 위한 코드를 정리한 것입니다.



- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    ...
    // iOS6.0 이상
    NSString *currentVersion = [[UIDevice currentDevice] systemVersion];
    
    NSLog(@"%@", currentVersion);
    
    if ([currentVersion compare:@"6.0" options:NSNumericSearch] != NSOrderedAscending) {
        self.window.rootViewController = self.viewController;
    } else {
        // 하위버전
        [self.window addSubview:self.viewController.view];
    }
    [self.window makeKeyAndVisible];
    return YES;
}

#pragma mark - 회전지원 iOS6.0 이상

// 자동회전 지원유무
- (BOOL) shouldAutorotate {
    return YES;
}

// 회전방향 지원유무
- (NSUInteger)supportedInterfaceOrientations {
    return (1 << UIInterfaceOrientationPortrait) |
           (1 << UIInterfaceOrientationPortraitUpsideDown) |
           (1 << UIInterfaceOrientationLandscapeLeft) |
           (1 << UIInterfaceOrientationLandscapeRight);
}

#pragma mark - 회전지원 iOS6.0 미만 

// 회전방향 지원유무
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
            interfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
            interfaceOrientation == UIInterfaceOrientationLandscapeRight ||
            interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown
            );
}


#pragma mark - 회전 델리게이트 

// 공통
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
    // 회전하기 전에 호출됩니다.
}

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
    // 회전 후에 호출됩니다.
}




소스코드 다운로드

Rotation.zip





[iPhone] UIView 애니메이션과 코어 애니메이션(CoreAnimation)

아이폰 2013. 1. 23. 01:43



아이폰에서는 애니메이션을 쉽게 구현할 수 있는 수단들을 제공한다


UIView의 위치 이동, 크기변경, 회전, 페이드인/아웃 등과 간단한 애니메이션은 UIView 애니메이션을


사용해서 구현할 수 있다. UIView 클래스에 애니메이션을 위한 메서드들이 존재하며, 


iOS 4.0 부터는 블럭방식의 애니메이션 메소드를 제공해서 코드단에서 편리하게 구현할 수 있다. 


CoreAnimation은 CALayer 단에 적용되며, UIView 애니메이션 효과를 구현할 수 있을 뿐만 아니라  


좀더 복잡하고 세밀한 애니메이션 효과를 줄 수 있다.  하지만 UIView 애니메이션보다는 코딩량이 좀더 많고 번거롭다 


간략히 UIView 애니메이션과 코어 애니메이션을 사용하는 방법에 대해서 알아보자


다음은 UIView를 사용하여 구현한 푸시 애니메이션(네비게이션 푸시가 아니라, 버튼이 움푹 눌리는 듯한 효과)과 


CoreAnimation을 사용하여 구현한 플립 애니메이션(y축을 기준으로 뒤집어 지는 듯한 효과)이다 




1. UIView 애니메이션을 사용한 푸시효과




구현방식은 터치가 발생하면 x, y축 스케일(scale)을 0.3초 동안 0.9로 축소하고


다시 0.3 초 동안 1.0으로 되돌아 오는 효과를 적용했다. UI 요소가 눌리는 듯한 효과를 줄 수 있으며, 인터렉티브한 


느낌을 줄 수 있어서 자주 사용하는 효과중에 하나이다. 


 [UIView animateWithDuration:0.3 animations:^(void) {
        self.transform = CGAffineTransformMakeScale(0.9, 0.9);
    } completion:^(BOOL finished) {
        [UIView animateWithDuration:0.3 animations:^(void) {
            self.transform = CGAffineTransformIdentity;
        }];
    }];


animations 블럭에서 속성을 변경하는 코드가 비동기 방식으로 (메인쓰레드에서) 애니메이션되는 방식이다. 


위 코드는 가로, 세로 Scale을 각각 0.9로 변환하는 애니메이션을 0.3초 동안 수행하고, 


애니메이션이 완료(completion) 하면 원래 스케일로 복원하는 애니메이션을 다시 0.3초 동안 수행한다. 


CGAffineTransform 즉 아핀변환은 벡터에서 등장하는 용어로 변환을 행렬을 사용해서 나타낸 것이라는 정도만 알아두자.





2. CoreAnimation을 사용한 플립효과 




CoreAnimation 은 UIView가 아닌 CALayer에 적용되며, 애니메이션할 CALayer의 property를  지정하고 


애니메이션 상태를 좀더 세밀하게 제어할 수 있는 속성을 제공한다. 


플립 애니메이션을 구현하는 동안 주의할 점은  y축을 기준으로 회전을 하면 이미지가 좌우가 뒤집어 지는 


현상이 발생한다는 것이다.  레이어를 회전시켰기 때문에 뒤집어 지는 것이다. 





이문제는 먼저 90도를 회전 시키고, -90로 뒤집은 상태에서 다시 나머지 회전을 수행하는 방식을


사용하면 해결 할 수 있다. 


- (void)flip {
    // 90도 회전 
    CABasicAnimation *rotateY = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
    rotateY.beginTime = 0.0;
    rotateY.toValue = [NSNumber numberWithFloat:radians(90)];
    rotateY.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
    rotateY.autoreverses = NO;
    rotateY.repeatCount = 1;
    rotateY.removedOnCompletion = NO;
    rotateY.duration = 0.4;
    rotateY.fillMode = kCAFillModeForwards;
    
    // -90도로 뒤집은 상태에서 나머지 회전
    CABasicAnimation *rotateY2 = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
    rotateY2.beginTime = 0.4;
    rotateY2.fromValue = [NSNumber numberWithFloat:radians(-90)];
    rotateY2.toValue = [NSNumber numberWithFloat:radians(0)];
    rotateY2.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
    rotateY2.autoreverses = NO;
    rotateY2.repeatCount = 1;
    rotateY2.removedOnCompletion = NO;
    rotateY2.duration = 0.4;
    rotateY2.fillMode = kCAFillModeForwards;
    
    CAAnimationGroup *group = [CAAnimationGroup animation];
    group.animations = [NSArray arrayWithObjects:rotateY, rotateY2, nil];
    group.duration = 0.8;
    group.autoreverses = NO;
    group.repeatCount = 1;
    group.removedOnCompletion = NO;
    group.fillMode = kCAFillModeForwards;
    group.delegate = self;
    
    [self.layer addAnimation:group forKey:nil];   
}

CAAnimationGroup은 동시에 여러 애니메이션을 복합적으로 적용할 때 사용하며,  CABasicAnimation의  


beginTime 속성을 사용해서 애니메이션 시작 시점을 조정할 수 있다. 총 0.8초 동안의 시간동안 처음 0.4초 동안


90도 회전하고, 0.4초 후 -90도 뒤집어진 상태에서 0도로 0.4초 동안 회전시켜 마치 카드가 자연스럽게 


뒤집어 지는 듯한 플립효과를 구현하였다


거의 대부분의 애니메이션은 UIView 애니메이션으로 구현할 수 있지만 


UI/UX에 특화된 좀더 재미나고 멋진 효과를 가진 앱을 개발하고 싶다면 코어 애니메이션 사용을 주저하지 말자!



소스코드

AnimButtton_for_bloging.zip





[Mask 레이어 활용] 이미지 일부만 보여주기

아이폰 2013. 1. 22. 01:31



마스크 레이어를 활용하여 이미지의 일부부만 노출시키는 예제를 작성해 보았다. 


핵심은 마스크 레이어를 사용하면 터치 이벤트를 수신할 수 없기 때문에 


상위에 터치 이벤트를 수신하여 마스크 레이어 단에 이벤트를 전달해주는 투명뷰


덮는 것 !


간단한 예제인데 fx의 설리와 크리스탈을 배경  이미지로 사용하니 재미있는 예제가 됐다. 


(나도 이제 나이 들었나봐 ㅠㅜ 바라만 봐도 행복하구나 ㅋ)


아래는 예제를 실행한 사진이다. 





자! 이제 어떻게 예제를 구현했는지 알아보자!



구현방식을 간단히 그림으로 나타내 보면 아래와 같다. 





SBTouchView 를 완전히 투명하게 해서는 안된다. 완전히 투명할 경우에는 터치 이벤트를 수신하지 못하기 때문에 


투명도를 0.1로 하는 꼼수를 발휘했다.   SBTouchView는 터치 이벤트를 단순히 nextResponder 에게 전달하고, 


SBMaskedView가 그 이벤트를 받게되고, 마스크 영역을 이동시킨다. 


구현 절차는 아래와 같다. 


1. 마스크 레이어 설정 


maskLayer = [[CAShapeLayer layer] retain];
maskLayer.backgroundColor = [UIColor whiteColor].CGColor;
        
pathRect = CGRectMake(1, 1, 118, 118);
        
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:pathRect];
maskLayer.path = path.CGPath;
maskLayer.fillColor = [UIColor blackColor].CGColor;



핵심은 레이어의 배경색을 흰색으로 처리하고, 마스킹 시킬 영역(Path)만 검정색으로 칠하는 것입니다.





2. 배경레이어에 마스크 설정 

// 배경이미지 (크리스탈, 설리 이미지)
CALayer *bgLayer = [CALayer layer];

UIImage *bgImage = [UIImage imageNamed:@"christal.jpeg"];
bgLayer.frame = [self frameForImage:bgImage];
bgLayer.contents = (id)bgImage.CGImage;
bgLayer.mask = maskLayer;

[self.layer addSublayer:bgLayer];



배경(이미지) 레이어의 mask 속성에 마스크 레이어를 설정합니다.





3. 터치뷰를 설정 

// 터치 이벤트를 잡을 투명뷰 
SBTouchView *touchView = [[[SBTouchView alloc] initWithFrame:self.bounds] autorelease];
[self addSubview:touchView];

뷰의 최상위에 Touch Event를 가로챌 SBTouchView를 덮습니다. SBTouchView는 이벤트를 수신할 수 있게 
투명도를 0.1로 설정했습니다. 



// SBTouchView
@implementation SBTouchView

- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        self.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.1];
    }
    return self;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [[self nextResponder] touchesBegan:touches withEvent:event];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
    [[self nextResponder] touchesCancelled:touches withEvent:event];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    [[self nextResponder] touchesEnded:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    [[self nextResponder] touchesMoved:touches withEvent:event];
}


SBTouchView는 보시는 것처럼 nextResponder 에게 터치 이벤트를 전달합니다. 
그러면 아래에 있는 뷰가 이벤트를 받게됩니다.



4. 터치 이벤트를 수신하여 마스크 레이어 이동 시키기 


- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    CGPoint point = [touch locationInView:self];
    
    if (CGRectContainsPoint(pathRect, point)) {
        touchBegan = YES;
        touchPoint = point;
    }
}


- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    if (touchBegan) {
        UITouch *touch = [touches anyObject];
        CGPoint point = [touch locationInView:self];
        
        CGPoint destPoint = CGPointMake(pathRect.origin.x + (point.x - touchPoint.x),
                                 pathRect.origin.y + (point.y - touchPoint.y));

        pathRect.origin.x = destPoint.x;
        pathRect.origin.y = destPoint.y;
        UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:pathRect];
        maskLayer.path = path.CGPath;
        touchPoint = point;
    }
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    touchBegan = NO;
}



터치 영역이 마스크 레이어의 영역일 경우, 마스크 레이어의 위치를 이동시킵니다. 



소스 다운받기

Mask.zip




이상 마스크 레이어 사용법을 알아보면서 간단한 예제도 만들어 보았다. 


아이폰을 하면서 항상 느끼는 거지만...UI 프로그래밍이 생각보다 재밌다는 것!







  • ㅇㅈㅁ 2013.01.22 16:03 ADDR 수정/삭제 답글

    잘 배우고 갑니다 퍼갑니당!

    • 로그 @로그 2013.01.22 16:30 신고 수정/삭제

      ^^ 배우긴요~ 조금이라도 도움이 되셨으면 좋겠네요!

Objective-C 훑어보기

Objective-C 2011. 3. 20. 23:40



iPhone 의 인기와 더불어 주목받고 있는 Objective-C 언어에 대해서 간략히 알아보자. 
Objective-C 언어는 C 언어 기반위에 객체지향적인 요소를 가미한 언어로서, 객체지향 언어의 원류인  Smalltalk 에서
그 개념을 많이  차용했다고 한다.  이 포스팅에서는 언어의 구체적인 설명은 하지않고,  간략히 어떤 언어 요소들이
있는지만  알아보겠다. 



1. Objective-C 에서 클래스 정의 
#import "SuperClass.h"
@interface NewClass : SuperClass {
   int instanceVariable;
}
- method;
- methodWithParameter:param;

@end
@implementation NewClass

- method {
   return (nil);
}
- methodWithParameter:param {
   return (nil);
}



2. Object Pointers
SpecificClass * specificObject;
id anyObject;
id 타입은 void * 형 포인터로 어떤 객체 포인터도 할당하거나 할당받을 수 있다.


3. Sending Messages
[object method];
[object methodWithParam:param];

objective-c에서는 메시지를 호출한다고 하기 보단, 객체에게 메세지를 보낸다고 한다. 



3. Naming Method

Objective-C에서는 메시지의 이름을 부여할 때,  메소드가 취하는 행위를 기술하도록 이름짓는다. 메시지의 이름은
다음과  같은 형식을 따른다.
반환타입+메시지행위+첫번째 인자이름: 첫번째 인자 이름:  두번째 인자이름...
반환타입이나 인자가 존재하지 않는 경우 생략될 수 있다. 


- valuePrepositionParameter:parameter
- objectForKey:key
- stringByAppendingString:string;
- numberFromString:string;




4. Parameter and Return Types
인자와 반환값은 괄호사이에 기술한다
- (id)objectForKey:(id)aKey;
- (NSMenuItem)itemWithTag:(NSInteger)aTag;
- (unichar)characterAtIndex:(NSUInteger)index;
- (NSString*)stringByAddingPercentEscapeUsingEncoding:(NSStringEncoding)encoding;
- (void)runInNewThread;
- (void)addAttribute:(NSString*)name value:(id)value range:(NSRange)aRange;



5. Method Selectors
메서드의 이름, 인자변수의 이름, 인자의 타입이 유일한 식별자를 구성하며, Java 의 메소드 시그니처와 동일한 의미를 갖는다. 
(NSfont*)fontWithFamily:(NSString*)family traits:(NSFontTraitMask)fontTraitMask weight:(NSInteger)weight size:(CGFloat)size

의 메소드 식별자는  fontWithFamily:family:traits:weight:size 가 된다.  이 식별자는  실제로 selector 라 불리는 숫자 상수와 
맵핑이 되며, 그 숫자 상수는 SEL 타입을 갖는다.  @selector()  지시어는  특정 메소드의 selector  상수를 반환한다
SEL selector = @selector(fontWithFamily:traits:weight:size)


6. Instance Variable

isa 인스턴스 변수
모든 Objective-C 객체가 상속받는 인스턴스 변수로,  클래스 자체를 정의하는  Class  타입의 객체이다.
- (Class)class 메소드 호출을 통해 얻을 수 있으며, - (NSString *)className 통해 클래스의 이름을 문자열로 얻을 수 있다.
 

Properties
객체의 프로퍼티를 정의하는 키워드로 @property  와 @synthesize 가 있다. @property 는 클래스가 계약을 지킬 것이라고 컴파일러에게 알려주는 역할을 한다.  @synthesize 는 컴파일러에게 해당 인스턴스 변수에 대한 getter와 setter  를 생성하라고 지시한다    
 int tag;

라는 인스턴스 변수에 대해 @property int tag; @synthesize tag;를  선언되면 컴파일러는
- (void)setTag:(int)newTag  
- (int)tag

와 같은 한쌍의 접근자를 생성할 것이다. 이렇게 생성된 접근자는 디폴트로 threadsafe 하다. @property만 선언했다면, 구현파일에
 getter 와 setter를 기술하는 것은 프로그래머의 몫이다. 즉 @synthesize를 통해 자동생성하거나,   프로그래머가 직접 구현해줘야 한다. 그렇지 않으면 컴파일 에러가 발생할 것이다.

@property 지시어와 함께 사용할 수 있는 속성들이 있는데, getter와 setter울 생서을 위한 속성이 된다. 

 Property Attributes 

 Attribute  Description
 readonly  getter 만을 생성한다. readwrite 속성과 상반된다. 
 readwrite  디폴트 값이며,  getter 와 setter 를 모두 생성한다. 
 copy  setter 에서 인자를 인수변수에 저장할 때, 인자의 복사본을 저장한다.  객체 포인터 타입에만 유효한 속성이다.  (primitive 타입은 항상 복사되기 때문) 리시버가 센더의 인자값을 변경하지 못하게
할 때 사용한다. 
 assign  인스턴스 변수에 레퍼런스 카운팅 없이 단순 할당한다.  가비지 컬렉션이 존재하는 환경에서 참조를 유지하고 싶거나, 인자값이 변경불가능할 때 사용한다. 
 retain  assign과 유사하며, 가비지 컬렉션이 없는 Managed memory 환경에서 객체의 참조 카운터를 증가
시킨다. 
 getter=name 지정한 name 이름으로 getter 를 생성한다.  getter와 setter의 표준 패턴을 위반해서  Key-Value Coding 과 같은 기술들이 인식할 수 없게 될수도 있기 때문에 사용에 주의해야한다. 
 setter=name 지정한 name 이름으로 setter 를 생성한다.
 nonatomic 디폴트로 생성된  setter 는  atomic 속성을 갖는다. 락을 사용하여 thread-safe 하지만, 상당한 오버헤드를 수반한다. 쓰레드의 개입이 없을 경우,  명시적으로 nonatomic 으로 선언하여 performance 를 높이는게 좋다.
 
 

7. Class Methods
클래스 메소드는 메소드 이름 앞에 + 기호를 갖는다. 모든 클래스는 런타임에  그 클래스 인스턴스의 identity와 behavior를  정의하는 
하나의 Class  객체를 생성한다.  클래스는 이 Class 객체가 응답하는 메시지를 정의한다.  Objective-C의 클래스 메소드는 자바와 비슷한 역할을 하지만 기술적으로는 다르다.
@interface RandomSequece : NSObject {
long long int seed;
}
+ (NSNumber*)number;
+ (NSNumber*)string;
- (NSNumber*)nextNumber;
- (NSString*)string;
@end
...
NSNumber *n = [RandomSequence number];
NSString *s = [RandomSequence string];

관례적으로  클래스 메소드를 먼저 정의하고, 인스턴스 메소드를 정의한다. 자바에서 클래스메소드는 this 키워드를 사용할 수 없지만,  Objective-C 의 클래스메소드 에서는 self  키워드를 사용할 수 있다.  클래스 메소드에  self 는 Class 객체를 가리키며, 인스턴스 메소드 처럼 서브클래스에 상속할 수 있으며 오버라이딩 또한 가능하다.

@interface Classy : NSObject {
   + (void)greeting;
+ (NSString*)salutation;
}
@end

@implementation
+ (void)greeting {
NSLog(@"%@, world!", [self salutation]);
 }
+ (NSString*)salutation {
NSLog(@"Greetings");
}
@end
@interface Classic : Classy
   + (NSString*)salutation;
@end
   + (NSString*)salutation {
     return (@"Hello");
}
@end
...
[Classy greeting];    // Logs "Greetings, world"
[Classic greeting];   // Logs "Hello, world!" 

[Classic greeting]  은 Classy Class 객체에 메시지를 보내고,   [self salutation]은 해당 Cass 클래스 객체에 메시지를 보낸다.
[Classic greeting] 은 greeting 메소드가 없지만, 수퍼클래스로부터 상속받은 greeting 메시지를 호출하며, [self salutation] 은 Classic
Class 객체에 메시지를 보낸다. 

 

8. Constructing Objects
java 에서는 객체를 생성하는 문법을 존재하여, 객체를 생성하는 방법을 명시적으로 제약한다.  반면 Objective-C 는 객체를
생성하는 방법을 클래스 설계자가 결정하도록 한다. 
 
객체를 생성하는 과정은 2가지 과정을 거친다. 
1) 메모리 구조 할당
2) 인스턴스 초기화

@interface RandomSequence : NSObject {
long long seed;
}
- (id)init;
- (id)initWithSeed:(long long)startingSeed;
@end

@implementation RandomSequence
- (id)init {
self  = [super init];
if (self != nil) {  
   seed = 1;
}
return (self);
}
- (id)initWithSeed:(long long)startingSeed {
self = [super init];
if (self != nil) {
seed = startingSeed;
}
return (self);
}
@end
...
RandomSequence *r1 = [[RandomSequence alloc] init];
RandomSequence *r2 = [[RandomSequence alloc] initWithSeed:-43 ];
RandomSequence * r3 = [RandomSequence new];

alloc 은 NSObject 에 정의된 클래스 메소드 +(id)alloc   이다.  객체를 생성하기 위해 클래스 참조를 사용하여 메모리를 할당하고,
그것을  isa 변수에 할당한다. 그리고 인스턴스를 모두 0으로 채운뒤, 참조를 반환한다. 이 시점에서 요청된 객체가 존재하지만
아직 초기화는 되지않았다.  init 메시는 객체를 초기화를 담당하며, 이메시지가  반환되면 깨체는 사용할 준비가 된 상태가 된다. 
new 도 NSObject의 클래스 메소드로 내부적으로 alloc과 init 를 호출한다

- (id)init...계열의 메소드를 오버라이드 하거나 재정의 해서 객체생성을 커스터마이징 할 수 있다. 
+ (id)alloc  또는 +(id)new 를 재정의 해서는 안된다. 



9. Writing an init Method
Objective-C에서 생성자를 작성하기 위해서 4가지 계약을 충족해야 한다
1) 수퍼 클래스의 생성자를 호출한다  :  [super init]
2) 자신의  self 변수를 업데이트 한다  :  self = [super init]
3) nil 객체 검사를 한다                          : if (self != nil ) { ... }
4) 자신의 포인터를 반환한다                : return self

모든 생성자는 수퍼클래스의 생성자를 호출해야한다. 자바는 언어 차원에서 이를 지원하지만,  Objective-C 는 프로그래머에게
책임이 있다.  self 를 변경하는 것은 자바 프로그래머에게는 이상하지만,  Class Cluster를 위해 핵심이 되는 부분이다.
nil 객체 검사를 하는 이유는, 반드시 메모리 할당이 성공한다고 보장할 수 없기 때문이다. 메모리 고갈로 인한 실패가 있을 수 있기
때문에 방어적인 프로그램을 위한 것이다.  모든 잘 작성된 생성자는 위의 4가지 조건을 만족해야 한다 

Chaining Initializers
자바에서는 명시적인 생성자 호출 문법을 가지고 있어서, 특정한 수퍼클래스의 생성자나 자신의 생성자를 호출 할 수 있다.
Objective-C에서는 이러한 문법은 없지만, 동일한 원리로  수퍼클래스나 자신의 init...계열의 메시지로 초기화가 가능하다.

Designated Initializer
Objective-C 의 많은 클래스들이 "설계된" 생성자를 갖으며, 객체 생성시 이를 사용하여 초기화를 해야한다
그렇기 때문에 이러한 클래스들을 사용할 경우에는 문서를 참조해야 한다. 

Convenience Constructors
클래스 메소드의 일반적인 용례는 복잡한 객체 생성 및 초기화를 간소화시킷는 것이다. 
Cocoa framework의  NSDictrionary  클래스는 [NSDictionary dictionary]  클래스 메소드를 제공하며, 이것은
[[NSDictionary alloc] init]  와 동일하다 


 
10. Destructors
Objective-C 에서도 선택적으로 - (void)finalize 메소드를 오버라이드 할수 있다. 이 메시지는 가비지 컬렉터에 의해 객체가 파괴되기
전에 호출된다. 모든 finalize 메소드는  반환되기 직전에 수퍼클래스의 finalize  메시지를 호출해야한다
- (void)finalize {
if (file != nil) {
[file close];
file = nil;
}
[super finalize]
}   

 
   
11. etc...

Inner / Anonymous Classes (내부 클래스와 익명클래스)
내부 클래스와 익명클래스는 코드 캡슐화나 어댑터로 사용되는데, Objective-C 에서는 비공식 프로토콜과 코드 블록을
사용하여 구현된다. (코드 블록은 변수에 실행가능한 코드 블록 할당을 허용하는 Objective-C 언어에 최근 추가된 개념이다)
 
Object Arrays
Objecitve-C에서는  C object 포인터 배열이나  NSArray 컬렉션 클래스를 사용하여 구현한다
 
final
final 키워드를 지원하지 않으며, 클래스 상속이나 메소드 오버라이딩을 막을 수 없다. 변수에 적용 할 경우,  const가 유사하다.
 
abstract
Objective-C 의 모든 클래스와 메소드는 구체 클래스이다 
 
package
Objective-C에는 패키지가 없으며, 패키지 범위 또한 없다. 
 
 


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

Objective-C 문자열 조작 메서드  (0) 2011.04.29
다중인자를 갖는 PerformSelector 메시지  (0) 2011.03.23
Protocol 과 Category  (0) 2011.03.22
Objective-C 훑어보기  (2) 2011.03.20
[Cocoa] Cocoa Design Pattern  (0) 2011.03.20
HMAC-SHA1 구현  (0) 2011.03.03
  • sungsik81 2011.03.22 09:49 ADDR 수정/삭제 답글

    날 위한 포스트인가... 두둥...

  • 로그 @로그 2011.03.23 14:00 신고 ADDR 수정/삭제 답글

    예전에 자바 개발자를 위한 objective-c 보고 정리했던 거에요.. 정리라기 보단...
    책에서 그대로 가져온...^^;;