카테고리 Archives: 미분류

회전각도의 취득

6-4-1 회전 각도를 취득하는 프로그램의 구성

단말 내장의 자이로스코프를 이용해서 회전 각도를 취득해 표시하는 프로그램을 만든다. 자이로스코프가 내장되어있는 것은 iPhone4뿐이므로 iPhone4이외에서는 동작 않는다. 표시할 정보는 다음과 같다.

X축 회전각도 (도)
Y축 회전각도 (도)
Z축 회전각도 (도)

단말을 정지한 상태에서는 모두 0.0에 근접한 값이 되고 기울이면 기울인 각도가 표시되며 천천히 0.0으로 돌아간다. <-?

(1) 회전각도 통지 개시
=============
회전 각도의 통지를 개시하려면 CMMotionManager클래스를 이용한다. alloc으로 메모리를 확보후 init메소드로 생성한다. 주요 프로퍼티는 다음과 같다.

(중략)

회전 각도의 통지 타겟은 startDeviceMotionUpdatesToQueue:withHandler:메소드로 지정한다.

(중략)

큐에는 오퍼레이션 큐를 NSOperationQueue클래스의 currentQueue프로퍼티로 취득해서 지정한다.

(중략)
 핸들러의 형은 iPhone4부터 추가된 “Blocks”라 불리는 새로운 형으로 다음과 같이 정의 된다.

typedef void (^CMDeviceMotionHandler)(CMDeviceMotion* motion, NSError* error);

“^”가 Blocks의 표시다. Java 언어의 무명함수와 닮았다. 이하의 서식으로 함수의 선언 없이 함수의 구현과 대입을 동시에 할 수 있다.

^(인수의 형 인수…){처리}

이 프로그램에서는 회전 각도 통지시에 Gyro클래스의 updateGyro:메소드를 부르는 것으로 다음과 같이 기술한다.

(중략)

(2) 회전 각도의 취득
============
CMDeviceMotion 오브젝트부터 회전 각도를 취득하려면 CMDeviceMotion 클래스의 attitude 프로퍼티를 사용한다.
CMAttitude 클래스는 회전 각도를  가진 데이터 형으로 pitch 프로퍼티에 X축 회전각도, yaw 프로퍼티에 Y축 회전 각도, roll프로퍼티에 Z축 회전 각도를 가진다.

(중략)

다음 계산으로 라디언을 도로 변환해 화면에 표시한다. M_PI는 파이 값을 표시하는 상수다.

도=라디안*(180/M_PI)

역으로 도를 라디언으로 변환하는 계산식은 다음과 같다.

라디안 = 도*(M_PI/180)

사용자 삽입 이미지

클래스 구현

클래스 정의
===========
[.h]확장자 파일은 [인터페이스 파일]이라 불리고 클래스의 정의를 기술한다. 서식은 다음과 같다. 클래스 정의는 [@interface]로 시작해 [@end]로 끝난다.


@interface 클래스명 : 부모 클래스명 {
     인스턴스 변수 선언
     …
}
프로퍼티 선언

메소드 선언
@end

인스턴스 변수의 선언
===================
인스턴스 변수는 오브젝트가 유지하는 변수다. 그 오브젝트 안의 모든 메소드로부터 액세스 가능하다. 서식은 다음과 같다.

– 변수형 인스턴스변수명;

본서에서는 로컬 변수 (메소드 내에 정의한 변수)와 인스턴스 변수를 구별하기 쉽게하도록 인스턴스 변수명의 앞에 “_”를 붙이고 있다.


@property 변수형 프로퍼티명;

오브젝트형 프로퍼티의 선언 서식은 다음과 같다.


@property (프로퍼티속성)클래스명* 프로퍼티명;

프로퍼티 선언에서는 프로퍼티의 액세스 방법을 프로퍼티 속성에서 지정할 수 있다. 필요에 따라 다음의 프로퍼티 속성을 설정할 수 있다. 기본적으로는 retain이면서 비동기인 (nonatomic,retain)을 설정한다.

*액세스 제한
프로퍼티 속성   의미
readwrite        읽기 쓰기 가능(디폴트)
readonly         읽기만 가능
표2-6-1 액세스 제한의 프로퍼티 속성

