Objective-C 프로토콜의 사용법이 다음과 같은 방식으로 사용되는 것을 보았습니다.
@protocol MyProtocol <NSObject>
@required
@property (readonly) NSString *title;
@optional
- (void) someMethod;
@end
서브 클래스가 확장되는 구체적인 수퍼 클래스를 작성하는 대신이 형식이 사용되는 것을 보았습니다. 문제는이 프로토콜을 준수하면 속성을 직접 합성해야합니까? 수퍼 클래스를 확장하는 경우 대답은 ‘아니오’입니다. 그렇지 않아도됩니다. 그러나 프로토콜이 준수해야하는 속성은 어떻게 처리합니까?
이해하기 위해서는 여전히 이러한 속성이 필요한 프로토콜을 따르는 개체의 헤더 파일에서 인스턴스 변수를 선언해야합니다. 이 경우, 그것들이 단지 지침 원칙이라고 가정 할 수 있습니까? 필요한 방법의 경우도 마찬가지입니다. 프로토콜은 프로토콜이 나열하는 필수 방법을 제외하기 위해 손목을 때립니다. 속성 뒤에 숨겨진 이야기는 무엇입니까?
다음은 컴파일 오류를 생성하는 예입니다 (참고 : 현재 문제를 반영하지 않는 코드를 다듬 었습니다).
MyProtocol.h
@protocol MyProtocol <NSObject>
@required
@property (nonatomic, retain) id anObject;
@optional
TestProtocolsViewController.h
- (void)iDoCoolStuff;
@end
#import <MyProtocol.h>
@interface TestProtocolsViewController : UIViewController <MyProtocol> {
}
@end
TestProtocolsViewController.m
#import "TestProtocolsViewController.h"
@implementation TestProtocolsViewController
@synthesize anObject; // anObject doesn't exist, even though we conform to MyProtocol.
- (void)dealloc {
[anObject release]; //anObject doesn't exist, even though we conform to MyProtocol.
[super dealloc];
}
@end
답변
프로토콜은 단지 프로토콜을 통해 클래스에 대해 알고있는 모든 사람들에게 그 속성 anObject
이있을 것이라고 말하고 있습니다. 프로토콜은 실제가 아니며 변수 나 메소드 자체가 없습니다. 프로토콜에 대한 참조를 보유한 객체가 특정 방식으로 사용할 수 있도록 클래스에 대한 특정 속성 세트 만 설명합니다.
그것은 당신의 클래스에서 당신의 프로토콜에 맞는 것을 의미하며, 당신은 anObject가 작동하도록 모든 것을해야합니다.
@property
그리고 @synthesize
코드를 생성하는 두 가지 메커니즘이 핵심입니다. @property
해당 속성 이름에 getter (및 / 또는 setter) 메소드가 있다고 말하고 있습니다. 요즘 @property
에는 시스템에 의해 생성 된 메소드와 저장 변수가 충분합니다 (추가해야했던 경우 @sythesize
). 그러나 변수에 액세스하고 저장할 무언가가 있어야합니다.
답변
다음은 프로토콜 정의가 완벽하게 작동하는 예제입니다.
@class ExampleClass;
@protocol ExampleProtocol
@required
// Properties
@property (nonatomic, retain) ExampleClass *item;
@end
다음은이 프로토콜을 지원하는 클래스의 실제 예입니다.
#import <UIKit/UIKit.h>
#import "Protocols.h"
@class ExampleClass;
@interface MyObject : NSObject <ExampleProtocol> {
// Property backing store
ExampleClass *item;
}
@implementation MyObject
// Synthesize properties
@synthesize item;
@end
답변
당신이 정말로해야 할 일은
@synthesize title;
당신의 구현에서 당신은 모두 설정되어야합니다. 클래스 인터페이스에 속성을 넣는 것과 같은 방식으로 작동합니다.
편집하다:
이 작업을보다 구체적으로 수행 할 수 있습니다.
@synthesize title = _title;
이것은 자동 합성을 사용하는 경우 xcode의 자동 합성이 속성과 ivar를 만드는 방법과 일치하므로 클래스에 프로토콜 및 클래스의 속성이있는 경우 일부 ivar의 형식이 다르므로 영향을 줄 수 있습니다 가독성.
답변
내 기사 PROTOCOL의 속성을 살펴보십시오
이름 속성을 선언하는 MyProtocol과이 프로토콜을 따르는 MyClass가 있다고 가정합니다.
주목할만한 것들
- MyClass의 identifier 속성은 getter, setter 및 backing _identifier 변수를 선언하고 생성합니다.
- name 속성은 MyClass에 헤더에 getter가 있음을 선언합니다. getter, setter 구현 및 지원 변수를 생성하지 않습니다.
-
이 이름 속성은 이미 프로토콜에 의해 선언되었으므로 다시 선언 할 수 없습니다. 오류가납니다
@interface MyClass () // Class extension @property (nonatomic, strong) NSString *name; @end
프로토콜에서 속성을 사용하는 방법
이름 속성으로 MyClass를 사용하려면 다음 중 하나를 수행해야합니다.
-
속성을 다시 선언하십시오 (AppDelegate.h는 이런 식으로 수행합니다)
@interface MyClass : NSObject <MyProtocol> @property (nonatomic, strong) NSString *name; @property (nonatomic, strong) NSString *identifier; @end
-
우리 자신을 종합하십시오
@implementation MyClass @synthesize name; @end
답변
예 : 2 개의 클래스 (Person 및 Serial)는 Viewer의 서비스 사용을 원하며 ViewerProtocol을 준수해야합니다. viewerTypeOfDescription은 구독자 클래스가 준수해야하는 필수 특성입니다.
typedef enum ViewerTypeOfDescription {
ViewerDataType_NSString,
ViewerDataType_NSNumber,
} ViewerTypeOfDescription;
@protocol ViewerProtocol
@property ViewerTypeOfDescription viewerTypeOfDescription;
- (id)initConforming;
- (NSString*)nameOfClass;
- (id)dataRepresentation;
@end
@interface Viewer : NSObject
+ (void) printLargeDescription:(id <ViewerProtocol>)object;
@end
@implementation Viewer
+ (void) printLargeDescription:(id <ViewerProtocol>)object {
NSString *data;
NSString *type;
switch ([object viewerTypeOfDescription]) {
case ViewerDataType_NSString: {
data=[object dataRepresentation];
type=@"String";
break;
}
case ViewerDataType_NSNumber: {
data=[(NSNumber*)[object dataRepresentation] stringValue];
type=@"Number";
break;
}
default: {
data=@"";
type=@"Undefined";
break;
}
}
printf("%s [%s(%s)]\n",[data cStringUsingEncoding:NSUTF8StringEncoding],
[[object nameOfClass] cStringUsingEncoding:NSUTF8StringEncoding],
[type cStringUsingEncoding:NSUTF8StringEncoding]);
}
@end
/* A Class Person */
@interface Person : NSObject <ViewerProtocol>
@property NSString *firstname;
@property NSString *lastname;
@end
@implementation Person
// >>
@synthesize viewerTypeOfDescription;
// <<
@synthesize firstname;
@synthesize lastname;
// >>
- (id)initConforming {
if (self=[super init]) {
viewerTypeOfDescription=ViewerDataType_NSString;
}
return self;
}
- (NSString*)nameOfClass {
return [self className];
}
- (NSString*) dataRepresentation {
if (firstname!=nil && lastname!=nil) {
return [NSString stringWithFormat:@"%@ %@", firstname, lastname];
} else if (firstname!=nil) {
return [NSString stringWithFormat:@"%@", firstname];
}
return [NSString stringWithFormat:@"%@", lastname];
}
// <<
@end
/* A Class Serial */
@interface Serial : NSObject <ViewerProtocol>
@property NSInteger amount;
@property NSInteger factor;
@end
@implementation Serial
// >>
@synthesize viewerTypeOfDescription;
// <<
@synthesize amount;
@synthesize factor;
// >>
- (id)initConforming {
if (self=[super init]) {
amount=0; factor=0;
viewerTypeOfDescription=ViewerDataType_NSNumber;
}
return self;
}
- (NSString*)nameOfClass {
return [self className];
}
- (NSNumber*) dataRepresentation {
if (factor==0) {
return [NSNumber numberWithInteger:amount];
} else if (amount==0) {
return [NSNumber numberWithInteger:0];
}
return [NSNumber numberWithInteger:(factor*amount)];
}
// <<
@end
int main(int argc, const char * argv[])
{
@autoreleasepool {
Person *duncan=[[Person alloc]initConforming];
duncan.firstname=@"Duncan";
duncan.lastname=@"Smith";
[Viewer printLargeDescription:duncan];
Serial *x890tyu=[[Serial alloc]initConforming];
x890tyu.amount=1564;
[Viewer printLargeDescription:x890tyu];
NSObject *anobject=[[NSObject alloc]init];
//[Viewer printLargeDescription:anobject];
//<< compilator claim an issue the object does not conform to protocol
}
return 0;
}
서브 클래 싱에 대한 프로토콜 상속을 가진 다른 예제
typedef enum {
LogerDataType_null,
LogerDataType_int,
LogerDataType_string,
} LogerDataType;
@protocol LogerProtocol
@property size_t numberOfDataItems;
@property LogerDataType dataType;
@property void** data;
@end
@interface Loger : NSObject
+ (void) print:(id<LogerProtocol>)object;
@end
@implementation Loger
+ (void) print:(id<LogerProtocol>)object {
if ([object numberOfDataItems]==0) return;
void **data=[object data];
for (size_t i=0; i<[object numberOfDataItems]; i++) {
switch ([object dataType]) {
case LogerDataType_int: {
printf("%d\n",(int)data[i]);
break;
}
case LogerDataType_string: {
printf("%s\n",(char*)data[i]);
break;
}
default:
break;
}
}
}
@end
// A Master Class
@interface ArrayOfItems : NSObject <LogerProtocol>
@end
@implementation ArrayOfItems
@synthesize dataType;
@synthesize numberOfDataItems;
@synthesize data;
- (id)init {
if (self=[super init]) {
dataType=LogerDataType_null;
numberOfDataItems=0;
}
return self;
}
@end
// A SubClass
@interface ArrayOfInts : ArrayOfItems
@end
@implementation ArrayOfInts
- (id)init {
if (self=[super init]) {
self.dataType=LogerDataType_int;
}
return self;
}
@end
// An other SubClass
@interface ArrayOfStrings : ArrayOfItems
@end
@implementation ArrayOfStrings
- (id)init {
if (self=[super init]) {
self.dataType=LogerDataType_string;
}
return self;
}
@end
int main(int argc, const char * argv[])
{
@autoreleasepool {
ArrayOfInts *arr=[[ArrayOfInts alloc]init];
arr.data=(void*[]){(int*)14,(int*)25,(int*)74};
arr.numberOfDataItems=3;
[Loger print:arr];
ArrayOfStrings *arrstr=[[ArrayOfStrings alloc]init];
arrstr.data=(void*[]){(char*)"string1",(char*)"string2"};
arrstr.numberOfDataItems=2;
[Loger print:arrstr];
}
return 0;
}
답변
변수 anObject는 TestProtocolsViewController 클래스 정의에 정의되어야합니다. 프로토콜은 단지 거기에 있어야한다는 것을 알려주는 것입니다.
컴파일러 오류는 진실을 말해줍니다-변수가 존재하지 않습니다. @properties는 결국 도우미입니다.