Как обращаться с протоколами Objective-C, которые содержат свойства? - PullRequest
129 голосов
/ 10 мая 2009

Я видел, как использование протоколов 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     

Ответы [ 6 ]

131 голосов
/ 10 мая 2009

Протокол просто сообщает всем, кто знает о вашем классе по протоколу, что свойство anObject будет там. Протоколы не являются реальными, у них самих нет переменных или методов - они описывают только определенный набор атрибутов, который относится к вашему классу, так что объекты, содержащие ссылки на них, могут использовать их особым образом.

Это означает, что в вашем классе, который соответствует вашему протоколу, вы должны сделать все, чтобы убедиться, что anObject работает.

@property и @synthesize - это два механизма, которые генерируют код для вас. @property просто говорит, что для имени этого свойства будет метод получения (и / или установки). В наши дни одного @property достаточно, чтобы также иметь методы и переменную хранения, созданную для вас системой (раньше вы добавляли @sythesize). Но у вас должно быть что-то для доступа и хранения переменной.

31 голосов
/ 02 января 2011

Вот мой пример, который отлично работает, прежде всего определение протокола:

@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
13 голосов
/ 10 мая 2009

все, что вам нужно сделать, это сбросить

@synthesize title;

в вашей реализации, и вы должны быть все в порядке. он работает так же, как просто помещает свойство в интерфейс вашего класса.

Edit:

Вы можете сделать это более конкретно:

@synthesize title = _title;

Это будет соответствовать тому, как автоматический синтез xcode создает свойства и ivars, если вы используете автосинтез, поэтому, если ваш класс имеет свойства из протокола и класса, некоторые из ваших ivars не будут иметь другой формат что может повлиять на читабельность.

6 голосов
/ 12 июля 2014

Взгляните на мою статью ИМУЩЕСТВО В ПРОТОКОЛЕ

Предположим, у меня есть MyProtocol, который объявляет свойство name, и MyClass, который соответствует этому протоколу

Стоит отметить

  1. Свойство идентификатора в MyClass объявляет и генерирует переменную getter, setter и backing _identifier
  2. Свойство name только объявляет, что в заголовке MyClass есть 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
    
1 голос
/ 08 мая 2014

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;
}
0 голосов
/ 10 мая 2009

Переменная anObject должна быть определена в вашем определении класса TestProtocolsViewController, протокол просто сообщает вам, что он должен быть там.

Ошибки компилятора говорят вам правду - переменная не существует. В конце концов, @properties - просто помощники.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...