iPhone/팁&태크2009/03/05 19:47
이번에는 Object-C의 Property에 대해서 이야기를 해볼까 합니다.

어떻게 보면 조금 필요없어 보일수도 있고 어찌보면 MVC(Model, View, Controller)개발 기법과도 밀접한 연관이 있어 보입니다.

하지만 일반적으로 MVC개발에서도 setter/getter로 구성 된 도메인 객체를 따로 두고 있듯이 Object-C에서도 역시 비슷한 구현을 할 수 있는것 같습니다.

Object-C에서 일반적으로 객체의 Property에 접근하기 위해서는 한쌍의 접근자 메서드(getter/setter)를 사용합니다.

이 메서드들을 사용함으로써 객체지향 프로그래밍의 캡슐화(Encapsulation)에 더욱 충실 할 수 있습니다.

관련된 자료는 Object-Oriented Programming with Objective-CMechanism Of Abstraction을 찾아보라고 하는군요.

Property를 정의함으로써 효과적으로 접근자 메서드들을 간략화 시키는 효과를 가져올 수 있습니다.

Property를 사용하기 위해서 일반적으로 @property 지시자와 @synthesize 지시자를 함께 사용합니다.

@property 지시자는 클래스의 @interface 내부에 선언하며 다음과 같은 형식으로 선언합니다.
@property(attributes) type name;

하나의 Property 선언은 두개의 접근자 메서드와 동일한 기능을 갖습니다.
@proprty float value;

위의 선언은 다음과 같이 두가지 메서드를 선언한것과 같은 기능을 하게 됩니다.
- (float)value;
- (void)setValue:(float)newValue;


@property 지시자의 attributes 에는 다음과 같은 정의를 할 수 있습니다.

getter=gettername
기본적으로 Property의 getter 메서드 명은 Property 자신의 이름과 동일 (예 : Property가 foo일 경우 foo)
하지만 이 기본 설정을 내가 원하는 메서드명으로 변경 할 수 있습니다.

setter=settername
Property의 setter 메서드 명은 setPropertyName: 입니다. (예 : Property가 foo일 경우 setFoo:)
역시나 이 기본 설정을 내가 원하는 메서드명으로 변경 할 수 있습니다.

readwrite (DEFAULT)
Property의 값을 읽고 쓸 수 있다는 것입니다. 이 설정은 기본 설정입니다.

readonly
Property의 값을 단지 읽기만 할수 있다고 정의하는 속성입니다.
이 속성은 @implementation 블럭 안에서 오로지  getter 메서드만 필요할 경우에 사용합니다.
@synthesize 지시자를 사용하였을 경우에는 역시나 getter 메서드의 역할만을 하게 됩니다.
값을 대입 하려고 할 경우 에러를 출력하게 됩니다.

assign (DEFAULT)
단순하게 값을 대입합니다. 기본설정입니다. 이전에 어떤 객체를 가리키고 있던 Property라면 이로 인해 해당 객체는 미아가 되어 메모리릭의 주범이 될 수 있습니다. 가비지콜렉터를 사용하지 않는다면 사용을 피해야 합니다.

retain
이것은 assign과 비슷하지만 조금 다릅니다. 이전에 가리키고 있던 객체가 있다면 해당 객체를 Release하여 메모리에서 제거 합니다. 가비지콜렉터를 사용한다면 결과적으로 assign과 동일한 결과를 가지겠지만 좀더 명시적으로 사용해 주면 좋을것 같습니다.

copy
객체를 바로 대입하지 않고 해당 객체의 복사 메서드를 Invoke호출합니다.
그리하여 다른 메모리 영역에 복사본을 만든 다음 그것을 반환하게 됩니다. 이전에 가리키고 있던 값은 Release 시킵니다.

nonatomic
이 속성은 접근자 메서드가 Atomic 하지 않게 동작하게 합니다(?). 기본적으로 접근자는 Atomic하게 동작합니다.
Atomic이라는 말은 멀티스레드 등으로 구성된 프로그램이 특정 접근자 메서드를 호출할때 서로 충돌이 나지 않도록(보통 세마포어니 크리티컬섹션이니 하는 말들 들어보셨을겁니다.) 객체 레벨에서 Lock을 걸고 Property에 접근하게 되는데요 매우 좋은 이야기지만 접근할때 마다 Lock을 걸고 다시 푸는 작업이 반복되므로 퍼포먼스를 떨어뜨리는 결과를 가져오게 됩니다.
이런 접근이 필요없다면 이 속성을 사용하여 Non-Atomic하게 동작하도록 만들어 주시는 것이 좋습니다.

이제 Property에 대해 거의 모든것을 알게 된것 같네요. 이제 예제를 한번 볼까요?
@interface MyClass : NSObject {
NSString *value;
}
@property(copy, readwrite) NSString *value;
@end

