Widziałem, jak protokoły Objective-C są wykorzystywane w następujący sposób:
@protocol MyProtocol <NSObject>
@required
@property (readonly) NSString *title;
@optional
- (void) someMethod;
@end
Widziałem ten format używany zamiast pisania konkretnej nadklasy, która rozszerza podklasy. Pytanie brzmi, czy jeśli dostosujesz się do tego protokołu, czy musisz samodzielnie zsyntetyzować właściwości? Jeśli rozszerzasz superklasę, odpowiedź oczywiście brzmi: nie, nie musisz. Ale jak radzić sobie z właściwościami, z którymi protokół wymaga zgodności?
W moim rozumieniu nadal musisz zadeklarować zmienne instancji w pliku nagłówkowym obiektu, który jest zgodny z protokołem wymagającym tych właściwości. W takim razie czy możemy założyć, że są one tylko zasadą przewodnią? W zasadzie to samo nie dotyczy wymaganej metody. Kompilator uderzy cię w nadgarstek za wykluczenie wymaganej metody wymienionej w protokole. Jaka jest jednak historia nieruchomości?
Oto przykład, który generuje błąd kompilacji (Uwaga: przyciąłem kod, który nie odzwierciedla problemu):
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
źródło
Oto mój przykład, który działa doskonale, przede wszystkim definicja protokołu:
@class ExampleClass; @protocol ExampleProtocol @required // Properties @property (nonatomic, retain) ExampleClass *item; @end
Poniżej znajduje się roboczy przykład klasy obsługującej ten protokół:
#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
źródło
wszystko, co naprawdę musisz zrobić, to upuścić plik
@synthesize title;
w implementacji i wszystko powinno być gotowe. działa tak samo, jak zwykłe umieszczenie właściwości w interfejsie klasy.
Edytować:
Możesz to zrobić bardziej szczegółowo:
@synthesize title = _title;
Będzie to zgodne ze sposobem, w jaki automatyczna synteza xcode tworzy właściwości i ivars, jeśli używasz automatycznej syntezy, więc jeśli twoja klasa ma właściwości z protokołu i klasy, niektóre z twoich ivarów nie będą miały innego formatu, który może mieć czytelność.
źródło
synthesize
wystarczy. Fajne!Spójrz na mój artykuł WŁASNOŚĆ W PROTOKOLU
Załóżmy, że mam MyProtocol, która deklaruje właściwość name i MyClass, która jest zgodna z tym protokołem
Rzeczy warte odnotowania
Nie mogę ponownie zadeklarować tej właściwości nazwy, ponieważ została już zadeklarowana przez protokół. Zrobienie tego spowoduje wyświetlenie błędu
@interface MyClass () // Class extension @property (nonatomic, strong) NSString *name; @end
Jak używać właściwości w protokole
Tak więc, aby użyć MyClass z tą właściwością name, musimy zrobić jedno i drugie
Ponownie zadeklaruj właściwość (AppDelegate.h robi to w ten sposób)
@interface MyClass : NSObject <MyProtocol> @property (nonatomic, strong) NSString *name; @property (nonatomic, strong) NSString *identifier; @end
Zsyntetyzuj się
@implementation MyClass @synthesize name; @end
źródło
Przykład: 2 klasy (Person i Serial) chcą korzystać z usługi Viewer ... i muszą być zgodne z ViewerProtocol. viewerTypeOfDescription jest obowiązkową właściwością, której klasy subskrybentów muszą być zgodne.
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; }
Inny przykład z dziedziczeniem protokołu po podklasach
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; }
źródło
Zmienna anObject musi być zdefiniowana w Twojej definicji klasy TestProtocolsViewController, a protokół informuje Cię tylko, że powinna tam być.
Błędy kompilatora mówią prawdę - zmienna nie istnieje. W końcu @properties są tylko pomocnikami.
źródło