이건 안하느니 못한 거 같다. 회사에서도 더 이사 개발을 안하고 스스로도 개발을 안하는 상황인데 이런 글 읽고 말로 만들려니 나도 무슨 소리를 하고 있는지를 모를 지경이다. 대학 때 잠시 C# 공부하면서 델리게이트 등에 대한 것은 알고는 있지만 말로는 설명할 수준이 아니다 보니 뭐라고 하는 건지...
나중에 다시 한번 보면 좀더 그럴듯하게 써내려 갈 수 있을까 모르겠다...
Object-C 언어는 정교한 객체 지향 프로그래밍을 가능하게 하기 위해 디자인된 심플한 컴퓨터 언어다. Object-C는 클래스, 메소드, 그리고 프로퍼티 정의하기 위한 문법과 클래스의 동적 확장을 활성화하는 생성자들을 제공하여 표준 ANSI C 언어에서 확장하였다. 클래스 문법과 디자인은 대부분 스몰톡 기반이며, 스몰톡은 초기 객체 지향 언어 중 하나이다.
이전에 객체 지향 프로그래밍을 한 적이 있다면 다음 정보를 통해 Object-C의 기본 문법을 배우는 데 도움이 될 것이다. 캡슐화, 상속, 다상성 등과 같은 전통적인 객체 지향 컨셉의 많은 부분이 Object-C에 포함되어 있다. 몇몇 중요한 차이점이 있는데, 이러한 차이점은 이 문서에는 논외로 하고 더 자세하 정보는 필요할 때 찾아 볼 수 있다.
객체 지향 언어를 사용하여 프로그래밍을 한 적이 없다면, 앞으로 진행하기에 앞서 적어도 객체 지향 언어의 컨셉에 대한 기본 이해가 필요하다. 객체와 객체 지향 생성자를 사용하는 것은 iPhone 어플리케이션을 디자인하는 데 기본이다. 객체 지향 컨셉에 대한 개요는 Object-Oriented Programming with Object-C에서 볼 수 있다.
Object-C 언어에 대한 더 자세한 소개는 The Object-C 2.0 Programming Language를 살펴보면 된다.
Object-C: A Superset of CObject-C는 C 프로그래밍 언어의 ANSI 버전의 슈퍼셋이고 C의 기본 문법을 지원한다. C 코드로 헤더와 소스 파일을 아래 표와 같이 파일 확장자를 사용하여 분리된 공개 선언을 통해 정의할 수 있다.
| Extension |
Source type |
.h |
Header files. Header files contain class, type, function, and constant declarations. |
.m |
Source files. This is the typical extension used for source files and can contain both Objective-C and C code. |
.mm |
Source
files. A source file with this extension can contain C++ code in
addition to Objective-C and C code. This extension should be used only
if you actually refer to C++ classes or features from your Objective-C
code. |
소스 코드 안에 헤더를 포함하기를 원하면 표준 #include 컴파일러 지시자를 사용할 수 있으나 Object-C는 더 나은 방법을 제공한다. #import 지시자는 #include와 동일하지만 동일한 파일을 한번만 포함하게 한다. Object-C 샘플과 도큐멘테이션은 #import 사용을 선호하며 개발자의 코드에서 #import를 사용해야 한다.
StringsC의 슈퍼셋으로써의 Object-C는 C와 마찬가지로 문자열을 구체화하기 위한 동일한 변환 방식을 지원한다. 즉, 하나의 문자는 싱글 쿼테이션 마크(‘)로 둘러싸고 여러개의 문자는 더블 쿼테이션 마크(“)로 둘러싼다. 그러나, 대부분의 Object-C 프레임워크는 C스타일의 문자열을 자주 사용하지는 않다. 대신에 대부분의 프레임워크는 문자열을 NSString 객체에 넣는다.
NSString 클래스는 부동형 문자열에 대한 메모리 관리, 유니코드 지원, 프린트 스타일의 포맷 유틸리티 등을 포함한 모든 이점이 있는 객체 랩퍼를 제공한다. 이런 문자열이 일반적으로 사용되기 때문에 Object-C는 상수값으로 이루어진 NSString 객체를 생성하기 위한 약식의 노테이션을 제공한다. 이러한 약식의 방법을 사용하기 위해 해야할 일은 더블 쿼테이션 마크 앞에 @ 심볼을 놓기만 하면 된다. 아래 예제를 참고하면 된다.
NSString* myString = @"My String\n";
NSString* anotherString = [NSString stringWithFormat:@"%d %s", 1, @"String"];
// Create an Objective-C string from a C string
NSString* fromCString = [NSString stringWithCString:"A C string" encoding:NSASCIIStringEncoding];
Classes다른 객체 지향 언어에서처럼 Object-C의 클래스는 데이터를 처리하는 액션과 데이터를 캡슐화하는 기본 생성자를 제공한다. 객체는 클래스의 런타임 인스턴스이며, 클래스에서 정의된 멤버 변수의 메모리내 복사본과 클래스 메서드의 포인터를 포함한다.
Object-C의 클래스 스펙은 interface와 implementation이라는 두개의 명확히 구분되는 부분들이 필요하다. interface는 클래스 선언과 정의를 포함한다. implementation는 메서드에 대한 실제 코드를 포함한다. 아래 그림은 MyClass 클래스의 정부 부분에 대한 문법을 보여준다. 클래스 선언은 항상 @interface 컴파일러 지시자로 시작되고 @end 지시자로 끝난다. 그 다음에 클래스의 부모 클래스명이 따라 온다. 클래스의 멤버 변수는 중괄호 안의 블럭에서 선언된다. 그 다음에는 클래스에서 선언된 메서드의 리스트가 따라온다. 세미콜론은 각 멤버 변수와 메서드 선언을 의미한다.
아래 그림은 MyClass의 implementation 부분을 보여준다. 클래스 선언과 같이 클래스 implementation은 2개의 컴파일러 지시자로 구분된다 - @implementation과 @end. 이 지시자들은 컴파일러가 클래스에 속한 메서드의 범위를 지정하는데 필요한 정보를 제공한다. 메서드 정의부분은 코드 블럭을 제외한 interface내의 선언부분에 대응한다.
노트 : 이전의 클래스 선언에서는 메서드만 있지만 프로퍼티도 선언할 수 있다. 더 많은 정보는 Properties를 살펴보면 된다.
변수에 객체를 저장할 때는 항상 포인터 타입을 사용해야 한다. Object-C는 객체를 포함하는 변수에 대해 강한 타입과 약한 타입 모두를 지원한다. 강한 타입 포인터는 변수형 선언에서 클래스명을 포함한다. 약한 타입의 포인터는 콜렉션 클래스와 같이 객체의 타입을 정확히 알 수 없는 것에 자주 사용된다. 강한 타입의 언어를 알고 있다면 약한 타입의 변수 사용이 문제를 일으킬 수 있다고 생각할 수 잇지만 실제로 엄청난 유연성을 제공하고 Object-C 프로그램에 생동감을 불어 넣는다.
다음 예제는 강한 타입의 변수 선언과 약한 타입의 변수 선업을 보여준다.
MyClass* myObject1; // Strong typing
id myObject2; // Weak typing
MethodsObject-C의 클래스는 두가지 타입의 메서드를 선언할 수 있다: 인스턴스 메서드와 클래스 메서드. 인스턴스 메서드는 특정 클래스의 인스턴스에서만 실행이 된다. 즉, 인스턴스 메서드를 호출하기 전에 반드시 클래스의 인스턴스를 생성한 후 인스턴스에서 실제 메서드를 출해야 한다. 반대로 클래스 메서드는 인스턴스를 생성할 필요가 없다.
메서드의 선언은 메서드 타입 식별자, 리턴 타입, 하나 이상의 시그너처 키워드, 그리고 파라미터 타입과 이름 정보로 구성된다. 아래 그림은 insertObject:atIndex: 라는 인스턴스 메서드의 선언 예제다. 선언은 인스턴스 메서드를 나타내는 마이너스(-) 사인이 앞에 온다. 메서드의 실제 이름(insertObject:atIndex:)는 콜론(:)를 포함한 모든 시그너처 키워드를 연결한 것이다. 콜론(:)은 파라미터이 있음을 선언한다. 메서드의 파라미터가 없다면 첫번째 시그너처 키워드 뒤의 콜론을 생략할 수 있다.
메서드를 호출하기 원하면 해당 객체에 메세지를 보내어 호출할 수 있다. 이 경우의 메시지는 메소드 시그너처로 메소드가 필요로하는 파라미터 정보와 함께 간다. 객체에 보내는 모든 메시지는 동적으로 빠르게 처리되며 Object-C의 다상성 행동을 쉽게 해준다. 즉, 서브 클래스가 부모 클래스와 동일한 시그너처인 클래스를 정의하면, 서브 클래스가 먼저 메시지를 받고 부모 클래스에 메시지를 포워딩할 수 있다.
메시지는 꺽쇠괄호([,])로 둘러싸인다. 꺽쇠괄호 안에서 메시지를 받는 객체는 왼쪽 편에 위치하고 메시지는 오른쪽에 위치한다. 예를 들어, insertObject:atIndex: 메시지를 myArray 변수의 객체에 보내기 위해서는 다음과 같은 문법을 따르면 된다.
[myArray insertObject:anObj atIndex:0];
일시적인 결과를 저장하는 다수의 지역변수를 선언하는 것을 피하기 위해, Object-C는 메시지 중첩을 허용한다. 각각의 중첩된 메시지의 리턴값은 파라미터, 타겟, 다른 메시지 등으로 사용된다. 예를 들어, 위의 샘플에서 메시지로 사용된 변수를 값을 받기 위한 메시지로 변경할 수 있다. 배열 객체에 접근할 수 있는 메서드를 가진 myAppObject라는 객체가 있고 배열에 객체를 추가하려면 다음과 같이 예젠 앞부분에 코드를 추가하면 된다.
[[myAppObject getArray] insertObject:[myAppObject getObjectToInsert] atIndex:0];
위의 예제가 클래스 인스턴스에 메세지를 보내는 것이지만 클래스 자신에게도 메시지를 보낼 수 있다. 클래스에 메세지를 보내려면 선택한 메서드는 인스턴스 메서드가 아닌 클래스 메서드로 정의되어야만 한다. C++ 클래스의 정적 멤버와 비슷한 것이라고 생각할 수 있다.(그러나 정확히는 같지 않다.)
일반적으로 클래스 메서드는 새로운 클래스의 인스턴스를 만들거나 클래스와 관련있는 공유 정보에 접속하기 위해 팩토리 메서드로 사용한다. 클래스 메서드 선언의 문법은 인스턴스 메서드와 동일하다. 마이너스(-) 싸인 대신 플러스(+) 싸인을 사용하는 것을 제외하고는.
아래 예제는 클래스 메서드를 클래스에 대한 팩토리 메서드로 사용한 것을 보여준다. 이 예제에서 arrayWithCapacity; 메서드는 NSMutableArray 클래스의 클래스 메서드이며 클래스의 새로운 인스턴서를 할당하고 초기화학 코드로 돌려주는 역할을 한다.
NSMutableArray* myArray = nil; // nil is essentially the same as NULL
// Create a new array and assign it to the myArray variable.
myArray = [NSMutableArray arrayWithCapacity:0];
Properties프러퍼티는 접근 메서드를 대체하기 위해 사용된 편리한 표기법이다. 프러퍼티는 새로운 멤버 변수를 만들어내지 않는다. 단순히 기존의 멤버 변수에 접근하는 정의된 메서드에 대한 약기이다. 멤버 변수를 노출하려는 클래스는 get이나 set 메서드를 사용하는 대신 프러퍼티 표기법을 사용할 수 있다. 가상의 멤버 변수를 노출하기 위해서도 프러퍼터를 사용할 수 있다. - 가상의 멤버 변수란 멤버 변수에는 실제로 저장되어 있지 않지만 동적으로 계산된 데이터의 일부분을 뜻한다.
실질적으로 프러퍼티는 짜야할 상당한 량의 코드를 줄여준다. 대부분의 접근 메서드는 비슷한 방식으로 구현이 되기 때문에, 프러퍼티가 각 멤버 변수에 각각 제공되어야 할 get과 set 메서드를 제거해 준다.프러퍼티 정의를 사용하여 원하는 방식으로 행동을 구체화하고 컴파일시 선언에 기반하여 실제의 get과 set 메서드를 합쳐야 한다.
프러퍼티 선언은 실제 메서드 생성에 영향을 미치므로, 클래스 인터페이스에 메서드 선언과 함게 포함해야 한다. 기본 정의는 @property 컴파일러 지시자를 사용하며 그 다음 프러퍼티의 type 정보와 이름이 나온다. 커스텀 옵션을 사용하여 접근 메서드가 어떻게 행동할지에 대해 프러퍼티를 설정할 수 있다. 아래 에제는 간단한 프러퍼티 선언에 대한 것이다.
@property BOOL flag;
@property (copy) NSString* nameObject; // Copy the object during assignment.
@property (readonly) UIView* rootView; // Create only a getter method.
프러퍼티의 또다른 이점은 변수에 접근하려고 할 때 닷(.) 문법을 사용할 수 있다는 것이다. 아래 예제를 보라.
myObject.flag = YES;
CGRect viewFrame = myObject.rootView.frame;
위 예제의 객체와 프러퍼티 이름이 인위적이지만 프러퍼티의 유연성을 잘 보여주고 있다. 닷 문법은 호출할 수 있는 메서드들을 마스킹한다. 각각의 읽을 수 있는 프러퍼티는 동일한 이름의 메서드에 의해 뒤로 빠진다. 각각의 쓸 수 있는 메서드는 setPropertyName의 형식의 추가적인 메서드에 의해 뒤로 빠진다. 프러퍼티 이름의 첫번째 글짜는 대문자여야 한다. 프러퍼티 대신 메서드를 사용하여 이전 코드를 구현하려면 다음과 같이 하면 된다.
[myObject setFlag:YES];
CGRect viewFrame = [[myObject rootView] frame];
프러퍼티 선언하는 법에 대한 정보는 The Object-C 2.0 Programming Language의 Properties를 보면 된다.
Protocols and Delegates프로토콜은 어떤 클래스든지 구현할 수 있는 메서드를 선언한다. 프로토콜은 클래스가 아닌다. 단순히 객체들과 구현 부분에 대한 인터페이스를 정의할 뿐이다. 개발자가 만든 클래스에서 프로토콜 메서드를 구현한다면 클래스는 이러한 프로토콜에 대해 알아야 한다.
iPhone OS에서 프로토콜은 객체를 델리게이트하기 위해 종종 사용된다. 델리케이트 객체는 다른 객체와 협력하거나 절반처럼 행동하는 객체다. 프로토콜, 델리게이트, 그리고 다른 객체 사이가 어떻게 동작하는지를 살펴볼 수 있는 가장 좋은 방법은 예제를 살펴보는 것이다.
UIApplication 클래스는 어플리케이션의 필요한 행동을 수행한다. 어플리케이션의 현재 상태에 대한 단순한 통지를 받기 위해 UIApplication의 하위 클래스를 사용하는 것 대신에 UIApplication는 할당된 델리게이트 객체의 특정 메서드를 호출하여 통지를 전달한다. UIapplicationDelegate 프로토콜은 이러한 통지를 받고 적절한 응답을 제공한다.
프로토콜의 선언은 클래스 인터페이스와 유사하다. 다만 프로토콜은 부모 클래스를 가지고 있지 않고 멤버 변수를 정의할 수 없다는 것만 다르다. 다음 예제는 간단한 하나의 메서드에 대해 프로토콜 선언에 대한 것이다.
@protocol MyProtocol
- (void)myProtocolMethod;
@end
많은 델리게이트 프로토콜에 대해 프로토콜을 선택하는 것은 프로토콜에 의해 메서드를 정의하는 것이냐의 문제다. 몇몇 프로토콜은 프로노콜을 지원하기 위해 명확하게 기술해야 하지만 현재로써는 메서드를 구현하는 것으로도 충분하다. The Objective-C 2.0 Programming Language의 Protocols를 읽어보면 프로토콜에 대한 것과 어떻게 사용해야 하는지를 알 수 있게 된다.
For Mor information위의 정보는 Object-C 언어의 기본으로 이 글을 읽는 사람에게 친숙함을 제공하기 위한 의도로 만드러졌다. 나머지 문서를 읽는 동안 가장 많이 직면할 언어의 특징들을 반영한 것들이다. 언어의 특징들은 이뿐만이 아니며 The Object-C 2.0 Programming Lanugage를 보고 언어에 대해 더 많은 것을 얻을 수 있다.
원본 :
iPhone Dev Center, Learning Objective-C: A Primer