@implementation MyClass
@synthesize value;
@end

value라는 이름의 Property의 getter 메서드 명은 value이고 setter 명은 setValue입니다.

값을 대입할때 복사가 일어나고 읽고 쓰기를 할 수 있습니다. 또한 nonatomic 속성이 없으니 atomic하게 동작하겠군요.

마지막으로 Property를 사용할때 주의사항이 한가지 있습니다.

객체가 제거 될때 소멸자로 dealloc이 호출되는데 Property들이 자동으로 소거되지 않아 명시적으로 제거해 주셔야 합니다.
- (void)dealloc {
[property release];
[super dealloc];
}


이글은 http://theeye.pe.kr/entry/iPhone-Object-C-Declared-Properties 에서도 볼 수 있습니다.
Posted by 아이

Leave your greetings.

iPhone/팁&태크2009/03/05 19:08
Object-C 2.0에 들어 열거형 변수를 좀더 효과적이고 안전하게 사용하기 위해 for 문법이 업그레이드 되었습니다.

이것을 보고 이름하여 Fast Enumeration 이라고 부르는군요. PHP나 Ruby등에서 볼수 있는 foreach와 같은 기능입니다.

일반적인 C에서의 구현에 비해서 매우 편리하게 반복문을 작성할 수 있습니다. 문법은 다음과 같습니다.
// one
for ( Type newVariable in expression ) { statements }

// two
Type existingItem;
for ( existingItem in expression ) { statements }

사용 예제를 보시면 바로 이해가 되실 것입니다. 예제를 보실까요.

NSArray
NSArray *array = [NSArray arrayWithObjects:@"One", @"Two", @"Three", @"Four", nil];
for (NSString *element in array) {
NSLog(@"element: %@", element);
}
// element: One
// element: Two
// element: Three
// element: Four

NSDictionary
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
@"quattuor", @"four", @"quinque", @"five", @"sex", @"six", nil];
NSString *key;
for (key in dictionary) {
NSLog(@"English: %@, Latin: %@", key, [dictionary valueForKey:key]);
}
// English: four, Latin: quattuor
// English: five, Latin: quinque
// English: six, Latin: sex

NSArray - nextObject
NSArray *array = [NSArray arrayWithObjects:@"One", @"Two", @"Three", @"Four", nil];
NSEnumerator *enumerator = [array reverseObjectEnumerator];
for (NSString *element in enumerator) {
if ([element isEqualToString:@"Three"]) {
break;
}
}
NSString *next = [enumerator nextObject];
// next = "Two"

안타깝게도 인덱스가 필요하다면 따로 제공하는것이 없어 다음과 같이 사용하셔야 합니다.
NSArray *array = /* assume this exists */;
NSUInteger index = 0;
for (id element in array) {
NSLog(@"Element at index %u is: %@", element);
index++;
}


예제만으로도 충분히 만족감을 주는 간단한 내용이었습니다.

이글은 http://theeye.pe.kr/entry/Object-C-Fast-Enumeration 에서도 볼 수 있습니다.
Posted by 아이

Leave your greetings.

iPhone/팁&태크2009/02/14 04:16


Object-C 에서의 메서드 구현에는 정확히 딱 두가지가 있는것 같습니다.

바로 Class Method와 Instance Method인데요. 이 두가지 메서드는 Java로 따져보면 static 메서드와 일반 메서드로 구분될 수 있겠다고 생각합니다.

우선 테스트 코드를 작성하기 위해 Mac OS X이하의 Command Line UtilityFoundation Tool 프로젝트를 생성합니다.

보통 C++하실때 보는 콘솔 어플리케이션쯤으로 생각하시면 되겠네요.

우선 MethodTest라는 Object-C 클래스를 추가합니다.

MethodTest.h
#import <Cocoa/Cocoa.h>

@interface MethodTest : NSObject {

}
+ (void)printWithClassMethod;
- (void)printWithInstanceMethod;

@end

MethodTest.m
#import "MethodTest.h"

@implementation MethodTest

+ (void)printWithClassMethod {
NSLog(@"Running with class method");
}
- (void)printWithInstanceMethod {
NSLog(@"Running with instance method");
}

@end

이제 main 함수에 다음과 같이 기록해 봅시다.
#import <Foundation/Foundation.h>
#import "MethodTest.h"

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

// insert code here...
[MethodTest printWithClassMethod];
MethodTest *mt = [MethodTest alloc];
[mt printWithInstanceMethod];

[pool drain];
return 0;
}

감이 오시나요? printWithClassMethod는 클래스를 인스턴스화 하지 않고도 호출할 수 있는 메서드입니다.

