web-dev-qa-db-fra.com

Comment gérer les protocoles Objective-C contenant des propriétés?

J'ai constaté que l'utilisation des protocoles Objective-C était utilisée de la manière suivante:

@protocol MyProtocol <NSObject>

@required

@property (readonly) NSString *title;

@optional

- (void) someMethod;

@end

J'ai vu ce format utilisé au lieu d'écrire une super-classe concrète que les sous-classes étendent. La question est, si vous vous conformez à ce protocole, avez-vous besoin de synthétiser les propriétés vous-même? Si vous prolongez une super-classe, la réponse est évidemment non, vous n'en avez pas besoin. Mais comment traiter les propriétés auxquelles un protocole doit se conformer?

À ma connaissance, vous devez toujours déclarer les variables d'instance dans le fichier d'en-tête d'un objet conforme à un protocole nécessitant ces propriétés. Dans ce cas, pouvons-nous supposer qu'il ne s'agit que d'un principe directeur? Clairement, la même chose n'est pas le cas pour une méthode requise. Le compilateur va taper sur votre poignet pour exclure une méthode requise listée par un protocole. Quelle est l'histoire derrière les propriétés cependant?

Voici un exemple qui génère une erreur de compilation (Remarque: j'ai coupé le code qui ne reflète pas le problème actuel):

MonProtocole.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     
129
Coocoo4Cocoa

Le protocole indique simplement à tous ceux qui connaissent votre classe par le biais du protocole que la propriété anObject sera présente. Les protocoles ne sont pas réels, ils ne possèdent pas de variables ni de méthodes. Ils décrivent uniquement un ensemble d'attributs qui est vrai pour votre classe, de sorte que les objets contenant des références puissent les utiliser de manière spécifique.

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

@property et @synthesize _ sont au cœur de deux mécanismes qui génèrent du code pour vous. @property est juste en train de dire qu'il y aura une méthode getter (et/ou setter) pour ce nom de propriété. Ces jours-ci @property seul est suffisant pour avoir également des méthodes et une variable de stockage créées pour vous par le système (vous aviez l'habitude d'ajouter @sythesize). Mais vous devez avoir quelque chose pour accéder et stocker la variable.

Voici un exemple de mien qui fonctionne parfaitement, la définition du protocole tout d’abord:

@class ExampleClass;

@protocol ExampleProtocol

@required

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

@end

Voici un exemple de travail d’une classe prenant en charge 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
31
reddersky

tout ce que vous avez à faire est vraiment de laisser tomber un

@synthesize title;

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

Edit:

Vous voudrez peut-être faire ceci plus spécifiquement:

@synthesize title = _title;

Cela correspondra à la façon dont la synthèse automatique de xcode crée des propriétés et des ivars si vous utilisez la synthèse automatique. Ainsi, 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 avoir un impact. lisibilité.

13
Kevlar

Jetez un coup d'œil à mon article PROPERTY IN PROTOCOL

Supposons que MyProtocol déclare une propriété de nom et que MyClass soit conforme à ce protocole.

Choses à noter

  1. La propriété identifiant dans MyClass déclare et génère la variable _identifier getter, setter et backing
  2. La propriété name déclare uniquement que MyClass a 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, car elle est déjà déclarée par le protocole. Est-ce que cela va crier une erreur

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

Comment utiliser une propriété dans un protocole

Donc, pour utiliser MyClass avec cette propriété de nom, nous devons faire soit

  1. Déclarez à nouveau la propriété (c'est AppDelegate.h)

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

    @implementation MyClass
    
    @synthesize name;
    
    @end
    
6
onmyway133

Protocol Architecture

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

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

Un autre exemple avec l'héritage de 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;
}
1
Luc-Olivier

La variable, unObjet, doit être définie dans la définition de votre classe TestProtocolsViewController, le protocole vous informant simplement qu'il devrait être là.

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

0
mralex