我已经看到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
答案 0 :(得分:131)
该协议只是告诉每个通过协议了解您的课程的人,属性anObject
将在那里。协议不是真实的,它们本身没有变量或方法 - 它们只描述一组特定的属性,这些属性对于类是正确的,因此持有对它们的引用的对象可以以特定的方式使用它们。
这意味着在您的类中符合您的协议,您必须尽一切努力确保anObject正常工作。
@property
和@synthesize
是两种为您生成代码的机制。 @property
只是说该属性名称将有一个getter(和/或setter)方法。这些天@property
就足以让系统为您创建方法和存储变量(以前您必须添加@sythesize
)。但是你必须有一些东西可以访问和存储变量。
答案 1 :(得分:31)
这是我的一个完美运作的例子,首先是协议定义:
@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
答案 2 :(得分:13)
你要做的就是放弃
@synthesize title;
在您的实施中,您应该全部设置。它的工作方式与将属性放在类接口中的方式相同。
修改强>
您可能希望更具体地执行此操作:
@synthesize title = _title;
如果您使用自动合成,这将与xcode的自动合成创建属性和ivars的方式一致,因此如果您的类具有协议和类的属性,您的某些ivars将不具有不同的格式这可能影响可读性。
答案 3 :(得分:6)
查看我的文章PROPERTY IN PROTOCOL
假设我有声明名称属性的MyProtocol,以及符合此协议的MyClass
值得注意的事情
我无法重新声明此名称属性,因为它已经由协议声明。这样做会大喊错误
@interface MyClass () // Class extension
@property (nonatomic, strong) NSString *name;
@end
如何在协议中使用属性
因此要使用具有该name属性的MyClass,我们必须执行
再次声明属性(AppDelegate.h就是这样)
@interface MyClass : NSObject <MyProtocol>
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *identifier;
@end
综合自己
@implementation MyClass
@synthesize name;
@end
答案 4 :(得分:1)
示例: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;
}
在subClassing
上具有协议继承的另一个示例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;
}
答案 5 :(得分:0)
变量anObject需要在TestProtocolsViewController类定义中定义,协议只是告诉你应该在那里。
编译器错误告诉你实际情况 - 变量不存在。 @properties毕竟只是帮手。