하지만 printWithInstanceMethod는 꼭 초기화 된 상태에서 호출해야만 하죠.

이를 자바로 한번 풀어보면 다음과 같겠죠.
class MethodTest {
public static void printWithClassMethod() {
System.out.println("Running with class method");
}
public void printWithClassMethod() {
System.out.println("Running with instance method");
}
}

Class Method와 Instance Method의 차이 이해 되시죠? ^^

Posted by 아이

Leave your greetings.

iPhone/팁&태크2009/02/14 04:14






 

Object-C의 특별한 문법 때문에 처음 접근이 어려우신 분들을 위해 간단하게 문법을 비교하여 보았습니다.

Messages
일반적으로 객체의 메서드를 호출하는 것을 Object-C에서는 메세지라고 표현합니다.

특정 객체의 메서드를 호출하는데 Java나 C++에서는 다음과 같이 표현합니다.

// Java
obj.method(parameter);

// C++
obj->method(parameter);

하지만 Object-C에서는 다음과 같이 표현합니다.
// Object-C
[obj method:parameter];

처음 볼때는 정말 어색했는데 자꾸 보다 보니깐 이제 좀 괜찮은거 같습니다. 오히려 소스코드를 볼때 메서드라고 확 튀어 보이니 더 좋은것 같기도 하네요.

Interface
Object-C에서 말하는 Interface 지시자는 Java나 C++에서 말하는 Class를 뜻합니다. 정확히는 선언부분만이라고 말해야겠군요.

좀더 정확히 말해보자면 메서드의 시그네이쳐를 선언해 둔다는 관점에서 Java에서 말하는 Interface와 비슷할 수도 있겠네요.

C++에서는 헤더파일안에 클래스의 선언부분과 같을 수 있겠고요. 구현부분은 .m 파일에서 합니다.

그렇다면 마찬가지로 Java와 C++의 예제 소스 코드를 보도록 할까요?
// Java
class classname extends superclassname {
// instance variables
int memberValue;

// member methods
return_type method1(param1_type param1, param2_type param2) { ... }
return_type method1(param1_type param1, param2_type param2) { ... }
}

// C++
class classname : superclassname {
// instance variables
int memberValue;

// member methods
return_type method1(param1_type param1, param2_type param2);
return_type method1(param1_type param1, param2_type param2);
}

Java의 예는 클래스의 구현부분이 클래스에 포함됩니다. Java의 Interface와 비교를 해볼려니 억지같고 추상클래스를 만들자니 또 억지 같아서 그냥 저렇게 써두었습니다.

그럼 Object-C의 구현 부분을 한번 볼까요?
// Object-C
@interface classname : superclassname {
// instance variables
int memberValue;
}

// member methods
- (return_type)method1:(param1_type)param1 param2_varName:(param2_type)param2;
- (return_type)method1:(param1_type)param1 param2_varName:(param2_type)param2;
@end

어떤가요? 이해가 되시나요? 그렇다면 이제 클래스의 구현부를 보도록 하겠습니다.

Implementation
Java의 경우에는 클래스 내부에서 그냥 메서드가 구현되었다고 치고 C++의 예를 한번 살펴 보겠습니다.
// C++
return_type classname::method1(param1_type param1, param2_type param2) {
...
}

return_type classname::method1(param1_type param1, param2_type param2) {
...
}

Object-C의 문법도 한번 볼 차례군요.
// Object-C
@implementation classname
- (return_type)method1:(param1_type)param1 param2_varName:(param2_type)param2 {
...
}
- (return_type)method1:(param1_type)param1 param2_varName:(param2_type)param2 {
...
}
@end


Instantiation
Object-C 에서는 객체를 생성하는 방법또한 독특한 느낌을 가지고 있습니다. 마찬가지로 비교를 한번 해볼까요?
// Java
MyObject obj = new MyObject();
obj.init();

// C++
MyObject *obj = new MyObject();
obj->init();

자 그렇다면 Object-C에서는 어떻게 하는지 알아볼까요?
// Object-C
MyObject *obj = [MyObject alloc];
[obj init];

혹은 다음과 같이 한줄로 사용할 수도 있습니다.
// Object-C
MyObject *obj = [[MyObject alloc] init];

어떤가요? 굉장히 색다른 느낌인가요?

지금까지 제 생각에 일반 프로그래밍 언어와 가장많이 다른 부분을 정리해 보았습니다. 이정도면 처음 Object-C를 봤을때의 혼란을 줄어들지 않을까 싶네요.

더 공부해 보고 싶으신 분은 [이곳]을 참고해 보시면 애플에서 잘 정리한 문서를 제공하고 있으니 참고하시면 되겠네요.

이글은 http://theeye.pe.kr/entry/compare_object_c_with_java_and_c 에서도 볼 수 있습니다.
Posted by 아이