*오브젝트를 대입할 때의 조작
프로퍼티 속성  의미
retain             오브젝트를 retain하고 대입
assign           오브젝트를 그대로 대입(디폴트)
copy              오브젝트를 copy해서 대입
표2-6-2 오브젝트를 대입할 때의 프로퍼티 속성

*액세서 메소드 이름
프로퍼티 속성              의미
getter=GET메소드명      GET메소드명 지정
setter=SET메소드명      SET메소드명 지정
표2-6-3 액세서 메소드를 지정할 때의 프로퍼티 속성

*멀티스레드로부터의 액세스
프로퍼티 속성     의미
nonatomic         프로퍼티 액세스할 떄 동기처리를 하고싶지 않을 때 지정. 부가한 쪽이 퍼포먼스가 좋게 된다. 부가하지 않을 때는 비동기 처리를 한다.
표2-6-4 멀티스레드시의 프로퍼티 속성

프로퍼티명은 외부로부터 액세스할 때의 이름으로, 인스턴스 변수명과 같지 않아도 상관없다. 프로퍼티속성(nonatomic,retain)을 지정한 프로퍼티 선언이란 뒤에서 서명할 프로퍼티 구현에 따라 다음 2개의 메소드를 구현한 것과 같게 된다.

– (void)set 프로퍼티명:(변수형)인수 {
     [인스턴스변수 release];
     인스턴스변수=[인수 retain];
}

– (변수형)프로퍼티명 {
      return 인스턴스변수;
}

메소드 선언
===========
외부 클래스로부터 메소드의 이용을 허가할 때는 메소드 선언을 한다. 같은 클래스 내면서 이미 구현되어있는 메소드라면 메ㅗ드 선언이 없어도 메소드를 불러낼 수 있다.
오브젝트의 메소드 선언에서는 머리에 [-]를 붙인다. 인수가 없는 메소드의 선언 서식은 다음과 같다.

– (반환값의 형) 메소드명;

인수를 한개 가진 메소드 선언의 서식은 다음과 같다

-(반환값의 형)메소드명:(인수형)인수;

인수를 2개 가진 메소드의 선언 서식은 다음과 같다.

-(반환값의 형) 메소드명:(인수형)인수 레이블:(인수형)인수;

클래스 메소드 선언에서는 머리에 [+]를 붙있다. 인수가 없는 메소드 선언은 다음과 같다.

+ (반환값의 형) 메소드명;

인수를 한개 가진 메소드 선언의 서식은 다음과 같다

+(반환값의 형)메소드명:(인수형)인수;

인수를 2개 가진 메소드의 선언 서식은 다음과 같다.

+(반환값의 형) 메소드명:(인수형)인수 레이블:(인수형)인수;

클래스 구현
===========
[.m] 확장자 파일은 [구현 파일]이라 불리며 클래스 구현을 기술한다. 서식은 다음과 같다.
클래스 구현은 [@implementation]으로 시작해 [@end]로 끝난다.


@implementation 클래스명

프로퍼티 시장
메소드 구현
….


@end

프로퍼티 구현
=============

프로퍼티를 구현할 때는 다음과 같이 기술한다.


@synthesize 프로퍼티명=인스턴스 변수명;

프로퍼티명과 인스턴스 변수명이 같을 때는 [=인스턴스명]을 생략할 수 있다.


@synthesize 프로퍼티명;

메소드 구현
==========
오브젝트의 메소드를 구현할 때는 다음과 같이 기술한다. 인수를 가지지 않은 메소드의 서식은 다음과 같다

-(반환값의 형)메소드명 {
    처리
    return 반환값;
}

인수를 1개 가진 메소드의 구현 서식은 다음과 같다.

-(반환값의 형) 메소드명: (인수형)인수 {
      처리
     return 반환값;
}

인수를 2개 가진 메소드의 구현 서식은 다음과 같다

-(반환값의 형)메소드명:(인수형)인수 레이블:(인수형)인수{
      처리
      return 반환값;
}

클래스 메소드를 구현할 때는 다음과 같이 기술한다. 앞에 [-]가 [+]로 바뀐다. 인수가 없는 메소드의 구현 서식은 다음과 같다.

