如何处理包含属性的Objective-C协议?

我已经看到Objective-C协议的使用以如下方式使用:

@protocol MyProtocol <NSObject> @required @property (readonly) NSString *title; @optional - (void) someMethod; @end 

我已经看到了这种格式,而不是写一个子类扩展的具体的超类。 问题是,如果你符合这个协议,你需要自己综合属性? 如果你正在扩展一个超类,答案显然不是,你不需要。 但是,如何处理协议要求符合的属性呢?

就我的理解,你仍然需要在符合需要这些属性的协议的对象的头文件中声明实例variables。 在这种情况下,我们可以假设他们只是一个指导原则吗? 对于所需的方法来说,情况并非如此。 编译器会为你排除一个协议列出的必要方法。 财产背后的故事是什么?

下面是一个例子,它会产生一个编译错误(注意:我已经修剪了那些没有反映出问题的代码):

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属性将会在那里。 协议并不是真实的,它们本身没有variables或方法 – 它们只描述了一个关于你的类的特定的属性集合,所以持有引用的对象可以以特定的方式使用它们。

这意味着在你的课堂上,你要遵守你的协议,你必须尽一切努力确保anObject的工作。

@property@synthesize是核心的两个机制,为您生成代码。 @property只是说将会有一个getter(和/或setter)方法的属性名称。 这些天@property本身就足以让系统为你创build方法和存储variables(你以前必须添加@sythesize )。 但是你必须有一些东西来访问和存储variables。

下面是我的一个很好的例子,协议定义首先是:

 @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的自动综合如何使用自动综合创build属性和ivars,这样,如果您的类具有来自协议和类的属性,那么您的一些ivars将不会有可能影响的不同格式可读性。

看看我的文章PROPERTY IN PROTOCOL

假设我有MyProtocol声明一个名称属性,MyClass符合这个协议

值得注意的事情

  1. MyClass中的标识符属性声明并生成getter,setter和backing _identifiervariables
  2. name属性只声明MyClass在头中有一个getter,setter。 它不会生成getter,setter实现和后备variables。
  3. 正如协议已经宣布的那样,我不能重新声明这个名字。 这是否会喊出一个错误

     @interface MyClass () // Class extension @property (nonatomic, strong) NSString *name; @end 

如何在协议中使用属性

所以要使用MyClass和这个名称属性,我们必须做

  1. 再次声明属性(AppDelegate.h这样做)

     @interface MyClass : NSObject <MyProtocol> @property (nonatomic, strong) NSString *name; @property (nonatomic, strong) NSString *identifier; @end 
  2. 合成我们自己

     @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; } 

协议inheritanceoverClassing的另一个例子

 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; } 

variablesanObject需要在你的TestProtocolsViewController类定义中定义,协议只是告诉你它应该在那里。

编译器错误告诉你事实 – variables不存在。 毕竟@properties只是帮手。