Leave your greetings.

iPhone/팁&태크2009/02/14 04:05
Object-C는 아이폰 개발에 사용되는 언어입니다.

C와 C++의 중간 개념이라고들 하던데 확실히 C언어에 객체의 개념을 추가하고 적절한 라이브러리가 추가된 모습이더군요.

공부하면서 한번 정리를 해보도록 하겠습니다.

객체를 생성하기 위해 클래스를 선언하는데 일반적으로 C++을 해보셨던 분들이 이해하기 좀 어려운 모습을 가지고 있습니다.

XCode에서 New -> New File 항목에서 Object-C Class를 선택하여 클래스 파일을 추가합니다.

Foo 라는 클래스를 만들어 보면 Foo.h와 Foo.m 파일이 생성됩니다.

Foo클래스에 변수와 메서드들을 추가해 봅시다. 다음과 같은 방법으로 합니다.

Foo.h
@interface Foo : NSObject {
IBOutlet NSTextField *textField;
}
- (IBAction)seed:(id)sender;
- (IBAction)generate:(id)sender;
@end

Interface라는 이름을 가지고 선언하긴 하지만 엄연히 클래스를 선언하는 구문입니다.

Foo라는 클래스는 NSObject를 상속받았습니다. NSObject는 NextStep에서 만든 최상위 클래스입니다.

모든 NS로 시작하는 클래스들은 모두 최상위로 NSObject를 상속받습니다. 마치 자바의 Object와 같네요.

또한 NSTextField형의 textField라는 이름의 인스턴스변수(맴버변수)를 선언하였습니다.

IBOutlet은 외부의 다른 객체를 가리킬수 있는 변수를 뜻합니다.

추후에 올려보겠지만 UI상의 컴포넌트 객체들과 변수를 연결할 수 있습니다.

또한 -로 시작하는 줄들은 메서드들입니다. seed메서드만 보면 IBAction을 반환하고 id타입의 sender를 첫번째 인자로 입력 받습니다.

위의 클래스를 구현하면 다음과 같이 합니다.

Foo.m
@implementation Foo

- (IBAction)seed:(id)sender {
...
}
- (IBAction)generate:(id)sender {
...
}

위와 같이 메서드의 몸체를 구현할때 맴버 인스턴스 변수인 textField에 마음껏 접근할 수 있습니다.

객체 자기 자신을 가르킬 경우에는 자바나 C++에서는 this를 사용하지만 여기서는 self를 사용합니다.

인자를 받을 넘길때도 특이한점이 있군요. 두개 이상의 인자를 받을경우에는 굉장히 복잡해 집니다.
- (int)addNumbers:(int)left rightNumber:(int)right {
return left + right;
}

위와 같은 메서드는 addNumbers:rightNumber: 라고 부릅니다. 보통의 프로그래밍을 하던 느낌으론 굉장히 의문이 가는 부분입니다.

addNumbers는 int형 값을 반환하는 메서드입니다. 첫번째 인자는 int형 left 변수입니다.

그렇다면 rightNumber는 저게 뭘까요.  보통 인자를 가진 메서드를 셀렉터(Selector)라고 부릅니다.

위의 코드를 이렇게 바꿔 보면 이해가 쉬울것 같네요.
int addNumbers(int left, int right) {
return left + right;
}

실제로 사용되는 인자의 이름은 left, right이지만 rightNumber와 같은 식의 나누어진 셀렉터를 사용하여 메서드의 인자의 역할을 더욱 분명히 할 수 있는 장점(?)이 있습니다.

이제 함수를 호출하는 방법을 알아보겠습니다.
NSMutableArray *array = [NSMutableArray alloc];

Object-C에서 메서드 호출을 메세지를 보낸다고 표현합니다. 위의 경우에는 다음과 같이 생각하시면 될 것 같네요.
NSMutableArray array = new NSMutableArray();

위와 같이 동적으로 메모리를 잡고 array변수로 가리키게 합니다. 이제 초기화 하는 init 메세지를 보내보겠습니다.
NSMutableArray *array = [NSMutableArray alloc];
[array init];

array는 리시버(receiver)라고 합니다. 이 리시버에 init메세지를 입력합니다.

다음과 같이 위의 두줄을 한줄로 사용할 수도 있습니다.
NSMutableArray *array = [[NSMutableArray alloc] init];

인자를 입력할 경우에는 다음과 같이 사용합니다.
[array addObject:foo];

다수의 인자를 입력할때는 다음과 같은 방법으로 합니다.
[array insertObject:foo atIndex:bar];

위에서 언급했듯이 insertObject는 메서드명이기도 하면서 변수를 가리키는 셀렉터이기도 합니다.

Posted by 아이

Leave your greetings.