+(반환값의 형)메소드명 {
    처리
    return 반환값;
}

인수를 1개 가진 메소드의 구현 서식은 다음과 같다.

+(반환값의 형) 메소드명: (인수형)인수 {
      처리
     return 반환값;
}

인수를 2개 가진 메소드의 구현 서식은 다음과 같다

+(반환값의 형)메소드명:(인수형)인수 레이블:(인수형)인수{
      처리
      return 반환값;
}

초기화 메소드
=============
initWithFrmae:메소드는 UIView클래스 초기화 메소드다

UIView클래스
-(id)initWithFrame:(CGRect)frame
기능 : UIView 오브젝트 생성
인수 : frame 뷰 영역
반환값 : UIView 오브젝트

클래스 초기화 메소드 구현에는 반드시 부모 클래스의 초기화 메소드를 부를 필요가 있다. ClassEx 클래스는 UIView 클래스를 계승하고 있으므로 ClassEx 클래스의 initWithFrame:메소드로부터 부모 클래스의 initWithFrame:메소드를 불러 UIView오브젝트를 생성하고, 자신에 대입한다.
반환값이 nil이 아닐 때는 그 밖의 인스턴스 변수의 초기화를 한다. 최후에 반환값으로 self를 되돌린다. [super]는 부모 클래스의 오브젝트의 참조, [self]는 자신의 오브젝트를 참조를 나타낸다.

===========================
2-6-4 프로토콜과 카테고리
===========================

메소드를 선언하는 것으로 클래스 외에 [프로토콜]과 [카테고리]가 존재한다.

프로토콜이란
============
[프로토콜]이란 특정용도를 목적으로 한 메소드 선언의 집합이다. 프로토콜을 구현한 클래스는 동일한 프로토콜을 구현한 클래스 사이에 있어서 공통의 메소드를 가진다. 메소드의 기능은 클래스마다 정의한다. Java 언어에 있어서 인터페이스와 같은 역할을 한다.

                     구현     클래스
프로토콜                                     공통 메소드를 가진다. 메소드의 기능은 클래스마다 정의.
                     구현     클래스

그림2-6-2 프로토콜의 구현

프로토콜의 이점의 하나로 클래스가 여러개의 프로토콜을 구현할 수있다는 것을 들 수 있다. 공통의 메소드 선언을 여러개의 클래스에 구현하는 방법으로 슈퍼 클래스를 계승하는 방법도 있지만, Objective-C는 여러개의 슈퍼클라스의 계승(다중계승)을 할 수 없다.

*클래스의 계승                    *프로토콜의 구현
       
슈퍼클래스  –>                    프로토콜  —–>
                         클래스                                클래스
슈퍼클래스 -X->                   프로토콜 –ㅇ–>  
                                                  여러개의 프로토콜의 계승이 가능

그림2-6-3 클래스 계승과 프로토콜의 구현

프로토콜 선언
=============
Foundation 프레임 워크에 존재하는 [NSCoding 프로토콜]의 예로 프로토콜 선언과 구현에 대해 설명한다.

NSCoding 프로토콜은 오브젝트의 아카이브(오브젝트->NSData변환)과  언아카이브(NSData->오브젝트 변환)을 실행하기 위한 2개의 메소드가 준비되어있다. NSCoding 프로토콜의 선언은 다음과 같다.

//NSCoding 프로토콜
@protocol NSCoding
– (void)encodingWithCoder:(NSCoder*)encoder;
– (id)initWithCoder:(NSCoder*)decoder;
@end

프로토콜 선언은 [@protocol]로 시작해 [@end]로 끝난다. 그 사이에 클래스가 구현해야할 메소드가 정의되어있다. 프로토콜은 메소드의 선언을 제공하는 것뿐으로 어느정도 구현하는가는 프로토콜을 구현한 클래스가 담당하는 것에 주의하라. 클래스는 NSCoding 프로토콜을 구현하는 것으로 다른 클래스로부터 아카이브와 아카이브의 조작을 할 수 있도록 된다.
또, 프로토콜의 선언에는 [@requred]와 [@optional]이라는 지시자도 이용할 수 있다. @required는 프로토콜을 구현하는 클래스가 반드시 구현해야할 메소드, @optional은 프로토콜을 구현한 클래스가 반드시 구현할 필요는 없는 메소드를 나타낸다. 지정하지 않은 경우는 @optional과 같이 취급한다.
[@required][@optional]은 UIKit프레임워크의 UITableVieDataSource프로토콜 선언등에 이용되고 있다.

