繁体   English   中英

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

[英]How to handle Objective-C protocols that contain properties?

我已经看到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的自动合成创建属性和ivars的方式一致,因此,如果您的类具有协议和类的属性,那么您的一些ivars将不具有可能影响的不同格式可读性。

看看我的文章“ 属性协议”

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

值得注意的事情

  1. MyClass中的identifier属性声明并生成getter,setter和backing _identifier变量
  2. name属性仅声明MyClass在标头中有一个getter,setter。 它不会生成getter,setter实现和后备变量。
  3. 我无法重新声明此名称属性,因为它已经由协议声明。 这会是一个错误的大喊大叫

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

如何在协议中使用属性

因此,要使用具有该name属性的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;
}

另一个在subClassing上具有Protocol继承的示例

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毕竟只是帮手。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM