Как обрабатывать протоколы Objective-C, содержащие свойства? - программирование

Как обрабатывать протоколы 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     
4b9b3361

Ответ 1

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

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

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

Ответ 2

Вот мой пример, который отлично работает, определение протокола в первую очередь:

@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

Ответ 3

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

@synthesize title;

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

Edit:

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

@synthesize title = _title;

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

Ответ 4

Взгляните на мою статью СОБСТВЕННОСТЬ В ПРОТОКОЛЕ

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

Вещи, которые стоит отметить

  • Свойство идентификатора в MyClass объявляет и генерирует переменную _identifier getter, setter и backing
  • Свойство name объявляет только, что MyClass имеет getter, setter в заголовке. Он не генерирует функцию getter, setter и backing.
  • Я не могу переопределить это свойство name, как это уже было объявлено протоколом. Это вызовет ошибку.

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

Как использовать свойство в протоколе

Итак, чтобы использовать MyClass с этим свойством name, мы должны сделать либо

  • Объявите свойство снова (AppDelegate.h делает этот путь)

    @interface MyClass : NSObject <MyProtocol>
    
    @property (nonatomic, strong) NSString *name;
    
    @property (nonatomic, strong) NSString *identifier;
    
    @end
    
  • Синтезируйте себя

    @implementation MyClass
    
    @synthesize name;
    
    @end
    

Ответ 5

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];
            [email protected]"String";
            break;
        }
        case ViewerDataType_NSNumber: {
            data=[(NSNumber*)[object dataRepresentation] stringValue];
            [email protected]"Number";
            break;
        }
        default: {
            [email protected]"";
            [email protected]"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];
        [email protected]"Duncan";
        [email protected]"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

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;
}

Ответ 6

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

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