지정 이니셜라이저는 슈퍼 클래스의 이니셜라이저를 super를 통해 호출하고, 뒤이어 자신의 초기화 처리를 하는 메소드.

객체 타입과 동적 결합

동적 결합

동적결합이란?

실제로 프로그램을 개발할 때는 수많은 ㄱ클래스의 인스턴스를 사용한다. 이 때 모든 객체가 id 타입으로 표현되면 어떤 객체가 어떤 클래스의 인스턴스인지 분간하기 어렵다.

보내진 메시지에 대응해서 어떤 메소드가 실행되는지 런타임에 결정하는 방식을 동적 결합(dunamic binding)이라고 한다.

다형성

다형성이란?

같은 메시지를 보내더라도 메시지를 받는 리시버에 따라 적절한 메소드가 선택되고 실행되는 것을 다형성(polymorphism)이라고 한다.

클래스의 전방 선언

어떤 클래스의 인터페이스를 기술할 때, 인스턴스 변수의 타입이나 메소드의 리턴값 혹은 인수의 타입으로 클래스를 지정할 수가 있다. 이런 경우, 그 타입이 특정 클래스라는 것을 컴파일러에게 어떻게 알리면 될까 ?

OBJECTIVE-C

#import "Volume.h"

이렇게 그 클래스의 인터페이스를 포함한 헤더 파일을 임포트하면 해결된다.
하지만 헤더 파잃에는 필요로 하는 클래스명의 정의 외에도 다른 여러 정보가 들어 있다. 다른 정보는 필요없이 클래스명을 타입으로 사용하기만 한다면 아래와 같은 방법을 사용할 수 있다.

OBJECTIVE-C

@class Volume; // Volume 이라는 클래스를 사용한다는 것을 선언

이것을 개발 언어에서 일컫는 말로 클래스의 전방 선언이라고 한다. 만일 클래스를 여러 개 쓰고 싶다면 , 로 구분해서 적어주고 마지막에 ;만 찍어 마무리를 지어주면 된다.

클래스 객체

Objective-C에서는 클래스 메소드는 있지만 클래스 변수는 없다.

Objective-C에서는 클래스 객체를 팩토리(factory), 클래스 메소드를 팩토리 메소드라고 부르기도 한다.

클래스 객체 자체는 언제 만들어지는 것일까 ??

답은 프로그램이 실행된 시점부터 각 클래스당 하나씩 자동으로 만들어진다 이다.

클래스 메소드의 정의

인스턴스 메소드는 인터페이스나 구현부에서 - 문자를 앞에 두게 되어 있다. 클래스 메소드는 이 문자가 +가 되는 것만 다르다.

OBJECTIVE-C

+ (id)alloc;

문법적으로는 상당히 간단하지만 실제로 프로그램을 개발할 때 주의할 점이 있다.

  • 먼클래스 메소드 안에서 인스턴스 변수를 참조하는 것을 불가능하다.
  • 마찬가지로 인스턴스 메소드를 호출하는 것도 불가능하다.
  • 런타임에 클래스 메소드에서 사용하는 변수 self는 그 클래스 객체 자신을 참조하고 있다. 그래서 어떤 클래스 메소드 안에서 다른 클래스 메소드를 호출하기 위해서는 self에 메시지를 보내면 된다. 단, 인스턴스 메소드와 마찬가지로 self가 의미하는 클래스가 뭐가 될지는 주의해야 한다.
  • 슈퍼클래스의 클래스 메소드를 호출할 때는 super를 사용한다.

클래스 변수

Objective-C는 클래스 변수를 정의하는 특별한 구조가 없다. 이 때문에 객체는 메소드와 변수를 가진다 는 전제에 반하는 것처럼 보이지만 실제로는 그렇지 않다.

Objective-C에서는 구현 파일 안에서 static 지정자가 붙은 변수를 그 클래스의 클래스 변수로 사용한다.

C와 마찬가지로 Objective-C에서도 구현 파일 안에서 static 지정자를 붙여서 변수를 선언하면 그 변수는 구현 파일 안에서만 참조가 가능하다. 즉, 그 클래스의 클래스 메소드와 인스턴스 메소드에서만 참조가 가능하다.

단, 이 방법에는 약간의 문제가 있다. 바로 상속을 사용하면 슈퍼 클래스에서 클래스 변수로 사용하는 static 변수가 서브 클래스에서는 참조가 되지 않는다는 것이다. 그렇기 때문에 서브 클래스에서도 그 클래스 변수를 참조해야 한다면 슈퍼 클래스에서 static 지정자를 사용하면 안된다.

이 문제를 해결하려면 클래스 객체의 속성에 접근하기 위한 접근자 메소드를 클래스 메소드로 정의하면 된다.그 메소드가 서브 클래스로 상속되므로 자동적으로 슈퍼 클래스 안의 지역 변수에 접근할 수 있다.

클래스 객체의 초기화

클래스 객체는 프로그램이 실행된 시점에 이미 만들어져있기 때문에 인스턴스 객체처럼 생성 직후에 메시지를 보내는 것이 불가능하다.

