2009-05-10 6 views
120

Je l'ai vu l'utilisation des protocoles Objective-C se sert d'une manière telle que:Comment gérer les protocoles Objective-C qui contiennent des propriétés?

@protocol MyProtocol <NSObject> 

@required 

@property (readonly) NSString *title; 

@optional 

- (void) someMethod; 

@end 

Je l'ai vu ce format utilisé au lieu d'écrire une superclasse en béton qui étendent les sous-classes. La question est, si vous vous conformez à ce protocole, avez-vous besoin de synthétiser les propriétés vous-même? Si vous étendez une superclasse, la réponse est évidemment non, vous n'avez pas besoin de le faire. Mais comment traiter les propriétés qu'un protocole doit respecter? Pour ma compréhension, vous devez toujours déclarer les variables d'instance dans le fichier d'en-tête d'un objet qui se conforme à un protocole qui nécessite ces propriétés. Dans ce cas, pouvons-nous supposer qu'ils ne sont qu'un principe directeur? CLearly le même n'est pas le cas pour une méthode requise. Le compilateur va claquer votre poignet pour exclure une méthode requise qu'un protocole liste. Quelle est l'histoire des propriétés?

Voici un exemple qui génère une erreur de compilation (Note: J'ai parés le code qui ne reflète pas sur le problème à la main):

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épondre

125

Le protocole indique simplement à tous ceux qui connaissent votre classe via le protocole que la propriété anObject sera présente. Les protocoles ne sont pas réels, ils n'ont pas de variables ou de méthodes elles-mêmes - ils décrivent seulement un ensemble spécifique d'attributs qui est vrai au sujet de votre classe afin que les objets contenant des références puissent les utiliser de manière spécifique.

Cela signifie que dans votre classe qui est conforme à votre protocole, vous devez tout faire pour vous assurer qu'anObject fonctionne.

@property et @synthesize sont au cœur de deux mécanismes qui génèrent du code pour vous. @property dit simplement qu'il y aura une méthode getter (et/ou setter) pour ce nom de propriété. Ces jours @property est suffisant pour avoir aussi des méthodes et une variable de stockage créé pour vous par le système (vous deviez ajouter @sythesize). Mais vous devez avoir quelque chose pour accéder et stocker la variable.

+72

Pour les propriétés définies dans un protocole, vous avez toujours besoin d'un "@synthesize" même dans l'environnement d'exécution moderne, ou vous devez dupliquer le "@property" dans votre définition d'interface pour obtenir l'auto-synthèse. –

+0

@JeffreyHarris Qu'en est-il de même dans Swift ?? –

+0

@KaranAlangat - il n'y a rien de tel que \ @synthesize dans Swift, mais tout comme ObjC, vous devez déclarer la propriété dans une classe qui prétend se conformer au protocole. Dans Swift, vous pouvez créer une catégorie qui définit une implémentation par défaut d'une fonction, mais autant que j'ai pu le dire, vous ne pouvez pas avoir une propriété par défaut pour un protocole. –

13

tout ce que vous avez à faire est vraiment laisser tomber une

@synthesize title; 

dans votre mise en œuvre et vous devriez être tous ensemble. Cela fonctionne de la même manière que de simplement placer la propriété dans votre interface de classe.

Edit:

Vous pouvez faire cela plus précisément:

@synthesize title = _title; 

Cela tombe en accord avec la façon dont la synthèse automatique de Xcode crée des propriétés et Ivars si vous utilisez la synthèse automatique, donc De cette façon, si votre classe a des propriétés d'un protocole et d'une classe, certains de vos ivars n'auront pas le format différent qui pourrait affecter la lisibilité.

+1

Êtes-vous complètement sûr? J'ai une propriété facultative définie dans un protocole, et quand je la synthétise seulement @ dans une classe concrète qui se conforme à ce protocole - j'obtiens une erreur de compilateur affirmant que c'est une variable non déclarée. Aucune faute de frappe confirmée. – Coocoo4Cocoa

+0

Je ne suis pas sûr des propriétés facultatives, mais une chose que j'ai oublié de mentionner comme mralex est que vous devez lier à une variable membre, soit en nommant ce titre de variable, ou en disant @synthesize title = myinstancevar; – Kevlar

+2

Si vous utilisez le temps d'exécution moderne, @synthesize est tout ce dont vous avez besoin, les ivars sous-jacents seront créés pour vous. Si vous ciblez x86 32 bits, l'erreur du compilateur est mentionnée, car vous ciblez le runtime hérité. –

0

La variable, anObject, doit être définie dans votre définition de classe TestProtocolsViewController, le protocole vous informe simplement qu'il doit être présent.

Les erreurs du compilateur vous disent la vérité - la variable n'existe pas. @properties ne sont que des aides après tout.

27

Voici un exemple de la mine qui fonctionne parfaitement, la définition du protocole d'abord:

@class ExampleClass; 

@protocol ExampleProtocol 

@required 

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

@end 

est Ci-dessous un exemple de travail d'une classe de soutien de ce protocole:

#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 
1

Protocol Architecture

Exemple: 2 classes (Person et Serial) veulent utiliser le service de Viewer ... et doivent se conformer à ViewerProtocol. viewerTypeOfDescription est une propriété obligatoire les classes d'abonnés doivent être conformes.

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

Un autre exemple avec l'héritage Protocole sur le sous-classement

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

Jetez un oeil à mon article PROPERTY IN PROTOCOL

Supposons que j'ai MyProtocol qui déclare une propriété de nom et MyClass qui est conforme à ce protocole

Choses à noter

  1. La propriété d'identificateur de MaClasse déclare et génère getter, setter variables et support _identifier
  2. La propriété name déclare seulement que MyClass possède un getter, setter dans l'en-tête. Il ne génère pas de getter, d'implémentation de setter et de variable de support.
  3. Je ne peux pas redéclarer cette propriété de nom, comme cela a déjà été déclaré par le protocole. Pour ce faire, criera une erreur

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

Comment utiliser la propriété dans le protocole

donc d'utiliser MyClass avec cette propriété de nom, nous devons faire soit

  1. Declare la propriété à nouveau (AppDelegate.h fait de cette façon)

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

    @implementation MyClass 
    
    @synthesize name; 
    
    @end 
    

+0

Les blocs de code imbriqués dans des listes doivent être indentés de huit espaces par ligne. C'est une bizarrerie relativement inconnue de la syntaxe de Markdown. J'ai édité votre réponse pour vous. – BoltClock

+1

Malheureusement, le lien ne fonctionne pas. – Koen

Questions connexes