2009-05-10 10 views
120

que he visto el uso de protocolos de Objective-C se acostumbre de una manera tal como la siguiente:¿Cómo manejar los protocolos de Objective-C que contienen propiedades?

@protocol MyProtocol <NSObject> 

@required 

@property (readonly) NSString *title; 

@optional 

- (void) someMethod; 

@end 

que he visto este formato que se utiliza en lugar de escribir una superclase concreto que se extienden subclases. La pregunta es, si se ajusta a este protocolo, ¿necesita sintetizar las propiedades usted mismo? Si extiende una superclase, la respuesta es obviamente no, no es necesario. Pero, ¿cómo se manejan las propiedades que un protocolo requiere para cumplir?

A mi entender, aún necesita declarar las variables de instancia en el archivo de encabezado de un objeto que cumpla con un protocolo que requiera estas propiedades. En ese caso, ¿podemos suponer que solo son un principio rector? Por cierto, no es el mismo caso para un método requerido. El compilador golpeará su muñeca para excluir un método requerido que enumera un protocolo. ¿Cuál es la historia detrás de las propiedades?

Aquí hay un ejemplo que genera un error de compilación (Nota: He recortado el código que no refleja sobre el problema en cuestión):

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  

Respuesta

125

El protocolo solo le dice a todos los que conocen sobre su clase a través del protocolo, que la propiedad anObject estará allí. Los protocolos no son reales, no tienen variables ni métodos en sí mismos; solo describen un conjunto específico de atributos que es verdadero acerca de su clase, de modo que los objetos que tienen referencias a ellos pueden usarlos de maneras específicas.

Eso significa que en su clase que se ajuste a su protocolo, debe hacer todo lo posible para asegurarse de que funcione un Objeto.

@property y @synthesize son en el fondo dos mecanismos que generan código para usted. @property está diciendo que habrá un método getter (y/o setter) para ese nombre de propiedad. Estos días, @property solo, es suficiente para tener también métodos y una variable de almacenamiento creada por el sistema (solía tener que agregar @sythesize). Pero debes tener algo para acceder y almacenar la variable.

+72

Para propiedades definidas en un protocolo, aún necesita un "@synthesize" incluso en el tiempo de ejecución moderno, o necesita duplicar la "propiedad @" en la definición de su interfaz para obtener la auto-síntesis. –

+0

@JeffreyHarris ¿Qué hay de igual en Swift? –

+0

@KaranAlangat: no existe tal cosa como \ @synthesize en Swift, pero al igual que ObjC, es necesario declarar la propiedad en una clase que dice cumplir con el protocolo. En Swift puede crear una categoría que defina una implementación predeterminada de una función, pero por lo que he podido decir, no puede tener una propiedad predeterminada para un protocolo. –

13
todo lo que tiene que hacer es realmente

para dejar caer una

@synthesize title; 

en su aplicación y usted debe estar todo listo. funciona de la misma manera que simplemente poner la propiedad en su interfaz de clase.

Editar:

es posible que desee hacer esto más específicamente:

@synthesize title = _title; 

Esto caer en línea con la forma en la síntesis automática de Xcode crea propiedades y ivars si utiliza auto-síntesis, por lo De esa manera, si su clase tiene propiedades de un protocolo y una clase, algunos de sus ivars no tendrán el formato diferente que podría afectar la legibilidad.

+1

¿Estás completamente seguro? Tengo un conjunto de propiedades opcionales en un protocolo, y cuando solo @synthesize en una clase concreta que se ajuste a ese protocolo, obtengo un error de compilación alegando que es una variable no declarada. No hay errores tipográficos confirmados. – Coocoo4Cocoa

+0

No estoy seguro acerca de las propiedades opcionales, pero una cosa que olvidé mencionar como mralex es que necesitas vincularla a una variable miembro, ya sea nombrando ese título variable, o diciendo @synthesize title = myinstancevar; – Kevlar

+2

Si está utilizando el tiempo de ejecución moderno, @synthesize es todo lo que necesita, los ivars subyacentes se crearán para usted. Si tiene como objetivo x86 de 32 bits, se mencionará el error del compilador, porque se está orientando al tiempo de ejecución heredado. –

0

La variable, anObject, necesita definirse en su definición de clase TestProtocolsViewController, el protocolo simplemente le informa que debe estar allí.

Los errores del compilador le dicen la verdad: la variable no existe. @properties son solo ayudantes después de todo.

27

He aquí un ejemplo de mina que funciona perfectamente, la definición del protocolo en primer lugar:

@class ExampleClass; 

@protocol ExampleProtocol 

@required 

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

@end 

A continuación se muestra un ejemplo de trabajo de una clase de soporte este protocolo:

#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

Ejemplo: 2 clases (Persona y Serial) quieren usar el servicio de Viewer ... y deben cumplir con ViewerProtocol. viewerTypeOfDescription es una propiedad obligatoria que las clases de suscriptores deben cumplir.

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

Otro ejemplo con el protocolo de herencia sobre la subclasificación

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

Echa un vistazo a mi artículo PROPERTY IN PROTOCOL

Supongamos que tengo MyProtocol que declara una propiedad de nombre, y MiClase que se ajusta a este protocolo

Cosas dignas de mención

  1. La propiedad identificador en MyClass declara y genera getter, organismo y el respaldo _identifier variable de propiedad
  2. El nombre sólo declara que MyClass tiene un getter, organismo en el encabezado. No genera getter, implementación setter y variable de respaldo.
  3. No puedo volver a declarar esta propiedad de nombre, como ya ha declarado el protocolo. Haga esto va a gritar un error

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

Cómo utilizar la propiedad en el protocolo

Así que para usar MiClase con ese nombre de propiedad, que tenemos que hacer, ya sea

  1. Declare la propiedad nuevamente (AppDelegate.h lo hace)

    @interface MyClass : NSObject <MyProtocol> 
    
    @property (nonatomic, strong) NSString *name; 
    
    @property (nonatomic, strong) NSString *identifier; 
    
    @end 
    
  2. sintetizar mismos

    @implementation MyClass 
    
    @synthesize name; 
    
    @end 
    
+0

Los bloques de código anidados en listas deben sangrarse con ocho espacios por línea. Es una rareza relativamente desconocida de la sintaxis de Markdown. He editado tu respuesta por ti. – BoltClock

+1

Desafortunadamente, el enlace no funciona. – Koen

Cuestiones relacionadas