본문 바로가기
아이폰 개발/Objective C

objective c 클래스 클러스터(추상클래스)

by 인생여희 2020. 12. 29.

objective c 클래스 클러스터(추상클래스)

 

 

추상(버추얼) 클래스란 추상메소드 (아직 정의되지 않고 선언만된 메소드)를 포함하고 있는 클래스를 뜻한다. 이 추상클래스의 개념은 아래 그림과 같이 UML에도 정의되어 있을 만큼 객체지향 언어에서 필수적인 요소지만, obj c 에서는 추상클래스 기능을 별도로 제공하지 않는다. 그러나 어떤 측면에서는 obj c의 모든 클래스는 잠재적으로 추상클래스 상태라고 볼 수 있다. 

 

 

 

 

 

위의 추상 클래스를 obj c로 표현하면 아래와 같이 일반 클래스와 동일한 방법으로 작성한다. 다른 언어와 달리 virtual 또는 interface 같은 키워드는 존재 하지 않는다. 이런 이유로 alloc/init을 통해 geometry 클래스를 실제로 선언하는 것도 문제가 되지 않는다. 다만 이들은 다음 코드와 같이 정의가 완료된 move: 메소드 외에는 별다른 동작을 하지 않는다. 

 

 

 

 

지난 포스팅에서 인스턴스의 클래스를 확인하는 isMemberOfClass: 메소드 기능을 살펴보면서 NSString과 NSNumber 등의 인스턴스가 실제 이들 클래스의 인스턴스가 아닌 것을 확인했다. 이들은 모두 클래스 클러스터 방식으로 구현된 클래스로 비슷한 종류의 클래스 묶음(클러스터)으로 취급하는 방법이다. 클래스의 사용자는 단순화된 공개 가상 메소드를 사용하지만 실제로는 비공개인 서브클래스의 메소드가 각각 다른 방식으로 동작한다.

 

 

 

 

먼저 사용자에게 공개된 클래스는 인스턴스의 초기화 생성자와 공통된 사용법을 제공하는 공개된 가상 메소드이다. 위 그림 1번에서 사용자는 자신의 용도에 맞는 인스턴스를 초기화 한다. 이때 선택한 초기화 메소드에 의해 실제 생성되는 서브 클래스의 형식이 결정된다. NSString은 다음과 같은 간편 생성자를 제공하는데, 사용자가 어떤 메소드를 선택하느냐에 따라 생성되는 결과 인스턴스의 형식이 메소드에 의해 결정된다. 

 

 

 

 

그리고 생성자가 반환하는 인스턴스의 포인터는 공개클래스가 아닌 비공개 서브클래스의 인스턴스 주소가 된다. 2번에서 사용자가 인스턴스의 자료를 사용 또는 관리하기 위해 호출하는 메소드는 사용자 입장에서는 공개된 가상 메소드이지만, 실제는 인스턴스의 isa가 지시하는 서브클래스의 비공개 메소드가 동작한다.

 

예제

 

 

 

 

MyObject.h

 

//공개 가상 클래스

#import <Foundation/Foundation.h>

//비공개 서브클래스의 전방 선언
@class MyNumber;
@class MyString;

@interface MyObject : NSObject

//초기화에 따라 실제 생성될 서브 클래스 선택
-(MyString *)initWithString:(NSString *)str;
-(MyNumber *)initWithNumber:(int)i;

//시험용 가상 메소드
-(NSString *)print;
@end

 

MyObject.m

 

#import "MyObject.h"
#import "MyString.h"
#import "MyNumber.h"

@implementation MyObject
-(MyString *)initWithString:(NSString *)str
{
    //여기서 self는 MyObject이다.
    //여기서는 필요없어서 autorelease 해준다.
    //안해주면 메모리가 급격하게 올라간다.
	[self autorelease];
    
    //필요한건 MyString이라서 MyString을 alloc 해준다.
    //비공개 서브 클래스 생성 후 반환
	return [[MyString alloc] init:str];
}
-(MyNumber *)initWithNumber:(int)i
{
	[self autorelease];
	return [[MyNumber alloc] init:i];
}
-(NSString *)print
{
	return nil;
}
@end

 

MyString.h

 

#import "MyObject.h"

@interface MyString : MyObject
{
	NSString *data;
}
-(id)init:(NSString *)str;
@end

 

MyString.m

 

#import "MyString.h"

@implementation MyString
-(id)init:(NSString *)str
{
	self = [super init];
	if(self)
	{
		data = str;
		[data retain];
	}
	return self;
}
-(NSString *)print
{
	return data;
}
-(void)dealloc
{
	[data release];
	[super dealloc];
}
@end

 