이에 대한 한 가지 해결 방법으로는 실행 직후 main 함수에서 초기화 메시지를 보내는 방법을 생각할 수 있다. 하지만 프로그램 전체에서 어떤 클래스가 사용되는지, 초기화가 필요한지를 파악해 그것을 main 함수에 넣어줘야 한다. 즉 프로그램의 각 모듈의 실행 방법을 외부에 노출시키지 않는다는 정보 은닉의 사상에 위배되는 것은 물론, 실제로 소스 코드의 수정이 빈번해지고 변경이 어려운 프로그램이 될 확률이 높다.

Objective-C에서는 루트 클래스인 NSObject에 initialize라는 클래스 메소드가 제공되는데, 이것을 사용해서 각 클래스를 초기화한다.

어떤 클래스가 프로그램이 실행된 후 처음으로 메시지를 받으려고 하면, 그 직전에 initialize 메소드가 자동적으로 실행된다. 또 이에 앞서 슈퍼 클래스의 클래스 객체에도 initialize 메소드가 보내진다. 각 클래스의 initialize 메소드가 호출되는 것은 단 1회뿐이다. 즉 initialize 메소드에 기재된 코드는 클래스 객체(혹은 인스턴스 객체)가 사요ㅕㅇ되기 전에 단 한번만 호출되어 클래스 객체를 초기화할 수 있다.
슈퍼클래스의 initlaize 메소드는 위에서 설명한 대로 호출되는 시점이 이미 결정되어 있으므로 서브 클래스의 initlaize 메소드에서 명시적으로 호출하면 안된다.

NSObject 클래스

루트 클래스의 역할

일반저긍로 런타임 시스템의 기능을 개발자가 프로그램에서 직접 사용하는 경우는 거의 없다. 이러한 기본적인 기능들은 루트 클래스인 NSObject에서 메소드의 형태로 제공된다. NSObject를 상속한 모든 클래스는 상속받은 기능을 사용해서 런타임 시스템의 기능을 의식하지 않고 이용할 수 있다. 말하자면 루트 클래스는 런타임 시스템에 대해 인터페이스 역할을 하는 셈이다.

클래스와 인스턴스

NSObject는 인스턴스 변수를 하나만 가진다. 이것은 isa라는 Class 타입의 변수이다. 모든 인스턴스 객체는 이 isa에 의해 자신이 속한 클래스 객체를 참조하고 있다. isa는 인스턴스와 클래스의 관계를 결정하는 상당히 중요한 변수로 서브 클래스에서 변경해서는 안된다. 또 인스턴스가 속한 클래스를 확인하기 위해서는 isa를 참조하지 않고 인스턴스 메소드인 class를 사용해야 한다.

    • (Class) class
    • 리시버가 속하는 클래스의 클래스 객체를 리턴한다.
    • (Class) class
    • 클래스 객체를 리턴한다. 클래스 메소드를 호출할 때는 클래스명을 메시지 리시버로 사용하지만, 클래스 객체를 다른 메시지의 인수로 하거나 인수에 대입하는 경우에는 이 클래스 메소드를 사용해서 클래스 객체를 얻어낼 필요가 있다.
    • (id) self
    • 리시버 자체를 리턴.
    • (BOOL) isMemberOfClass: (Class) aClass
    • 리시버가 인수의 클래스의 인스턴스인지 확인.
    • (BOOL) isKindOfClass: (Class) aClass
    • 리시버가 인수의 클래스의 인스턴스인지 혹은 그 클래스의 서브 클래스인지 확인. 이 메소드가 isMemberOfClass:와 다른 점은 인수의 클래스 뿐만 아니라, 그 서브 클래스의 인스턴스인 경우에도 YES를 리턴.
    • (BOOL) isSubclassOfClass: (Class) aClass
    • 리시버가 인수의 클래스의 인스턴스이거나, 그 클래스 자체라면 YES를 리턴.
    • (Class) superclass
    • 리시버가 속하는 클래스의 슈퍼 클래스의 클래스 객체를 리턴.
    • (Class) superclass
    • 리시버의 슈퍼 클래스의 클래스 객체를 리턴.

메시지 송신의 구조

셀렉터와 SEL 타입

지금까지 셀렉터는 메시지 키워드를 나열한 것이라고 설명했다. 프로그램 내에서 메소드 선언이나 메시지 표현식으로 기재된 셀렉터는 컴파일 시 내부 표현 값으로 변환된다. 이 내부 표현에 대응하는 데이터 타입을 SEL 타입이라고 부른다.

Objective-C에서는 셀렉터의 내부 표현을 프로그램에서 다룰 수 있도록 프로그램 상의 셀렉터 표기에 SEL 타입 데이터를 얻어내는 컴파일러 지시자 @selector()가 제공된다.

OBJECTIVE-C(EX)

@selector(mutableCopy)
@selector(compare:)
@selector(replaceObjectAtIndex:withObject:)