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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/844678/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 21:31:52  来源:igfitidea点击:

How to handle Objective-C protocols that contain properties?

iphoneobjective-ccocoa-touch

提问by Coocoo4Cocoa

I've seen usage of Objective-C protocols get used in a fashion such as the following:

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

@protocol MyProtocol <NSObject>

@required

@property (readonly) NSString *title;

@optional

- (void) someMethod;

@end

I've seen this format used instead of writing a concrete superclass that subclasses extend. The question is, if you conform to this protocol, do you need to synthesize the properties yourself? If you're extending a superclass, the answer is obviously no, you do not need to. But how does one deal with properties that a protocol requires to conform to?

我已经看到使用这种格式而不是编写子类扩展的具体超类。问题是,如果你符合这个协议,你是否需要自己综合这些属性?如果您要扩展超类,答案显然是否定的,您不需要。但是如何处理协议需要遵守的属性呢?

To my understanding, you still need to declare the instance variables in the header file of an object that conforms to a protocol that requires these properties. In that case, can we assume that they're just a guiding principle? CLearly the same isn't the case for a required method. The compiler will slap your wrist for excluding a required method that a protocol lists. What's the story behind properties though?

据我了解,您仍然需要在符合需要这些属性的协议的对象的头文件中声明实例变量。在这种情况下,我们可以假设它们只是一个指导原则吗?显然,所需方法的情况并非如此。编译器会因为排除协议列出的必需方法而拍手叫好。房产背后的故事是什么?

Here's an example that generates a compile error (Note: I've trimmed the code which doesn't reflect upon the problem at hand):

这是一个生成编译错误的示例(注意:我已经修剪了没有反映手头问题的代码):

MyProtocol.h

我的协议文件

@protocol MyProtocol <NSObject>

@required
@property (nonatomic, retain) id anObject;

@optional

TestProtocolsViewController.h

TestProtocolsViewController.h

- (void)iDoCoolStuff;

@end

#import <MyProtocol.h>

@interface TestProtocolsViewController : UIViewController <MyProtocol> {

}

@end

TestProtocolsViewController.m

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     

回答by Kendall Helmstetter Gelner

The protocol is just telling everyone that knows about your class through the protocol, that the property anObjectwill be there. Protocols are not real, they have no variables or methods themselves - they only describe a specific set of attributes that is true about your class so that objects holding references to them can use them in specific ways.

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

That means in your class that conforms to your protocol, you have to do everything to make sure anObject works.

这意味着在符合您的协议的类中,您必须尽一切努力确保 anObject 有效。

@propertyand @synthesizeare at heart two mechanisms that generate code for you. @propertyis just saying there will be a getter (and/or setter) method for that property name. These days @propertyalone is enough to also have methods and a storage variable created for you by the system (you used to have to add @sythesize). But you have to have something to access and store the variable.

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

回答by reddersky

Here's an example of mine that works perfectly, the protocol definition first of all:

这是我的一个完美运行的例子,首先是协议定义:

@class ExampleClass;

@protocol ExampleProtocol

@required

// Properties
@property (nonatomic, retain) ExampleClass *item;

@end

Below is a working example of a class supporting this protocol:

以下是支持此协议的类的工作示例:

#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

回答by Kevlar

all you have to do really is to drop a

你所要做的就是放下一个

@synthesize title;

in your implementation and you should be all set. it works the same way as just putting the property in your class interface.

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

Edit:

编辑:

You may want to do this more specifically:

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

@synthesize title = _title;

This will fall in line with how xcode's automatic synthesis creates properties and ivars if you use auto-synthesis, so that way if your class has properties from a protocol and a class, some of your ivars won't have the different format which could impact readability.

如果您使用自动合成,这将符合 xcode 的自动合成如何创建属性和 ivars,这样,如果您的类具有来自协议和类的属性,则您的某些 ivars 不会具有可能影响的不同格式可读性。

回答by onmyway133

Take a look at my article PROPERTY IN PROTOCOL

看看我的文章PROPERTY IN PROTOCOL

Suppose I have MyProtocol that declares a name property, and MyClass that conforms to this protocol

假设我有声明 name 属性的 MyProtocol 和符合此协议的 MyClass

Things worth noted

值得注意的事情

  1. The identifier property in MyClass declares and generates getter, setter and backing _identifier variable
  2. The name property only declares that MyClass has a getter, setter in the header. It does not generate getter, setter implementation and backing variable.
  3. I can't redeclare this name property, as it already declared by the protocol. Do this will yell an error

    @interface MyClass () // Class extension
    
    @property (nonatomic, strong) NSString *name;
    
    @end
    
  1. MyClass 中的 identifier 属性声明并生成 getter、setter 和 backing _identifier 变量
  2. name 属性只声明 MyClass 在头中有一个 getter、setter。它不生成 getter、setter 实现和支持变量。
  3. 我不能重新声明这个 name 属性,因为它已经被协议声明了。这样做会报错

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

How to use property in protocol

如何在协议中使用属性

So to use MyClass with that name property, we have to do either

因此,要将 MyClass 与该 name 属性一起使用,我们必须执行以下任一操作

  1. Declare the property again (AppDelegate.h does this way)

    @interface MyClass : NSObject <MyProtocol>
    
    @property (nonatomic, strong) NSString *name;
    
    @property (nonatomic, strong) NSString *identifier;
    
    @end
    
  2. Synthesize ourself

    @implementation MyClass
    
    @synthesize name;
    
    @end
    
  1. 再次声明该属性(AppDelegate.h 就是这样做的)

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

    @implementation MyClass
    
    @synthesize name;
    
    @end
    

回答by Luc-Olivier

Protocol Architecture

协议架构

Example: 2 classes (Person and Serial) want use service of Viewer... and must conform to ViewerProtocol. viewerTypeOfDescription is a mandatory property subscriber classes must conform.

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

An other Example with Protocol inheritance over 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;
}

回答by mralex

The variable, anObject, needs to be defined in your TestProtocolsViewController class definition, the protocol is just informing you that it should be there.

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

The compiler errors are telling you the truth - the variable doesn't exist. @properties are just helpers after all.

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