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


131

我已经看到使用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     

Answers:


135

该协议只是通过该协议告诉所有了解您的类的人,该属性anObject将在那里。协议不是真实的,它们本身没有变量或方法-它们仅描述一组关于您的类正确的特定属性,以便持有对其引用的对象可以特定方式使用它们。

这意味着在您的符合协议的类中,您必须做所有事情以确保anObject起作用。

@property并且@synthesize本质上是两种为您生成代码的机制。 @property只是说该属性名称将有一个getter(和/或setter)方法。@property仅这些天就足以拥有系统为您创建的方法和存储变量(您以前必须添加@sythesize)。但是您必须有一些访问和存储变量的内容。


80
对于协议中定义的属性,即使在现代运行时中,您仍然需要“ @synthesize”,或者需要在接口定义中复制“ @property”以进行自动合成。
杰弗里·哈里斯

@JeffreyHarris在Swift中也一样吗?
卡兰·阿兰加特

@KaranAlangat-在Swift中没有\ @synthesize这样的东西,但是就像ObjC一样,您确实需要在声称符合协议的类中声明该属性。在Swift中,您可以创建一个定义函数默认实现的类别,但据我所知,您不能为协议提供默认属性。
肯德尔·赫尔姆斯特·盖尔纳

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

14

您要做的就是放下一个

@synthesize title;

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

编辑:

您可能要更具体地执行此操作:

@synthesize title = _title;

如果您使用自动合成,这将与xcode的自动综合如何创建属性和ivars保持一致,因此,如果您的类具有协议和类的属性,则某些ivars不会使用可能会影响格式的不同格式可读性。


1
您完全确定吗?我在协议中设置了一个可选属性,当我仅在符合该协议的具体类中@synthesize时,就会收到编译器错误,声称它是未声明的变量。未确认错别字。
Coocoo4Cocoa

我不确定可选属性,但是我忘了像mralex所说的一件事是您需要将其绑定到成员变量,或者通过命名该变量的标题,或者说@synthesize title = myinstancevar;
凯夫拉尔

2
如果您使用的是现代运行时,则只需@synthesize,将为您创建基础的ivars。如果您以32位x86为目标,则将提到编译器错误,因为您以旧版运行时为目标。
杰弗里·哈里斯

1
自动综合是Xcode 4.4中引入的,但是根据Graham Lee推文,它不包含协议中声明的属性。因此,您仍然需要手动合成这些属性。
cbowns 2013年

这很重要,没有意识到添加synthesize足够了。凉!
Dan Rosenstark '16

9

看看我的文章PROPERTY IN PROTOCOL

假设我有声明名称属性的MyProtocol和符合此协议的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

如何在协议中使用属性

因此,要将MyClass与该name属性一起使用,我们必须要么

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

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

    @implementation MyClass
    
    @synthesize name;
    
    @end

嵌套在列表中的代码块需要每行缩进8个空格。Markdown语法相对未知。我已经为您编辑了答案。
BoltClock

1

协议架构

示例:2个类(人和串行)要使用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;
}

0

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

编译器错误告诉您真相-该变量不存在。毕竟,@ properties只是帮助者。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.