프로토콜


메소드를 클래스간에 공유

프로토콜 이라는 용어는 비교적 네트워크 용어로 사용되고 있지만, Objective-C에 한해서는 여러 클래스에서 구현되는 동일한 이름의 메소드를 공유하기위한 메소드 선언을 말합니다 . Java 언어 나 C # 언어에서는 인터페이스라는 개념에 가까운 존재입니다.

프로토콜이 이용되는 프로그램의 설계상의 문제로, 클래스가 특정 메소드를 구현하고 있음을 보장하기위한 수단으로 사용됩니다. 예를 들어, 특정 라이브러리 사양에 따라 클래스는 지정된 프로토콜을 준수하도록해야한다,라고하는 관계를 구축 할 수 있습니다. 프로토콜이 제공하는 선언 된 메소드뿐만 메서드 구현이 어떤 작업을 수행하는지는 자유입니다.

프로토콜을 선언하려면 @protocol 컴파일러 지시문을 사용합니다. 이 가운데, 프로토콜 규약으로 지정하는 메소드를 선언합니다.

@protocol 프로토콜 명 <부모 프로토콜 1 ...>
프로토콜 본체
...
@end

프로토콜은 클래스 상속 관계처럼 부모 프로토콜을 사용하여 상속 할 수 있습니다. 프로토콜의 상속에 대해서는 나중에 자세히 설명되지만 프로토콜의 상속은 단순히 부모 프로토콜이 정하는 방법에 새로운 메소드를 추가하는 의미 밖에 가지지 않습니다. 프로토콜 이름은 클래스 이름 등과 같이 C 언어 명명 규칙에 따라 프로 言る를 식별하는 이름을 지정합니다. 프로토콜 본체에서는 메소드 선언 만 할 수 있습니다.

선언 한 프로토콜은 클래스에 채용 할 수 있습니다. 프로토콜이 지정되어있는 클래스는 그 프로토콜을 채용 하고 있다고 부를 수 있습니다. 프로토콜을 채용하고있는 클래스는 프로토콜에 선언 된 메소드를 반드시 구현해야합니다. 덧붙여서, 프로토콜에 선언 된 메소드를 프로토콜을 사용하는 클래스의 선언 부에서 다시 선언 할 필요가 없습니다. 프로토콜을 클래스에 채용하려면 다음과 같이 클래스를 선언합니다.

@interface 클래스 이름 : 슈퍼 클래스 이름 <프로토콜 1 ...>

프로토콜은 슈퍼 클래스의 지정의 직후에 <>에서 프로토콜 이름을 묶어야합니다. 슈퍼 클래스는 하나만 지정할 수 없지만 프로토콜은 쉼표로 구분 된 여러 개의 프로토콜을 채용 할 수 있습니다. 프로토콜을 사용하는 경우 반드시 프로토콜로 선언 된 메소드를 @implementation 부분에서 구현해야합니다. 즉, 프로토콜을 채용한다는 것은 그 클래스가 프로토콜로 선언 된 메소드의 구현을 보장한다는 것입니다.

#import <stdio.h>
#import <objc / Object.h>

@protocol ClassNameToString
- (id) ToString;
@end

@interface A : Object 
{
	char * name;
}
- (id) init;
- (id) free;
@end

@interface B : Object 
@end

@implementation A
- (id) init {
	[super init];
	name = (char *) malloc (255);
	sprintf (name, "% s : A @ % d", __FILE__, self);
	return self;
}
- (id) free {
	free (name);
	return [super free];
}
- (id) ToString {return (id) name;}
@end
@implementation B
- (id) ToString {return (id) "This is Object of B Class";}
@end

int main () {
	id objA = [A new];
	id objB = [B new];
	printf ( "objA = % s \ n", objA ToString]);
	printf ( "objB = % s \ n", objB ToString]);
	[objA free];
	[objB free];

	return 0;
}

이 프로그램은 클래스의 이름을 나타내는 문자열을 반환 ToString 메서드를 선언 ClassNameToString 프로토콜을 선언하고 있습니다. 이 프로토콜을 사용하는 A 클래스와 B 클래스는 항상 ToString 메서드를 구현해야합니다. A 클래스와 B 클래스의 상속 관계에서는 아무런 인연도 없지만 프로토콜을 채용하는 것으로, 이러한 클래스에 ToString 메서드가 구현되는 것을 확실하게 보장 할 수 있습니다.

실천은 클래스의 구현에 의존하지 않는 완전히 추상화 된 형태로 프로토콜이 이용되는 외에, 포인터를 사용하지 않고 메서드를 콜백시키는 방법으로도 이용됩니다. 특히 GUI 환경의 이벤트 처리 등이 프로토콜을 사용하는 것입니다.

프로토콜 형식으로 독립하지 않지만 형식 선언에서 형명의 직후에 <> 프로토콜을 지정하기위한 변수가 지정한 프로토콜을 채용하고있는 것을 명시 적으로 선언 할 수 있습니다.

형명 <프로토콜 이름> 변수 이름 ...

이것은 함수 나 메소드의 인수 선언에서도 지정할 수 있습니다.

#import <stdio.h>
#import <objc / Object.h>

@protocol InstanceListener
- (void) InstanceFree : (id) object;
@end

@interface Test : Object
{
	id <InstanceListener> listener;
}
- (id) init;
- (id) free;
- (void) SetInstanceListener : (id <InstanceListener>) l;
- (id <InstanceListener>) GetInstanceListener;
@end

@implementation Test
- (id) init {
	[super init];
	listener = NULL;
	return self;
}
- (id) free {
	if (listener) listener InstanceFree : self];
	return [super free];
}
- (void) SetInstanceListener : (id <InstanceListener>) l {
	listener = l;
}
- (id <InstanceListener>) GetInstanceListener {
	return listener;
}
@end

@interface WriteInstanceFree : Object <InstanceListener>
@end

@implementation WriteInstanceFree
- (void) InstanceFree : (id) object {
	printf ( "% X : 인스턴스가 해제되었습니다. \ n", object);
}
@end

int main () {
	id obj1 = [Test new, obj2 = [Test new];
	id <InstanceListener> listener = [WriteInstanceFree new];
	[obj1 SetInstanceListener : listener];
	[obj2 SetInstanceListener : listener];
	[obj1 free];
	[obj2 free];

	return 0;
}

이 프로그램은 인스턴스가 해방 된 시간에 호출되는 콜백 전문 InstanceFree 메소드를 선언 InstanceListener 프로토콜을 선언하고 있습니다. Test 클래스에서는이 프로토콜을 사용하는 인스턴스를 SetInstanceListener 메소드에서 설정할 수 있으며, Test 클래스의 인스턴스가 해제되기 직전에 프로토콜의 메소드를 호출합니다. GUI의 이벤트 처리는 이와 같은 원리로 처리되는 것입니다.

변수를 선언 할 때, 형명의 직후에 프로토콜을 지정하면 해당 개체가 지정한 프로토콜을 채용해야한다는 것을 명시합니다. 따라서 Test 클래스의 listener 인스턴스 변수는 확실하게 목적의 메소드를 콜백 할 수있는 것입니다.


프로토콜의 상속

프로토콜은 클래스처럼 상속 할 수 있습니다. 프로토콜을 상속은 클래스의 상속과는 성격이 다르고, 실제로는 기본이되는 프로토콜에 새로운 메소드 선언을 추가하면됩니다. 또한 슈퍼 클래스는 반드시 하나만 지정할 수 없습니다 만, 프로토콜은 여러 프로토콜을 상속 할 수 있습니다.

#import <stdio.h>
#import <objc / Object.h>

@protocol ProtocolA
- (void) MethodA;
@end

@protocol ProtocolB
- (void) MethodB;
@end

@protocol ProtocolC <ProtocolA>
- (void) MethodC;
@end

@interface Test : Object <ProtocolB, ProtocolC>
@end

@implementation Test
- (void) MethodA {printf ( "This is MethodA \ n");}
- (void) MethodB {printf ( "This is MethodB \ n");}
- (void) MethodC {printf ( "This is MethodC \ n");}
@end

int main () {
	id obj = [Test new];
	[obj MethodA];
	[obj MethodB];
	[obj MethodC];
	[obj free];
	return 0;
}

이 프로그램의 ProtocolC 프로토콜은 ProtocolA 프로토콜을 계승하고 있습니다. 따라서 ProtocolC에서는 MethodA과 MethodC가 선언되어 있다고 생각할 수 있습니다.

또한 Test 클래스는 ProtocolB과 ProtocolC을 동시에 채용하고있는 점에도 주목하십시오. 선언 밖에 보유하지 않은 프로토콜은 메소드 이름이 충돌해도 프로그램의 메소드 검색에는 영향이 없기 때문에 가능한 것입니다. 그러나 다른 의도의 메소드가 프로토콜의 상속 관계에서 충돌 한 경우는주의 할 필요가있을 것입니다.