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

我看到 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
78053 次浏览

你要做的就是

@synthesize title;

在您的实现中,您应该已经准备好了。它的工作方式与将属性放在类接口中相同。

编辑:

你可能需要做得更具体一些:

@synthesize title = _title;

如果您使用自动合成,这将与 xcode 的自动合成创建属性和 ivar 的方式一致,这样,如果您的类具有来自协议和类的属性,那么您的一些 ivar 将不会具有可能影响可读性的不同格式。

变量 anObject 需要在 TestProtocolsViewController 类定义中定义,协议只是通知您它应该在那里。

编译器错误告诉你真相——变量并不存在。@properties 毕竟只是助手。

协议只是通过协议告诉所有知道你的类的人属性 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

Protocol Architecture

示例: 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;
}

假设我有一个 MyProtocol,它声明了一个 name 属性,而 MyClass 符合这个协议

值得注意的事情

  1. MyClass 中的 Identity 属性声明并生成 getter、 setter 和 backing_ Identity 变量

  2. Name 属性只声明 MyClass 在头部有一个 getter,setter。它不生成 getter、 setter 实现和支持变量。

  3. 我不能重新声明这个 name 属性,因为它已经在协议中声明过了。这样做会显示一个错误

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

如何在协议中使用属性

因此,要将 MyClass 与 name 属性一起使用,我们必须这样做

  1. 再次声明属性(AppRegiate.h 是这样做的)

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

     @implementation MyClass
    
    
    @synthesize name;
    
    
    @end