2010-11-10 15 views
30

Tengo una situación en la que parece que necesito agregar variables de instancia a una categoría, pero sé por los documentos de Apple que no puedo hacer eso. Entonces me pregunto cuál es la mejor alternativa o alternativa.Variables de instancia para Categorías de Objetivo C

Lo que quiero hacer es agregar una categoría que agregue funcionalidad a UIViewControllers. Lo encontraría útil en todos mis diferentes UIViewControllers, sin importar qué subclase específica UIViewController extiendan, por lo que creo que una categoría es la mejor solución. Para implementar esta funcionalidad, necesito varios métodos diferentes, y necesito rastrear datos entre ellos, así que eso es lo que me llevó a querer crear métodos de instancia.

En caso de que sea útil, esto es lo que específicamente deseo hacer. Quiero facilitar el seguimiento cuando el teclado del software se esconde y se muestra, para poder cambiar el tamaño del contenido en mi opinión. Descubrí que la única forma de hacerlo de manera confiable es poner el código en cuatro métodos diferentes de UIViewController y rastrear datos adicionales en variables de instancia. Entonces esos métodos y variables de instancia son lo que me gustaría poner en una categoría, así que no tengo que copiarlos y pegarlos cada vez que necesito manejar el teclado del software. (Si hay una solución más simple para este problema exacto, también está bien, ¡pero me gustaría saber la respuesta a las variables de instancia de categoría para futuras referencias!)

+5

¿Puedes hacer una subclase? – kennytm

Respuesta

0

¿Por qué no crear una subclase de UIViewController? a eso, ¿entonces usas esa clase (o una subclase de eso) en su lugar?

+6

'UITableViewController' es una subclase de' UIViewController'. Una categoría agregará funcionalidad a ambas, mientras que una subclase no lo hará. –

+1

Sospecho que el OP ha encontrado las subclases 'UIViewController' que no controlan. – Justin

+0

Veo tu punto. Algo en la redacción me hizo pensar que estas eran puramente las subclases directas de UIViewController del OP. Sospecho que KennyTM (basado en su comentario) lo leyó de la misma manera. :-) –

0

Dependiendo de lo que esté haciendo, es posible que desee utilizar métodos de categoría estática.

Por lo tanto, supongo que tienes este tipo de problema:

ScrollView tiene un par de textedits en ellos. El usuario escribe sobre la edición de texto, desea desplazarse por la vista de desplazamiento para que la edición de texto sea visible sobre el teclado.

+ (void) staticScrollView: (ScrollView*)sv scrollsTo:(id)someView 
{ 
    // scroll view to someviews's position or some such. 
} 

al regresar de esto no necesitaría necesariamente la vista para retroceder, por lo que no necesita almacenar nada.

Pero eso es todo lo que puedo pensar sin ejemplos de código, lo siento.

0

Creo que es posible agregar variables a una clase usando el tiempo de ejecución de Obj-C.
Encontré this discussion también.

+0

¿Esto lo convirtió en iOS? No me he mantenido actualizado con esa nueva característica (me parece tan sucio :-)). –

7

Creo que ahora es posible agregar propiedades sintetizadas a una categoría y las variables de instancia se crean automágicamente, pero nunca lo he probado, así que no estoy seguro si funcionará.

Una solución más hacky:

Crear un conjunto unitario NSDictionary que tendrá la UIViewController como la clave (o más bien su dirección de envuelta como un NSValue) y el valor de su propiedad como su valor.

Crear getter y setter para la propiedad que realmente va al diccionario para obtener/establecer la propiedad.

@interface UIViewController(MyProperty) 

@property (nonatomic, retain) id myProperty; 
@property (nonatomic, readonly, retain) NSMutableDcitionary* propertyDictionary; 

@end 

@implementation UIViewController(MyProperty) 

-(NSMutableDictionary*) propertyDictionary 
{ 
    static NSMutableDictionary* theDictionary = nil; 
    if (theDictionary == nil) 
    { 
     theDictioanry = [[NSMutableDictionary alloc] init]; 
    } 
    return theDictionary; 
} 


-(id) myProperty 
{ 
    NSValue* key = [NSValue valueWithPointer: self]; 
    return [[self propertyDictionary] objectForKey: key]; 
} 

-(void) setMyProperty: (id) newValue 
{ 
    NSValue* key = [NSValue valueWithPointer: self]; 
    [[self propertyDictionary] setObject: newValue forKey: key];  
} 

@end 

Dos problemas potenciales con el enfoque anterior:

  • no hay manera de eliminar las claves de los controladores de vista que se han desasignado. Mientras solo rastree un puñado, eso no debería ser un problema.O puede agregar un método para eliminar una clave del diccionario una vez que sepa que ya terminó.
  • No estoy 100% seguro de que el método isEqual: de NSValue compare contenido (es decir, el puntero envuelto) para determinar la igualdad o si solo compara self para ver si el objeto de comparación es el mismo NSValue exacto. Si es el último, tendrá que usar NSNumber en lugar de NSValue para las claves (NSNumber numberWithUnsignedLong: hará el truco tanto en plataformas de 32 bits como de 64 bits).
+4

.... o simplemente puede utilizar una referencia asociada –

26