MyNumber.h

 

#import "MyObject.h"

@interface MyNumber : MyObject
{
	int num;
}
-(id)init:(int)i;
@end

 

MyNumber.m

 

#import "MyNumber.h"

@implementation MyNumber
-(id)init:(int)i
{
	self = [super init];
	if(self)
		num = i;
	return self;
}
-(NSString *)print
{
	return [NSString stringWithFormat:@"%d", num];
}
@end

 

main.m

 

#import <Foundation/Foundation.h>

//가상클래스 헤더만 임포트
//사용자는 서브클래스를 인식하지 않는다.!
#import "MyObject.h"

int main(int argc, const char * argv[])
{
	@autoreleasepool
	{
		id a, b, c;
        
        //서로 다른 초기화 메소드를 사용한다.
        //실제 클래스의 형식은 이때 결정.
		a = [[MyObject alloc] init];
		b = [[MyObject alloc] initWithString:@"ChoYS"];
		c = [[MyObject alloc] initWithNumber:100];
        
        
		printf("%s %s %s\n",
			   [[a print] UTF8String],
			   [[b print] UTF8String],
			   [[c print] UTF8String]);
        
        //각 인스턴스의 클래스 이름을 스트링으로 표시한다.
		printf("%s %s %s\n",
			   [NSStringFromClass([a class]) UTF8String],
			   [NSStringFromClass([b class]) UTF8String],
			   [NSStringFromClass([c class]) UTF8String]);
		[a release];
		[b release];
		[c release];
	}
	return 0;
}

 

결과

 

(null) ChoYS 100
MyObject MyString MyNumber
Program ended with exit code: 0

 

위의 예제에서 가장 중요한 부분은 MyObject의 initWith..메소드 정의 부분이다. 사용자가 alloc 한 인스턴스는 initWith..에서 [self autorelease]로 제거용 풀에 등록하고 비공개 서브클래스의 인스턴스를 생성하여 반환한다. 

 

실제 NSString 또는 NSNumber 와 같은 Foundation 프레임워크 클래스에서는 alloc에 의해 NSPlaceholder...형식의 클래스 인스턴스를 만든 후에 위와 유사한 처리를 행한다. 

 

 

아래는 기초 프레임워크에서 제공하는 클래스 중 공개클래스로 접근되는 클래스 클러스터를 정리한 것이다. 이외에도 클래스 클러스터는 많이 존재하며 사용자는 해당 클래스가 클러스터 방식인지 전혀 인지하지 못하고 사용하는 경우가 많다.

 

 

 

 

클래스 클러스터 확인 시 주의점

 

NSObject는 클래스 클러스터로 구성되지 않기 때문에 문제 없이 NSObject의 서브클래스를 만들고 형식을 확인 (isMemberOfClass 또는 isKindOfClass 이용) 할 수 있었다. 그러나 기초 프레임워크에서 제공하는 NSObject 이외의 클래스를 사용할 때는 주의가 필요하다.

 

 

 

 

위의 그림은 기초 프레임워크에서 제공하는 클래스들을 분류별로 모은 후 클래스 클러스터로 구성된 클래스를 짙은 색으로 표시한 것이다. 이들 클래스의 인스턴스를 생성한 후 그 형식을 확인할 때는 주의가 필요하다. NSString 으로 생성해도 그 하위에 숨겨진 서브클래스의 인스턴스가 생성되기 때문에 isMemberOfClass 으로 클래스를 확인할 때 반환값은 false가 나오게 된다.

 

1. 이경우는 isKindOfClass 를 사용하면 생성한 인스턴스는 NSString의 서브클래스이기 때문에 true를 반환한다. 즉, isMemberOfClass 대신 isKindOfClass를 사용한다.

 

2.클래스 확인이 필요한 경우는 대부분 특정 메소드가 동작하는지 확인이 필요한 경우다. 이 경우 인스턴스의 클래스를 확인하는 것보다 respondsToselector: @selector 를 통해 직접 그 메소드에 응답하는지를 확인하는 것이 더 간단할 수 있다.

 

 

 

'아이폰 개발 > Objective C' 카테고리의 다른 글

objective c 프로토콜 1  (0) 2020.12.31
objective c 클래스 클러스터의 서브 클래스  (0) 2020.12.30
objective c 카테고리  (0) 2020.12.29
NSObject 2  (0) 2020.12.28
NSObject 1  (0) 2020.12.28