//UITableViewDataSource프로토콜
@protocol UITableViewDataSource <NSObject>

@reuired
– (NSInterger)tabeView:(UITableView*)table
        numberOfRowsInSection:(NSInteger)section;

@optional
– (NSInteger)numberOfSelectionsInTableView:(UITableView*)tableView;

// 생략
@end

프로토콜의 구현
==============
자작 클래스에 프로토콜을 구현할 때 클래스 선언의 [@interface] 마지막에 <>를 사용해 프로토콜을 지정한다
Person클래스에 NSCoding클래스를 구현할 때의 클래스 선언은 다음과 같다.


@interface Person : NSObject <NSCoding> {

여러개 클래스를 구현할 때는, 프로토콜 명을 [,]로 구별해 지정한다.


@interface Person : NSObject <NSCoding,UITableViewDataSource> {

그리고 메소드 구현을 확장자 [m]파일에 추가한다.

//아카이브
– (void)encodeWithCoder:(NSCoder*)encoder {
           [encoder encoderObject:_name forKey:@”name”];
           [encoder encodeInt:_age forKey:@”age”];

//언아카이브

– (id)initWithCOder:(NSCoder*)decoder {
            self.name=[decoder decodeObjectForKey:@”name”];
            self.age=[decoder decodeintForKey:@”age”];
            return self;
}

Person 클래스에는 name 프로퍼티와 age프로퍼티 값을 아카이브/언아카이브 하는 처리를 기술한다. 다른 NSCoding 프로토콜의 구현한 클래스에서는 각 클래스 자체가 유지하는 프로퍼티 값을 아카이브/언아카이브하는 처리를 기술할 수 있다.

카테고리란?
===========
[카테고리]라는 건 1개의 클래스를 여러개의 클래스로 분할하거나, 클래스의 새로운 기능을 추가하고 싶을 떄를 위한 Objective-C 문법이다. NSString 과 같은 Cocoa Framework에서 제공되는 클래스 군에 대해서도 메소드를 추가할 수 있다.
NSString 클래스에 대해문자열 저후의 공백과 개행 문자수를 소거하는 Trim 메소드를 추가하려면 다음 소스코드를 추가한다.

//NSString+trim.h
#import <Foundation/Foundation.h>

@interface NSString(Trim)
– (NSString*)trim;
@end

//NSString+trim.m
#import “NSString+trim.h”


@implementaiton NSString(Trim)
-(NSString*)trim {
     return [self stringByTrimmingCharactersInSet:
                [NSCaracterSet whitespaceAndNewlineCharacterSet]];
}
@end

stringByTrimmingCharactersInSet:메소드는 문자열의 양단으로부터 지정한 문자 센에 포함된 문자열을 제거하는 메소드다. 문자셋에는 공백과 개행문자열([NSCharacterSet whitespaceAndNewlineCharacterSet])을 저정한다.

NSString 클래스
-(NSString*)stringByTrimmingCharactersInSet:(NSCharacterSet*)set
기능 : 문자열 양단으로 부터 지정한 문자셋에 포함된 문자열을 지움
인수 : set 문자 셋
반환값 :문자열의 양단으로부터 지정한 문자셋에 포함된 문자열을 지운 문자열

[NSStirng+trim.h]를 import하면, NSStirng오브젝트에 trim메소드를 이용할 수 있게 됩니다.

NSString* str=@”    aaa    “;
str=[str trim];
NSLog(@”>%@.”,str);

헤더 파일인 @interface의 뒤에 있는 [(Trim)]이 카테고리명으로 구현 파일인 @implementation의 뒤에 있는 [(Trim)]과 대응한다. 파일명과 클래스명은 같을 필요가 없다. 자작 클래스라도 카테고리를 나눠 기술하는 것에 따라 여러개 파일에 분할 해 프로그램을 기술할 수 있다. 다만 인스턴스 변수는 부모 파일에 정으할 필요가 있다.