Sí, puedes hacer esto, pero ya que estás preguntando, tengo que preguntar: ¿Estás absolutamente seguro de que lo necesitas? (Si dice "sí", vuelva atrás, descubra lo que quiere hacer y vea si hay una forma diferente de hacerlo)

Sin embargo, si realmente desea inyectar almacenamiento en una clase no lo hace t control, use un associative reference.

+0

No sabía acerca de referencias asociativas, buen consejo! –

+4

Nuevo enlace de referencia asociativo: http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocAssociativeReferences.html – lilbyrdie

+0

@lilbyrdie gracias, arreglé el enlace :) –

14

Recientemente, tuve que hacer esto (agregar estado a una Categoría). @Dave DeLong tiene la perspectiva correcta sobre esto. Al investigar el mejor enfoque, encontré un gran blog post de Tom Harrington. Me gusta la idea de @ JeremyP de usar declaraciones @property en la Categoría, pero no su implementación en particular (no es fan del singleton global o de tener referencias globales). Las referencias asociativas son el camino a seguir.

Aquí está el código para agregar (lo que parece ser) ivars a su categoría. He escrito sobre esto en detalle en here.

En file.h, la persona que llama sólo ve la limpieza, alto nivel de abstracción:

@interface UIViewController (MyCategory) 
@property (retain,nonatomic) NSUInteger someObject; 
@end 

En File.m, podemos implementar el @property (NOTA: Estos no pueden ser @ synthesize'd) :

@implementation UIViewController (MyCategory) 

- (NSUInteger)someObject 
{ 
    return [MyCategoryIVars fetch:self].someObject; 
} 

- (void)setSomeObject:(NSUInteger)obj 
{ 
    [MyCategoryIVars fetch:self].someObject = obj; 
} 

También tenemos que declarar y definir la clase MyCategoryIVars. Para facilitar la comprensión, he explicado esto fuera del orden de compilación adecuado. La interfaz @ debe colocarse antes de la implementación de Categoría @.

@interface MyCategoryIVars : NSObject 
@property (retain,nonatomic) NSUInteger someObject; 
+ (MyCategoryIVars*)fetch:(id)targetInstance; 
@end 

@implementation MyCategoryIVars 

@synthesize someObject; 

+ (MyCategoryIVars*)fetch:(id)targetInstance 
{ 
    static void *compactFetchIVarKey = &compactFetchIVarKey; 
    MyCategoryIVars *ivars = objc_getAssociatedObject(targetInstance, &compactFetchIVarKey); 
    if (ivars == nil) { 
    ivars = [[MyCategoryIVars alloc] init]; 
    objc_setAssociatedObject(targetInstance, &compactFetchIVarKey, ivars, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 
    [ivars release]; 
    } 
    return ivars; 
} 

- (id)init 
{ 
    self = [super init]; 
    return self; 
} 

- (void)dealloc 
{ 
    self.someObject = nil; 
    [super dealloc]; 
} 

@end 

El código anterior declara e implementa la clase que mantiene nuestros Ivars (someObject). Como realmente no podemos extender UIViewController, esto tendrá que hacer.

+1

En su ejemplo implementación, ¿para qué sirve la anulación '-init'? Parece ser un no-op. ¡Gracias! – Olie

+1

@Olie, tienes razón, no es necesario. Tiendo a ser un pequeño TOC con mi codificación. Como hay un dealloc, me gusta proporcionar un inicio coincidente para recordarme a mí mismo para tocar tanto si/cuando se agregan recursos al objeto. Puedes eliminarlo de forma segura. –

+0

No hay problema. Como soy nuevo en 'objc_setAssociatedObject()', no estaba seguro de si había algún efecto secundario extraño y deseable allí. ¡Gracias! – Olie

5

Esto se logra mejor usando la función incorporada ObjC Associated Objects (también conocido como Referencias Asociadas), en el ejemplo de abajo simplemente cambie a su categoría y reemplace el objeto asociado con su nombre de variable.

NSObject + AssociatedObject.h

@interface NSObject (AssociatedObject) 
@property (nonatomic, strong) id associatedObject; 
@end 

NSObject + AssociatedObject.m

#import <objc/runtime.h> 

@implementation NSObject (AssociatedObject) 
@dynamic associatedObject; 

- (void)setAssociatedObject:(id)object { 
    objc_setAssociatedObject(self, @selector(associatedObject), object, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 
} 

- (id)associatedObject { 
    return objc_getAssociatedObject(self, @selector(associatedObject)); 
} 

Vea aquí el tutorial completo:

http://nshipster.com/associated-objects/

3

Mencionó en muchos documentos en línea que no puede crear crear una nueva variable en la categoría, pero encontré una forma muy sencilla de lograrlo. Aquí está la forma en que permite declarar una nueva variable en la categoría.

En su.archivo

@interface UIButton (Default) 

    @property(nonatomic) UIColor *borderColor; 

@end 

h En el archivo .m

#import <objc/runtime.h> 
static char borderColorKey; 

@implementation UIButton (Default) 

- (UIColor *)borderColor 
{ 
    return objc_getAssociatedObject(self, &borderColorKey); 
} 

- (void)setBorderColor:(UIColor *)borderColor 
{ 
    objc_setAssociatedObject(self, &borderColorKey, 
          borderColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 
    self.layer.borderColor=borderColor.CGColor; 
} 

@end 

Eso es todo ahora que tiene la nueva variable.

Cuestiones relacionadas