2010-10-05 17 views
18

Tengo un objeto NSMutableArray al que deseo agregar métodos personalizados. Intenté subclasificar NSMutableArray, pero luego aparece un error que dice "método solo definido para la clase abstracta" cuando intento obtener el número de objetos con el método de recuento. ¿Por qué el método de conteo no es heredado?Debería subclase la clase NSMutableArray

He leído en otro lugar que tendré que importar algunos métodos NSMutableArray en mi clase personalizada si quiero usarlos. Solo quiero agregar un método personalizado a la clase NSMutableArray. Entonces, ¿debería subclase NSMutableArray o debería hacer algo más?

Respuesta

32

NSMutableArray no es una clase concreta, es sólo la superclase abstracta de un grupo de clase. La documentación para NSMutableArray tiene información acerca de cómo crear una subclase, ¡pero también le recomienda encarecidamente que no lo haga! Solo subclase si tiene una necesidad especial de almacenamiento real.

A class cluster significa que la clase real se elegirá en tiempo de ejecución. Una matriz creada vacía, no puede usar la misma clase que una matriz creada con 1000 elementos. El tiempo de ejecución puede hacer elecciones inteligentes de qué implementación usar para usted. En la práctica, NSMutableArray tendrá un puente CFArray. No debe preocuparse por nada, pero puede verlo si inspecciona el tipo de sus matrices en el depurador, nunca verá NSArray, pero con bastante frecuencia NSCFArray.

Como se mencionó anteriormente, la subclasificación no es lo mismo que extender una clase. Objective-C tiene el concepto de categorías. Una categoría es similar a lo que otros lenguajes de programación llaman mix-ins.

Si, por ejemplo, desea un método de conveniencia en NSMutableArray para ordenar todos los miembros de una propiedad, a continuación, definir la interfaz de la categoría en un archivo .h como tal:

@interface NSMutableArray (CWFirstnameSort) 
-(void)sortObjectsByProperty:(NSString*)propertyName; 
@end 

Y la puesta en práctica sería:

@implementation NSMutableArray (CWFirstnameSort) 
-(void)sortObjectsByProperty:(NSString*)propertyName; 
{ 
    NSSortDescriptor* sortDesc = [NSSortDescriptor sortDescriptorWithKey:propertName ascending:YES]; 
    [self sortUsingDescriptors:[NSArray arrayWithObject:sortDesc]]; 
} 
@end 

A continuación, utilice simplemente como:

[people sortObjectsByProperty:@"firstName"]; 
5

Si solo está agregando un método personalizado, use una categoría en NSMutableArray. Es un clúster de clase, por lo que la implementación es provista por subclases no documentadas. Debe proporcionar algunos métodos para generar su propia subclase. Sin embargo, si solo agrega una categoría, su método personalizado funcionará en NSMutableArrays en su aplicación.

Para comparar, aquí hay un ejemplo que escribí hace un tiempo atrás de implementing a custom NSMutableArray subclass.

+1

Me gustaría comentar que la implementación de una subclase NSMutableArray segura de tipo personalizado es una pérdida de tiempo. El tiempo dedicado a escribir esta subclase personalizada se habría gastado mejor al escribir algunas pruebas unitarias para validar que la lógica de la aplicación * real * sea sólida, sin agregarle más desorden. Su código adicional es solo más código para mantener, agregando solo más vectores de ataque para errores y fallas de seguridad sin agregar ningún beneficio real al usuario final. – PeyloW

+1

@PeyloW: una buena posición teórica para tomar, pero en última instancia, existe un código del mundo real donde la seguridad del tipo puede ser útil. Como se describe en la publicación que escribí, traes cualquier falla al punto donde se inyecta, no a algún otro momento arbitrario. Eso redujo la superficie de ataque al eliminar la posibilidad de sustitución de objetos maliciosos. –

+1

@PeyloW: también hay un código del mundo real que necesita una interfaz con CoreFoundation. A esto le importa si su implementación de matriz mutable es en realidad una subclase NSMutableArray u otra cosa; en ese punto su implementación tenía una mejor subclase NSMutableArray. –

4

Objective-C tiene un mecanismo para agregar métodos a las clases existentes llamadas Categories. De esta forma no tienes que crear tu propia subclase.

+0

El enlace ahora es malo. ¿Hay uno nuevo que funcione? – JohnK

+0

He actualizado el enlace. –

2

tengo t o estoy de acuerdo tanto con el nodo ninja como con PeyloW porque técnicamente tienen ambos derechos. En realidad, eso no me ayuda mucho.

Preámbulo: Hay muchas matrices en el código que todas contienen solo un pero diferente tipo de datos, p. classA, classB, classC.

Problema: Puedo mezclar matrices fácilmente pasando el incorrecto a, p. algunos selectores porque son todos NSMutableArray. No hay comprobación estática, solo en tiempo de ejecución.

Solución - 1er intento: Hacer una subclase de NSMutableArray por lo compilador hace comprobación estática y advierte tipo de datos sobre el mal.

Eso es bueno porque el compilador le advierte incluso cuando pasa el tipo incorrecto a -addObject o -objectAtIndex cuando sobrecarga esos. Eso es malo porque no se puede crear una instancia de la superclase NSMutableArray de esta manera.

Solución - Segundo intento: Cree una nueva clase (proxy) de algún tipo, p. NSObject como para NSMutableArray y agrega un miembro de clase del tipo NSMutableArray.

Esto es bueno porque puede crear instancias de NSMutableClass y verificaciones del compilador cuando pasa el tipo incorrecto a -addObject o -objectAtIndex cuando sobrecarga esos.

Lo malo es que tiene que sobrecargar todos los selectores de NSMutableArray que utiliza, no solo los que difieren en clase que contiene la matriz.

Conclusión: Cuando se genera un código sofisticado que tiene muchos tipos de clase en sus matrices, créeme, vale la pena probar. Simplemente al hacer esto, el compilador me mostró varios errores que no reconocería hasta que lo enfrentare en tiempo de ejecución. O incluso peor, cuando el usuario final lo enfrentaría.

-1

De la referencia Apple por NSArray, en los Métodos para anular sección:

cualquier subclase de NSArray debe invalidar cuentan los métodos de instancia primitivas y objectAtIndex :. Estos métodos deben operar en la tienda de respaldo que proporcione para los elementos de la colección. Para esta tienda de respaldo puede usar una matriz estática, un objeto NSArray estándar o algún otro tipo de datos o mecanismo. También puede elegir anular, parcial o totalmente, cualquier otro método NSArray para el que desee proporcionar una implementación alternativa.

Como nota al margen, en Objective-C, no existe una característica real que le permita declarar una clase como una clase abstracta, per se, como en Java, por ejemplo. Entonces, lo que hacen en su lugar es llamar algo así como el siguiente código, desde algún método que quieran forzar para que sea reemplazado por una subclase. En efecto, le dan a la clase semántica de "clase abstracta".

Este método de definición actúa como un método abstracto, lo que plantea una excepción si no se reemplaza, con el siguiente resultado:

-someAbstractFooMethod sólo se han definido para la clase abstracta. Definir - [YourClassName someAbstractFooMethod]!

- (void) someAbstractFooMethod 
{ 
    //Force subclassers to override this method 
    NSString *methodName = NSStringFromSelector(_cmd); 
    NSString *className = [self className]; 
    [NSException raise:NSInvalidArgumentException 
       format:@"-%@ only defined for abstract class. Define -[%@ %@]!", methodName, className, methodName]; 
} 
1

Esta es una entrada antigua, pero que me gustaría añadir mi experiencia. La respuesta de @PayloW es una buena respuesta y creo que responde perfectamente tu pregunta, sin embargo, nadie respondió tu pregunta al revés, así que lo haré aquí.

¿Debería la subclase NSMutableArray (o NSArray)? Depende de lo que quiere lograr. Si solo desea agregar un método para extender la funcionalidad BÁSICA de una matriz, como ordenar, entonces la respuesta de @PayloW es Categories. Sin embargo, si desea crear una clase personalizada que se comporte como una matriz, entonces sí, la subclasificación NSMutableArray es bastante fácil. Pero debido a que es un Clúster de Clase, no es exactamente una subclase como cabría esperar. Normalmente en subclases los métodos disponibles en la Súper clase están disponibles para su subclase o puede anularlos. Con Class Clusters, en su lugar, DEBE incluir los métodos de Super que va a utilizar y proporcionar una instancia _backend de la superclase para ajustar esos métodos.

A continuación se muestra un ejemplo de cómo te subclase NSMutableArray (o cualquier clase Cluster):

La interfaz:

@interface MyCustomArrayClass : NSMutableArray { 

    // Backend instance your class will be using 
    NSMutableArray *_backendArray; 
} 

// *** YOUR CUSTOM METHODS HERE (no need to put the Super's methods here) *** 

-(bool)isEmpty; 
-(id)nameAtIndex:(int)index; 
-(int)rowAtIndex:(int)index; 
-(int)columnAtIndex:(int)index; 

@end 

La aplicación:

@implementation MyCustomArrayClass 

-(instancetype)init { 

    if (self = [super init]) {    
     _backendArray = [@[] mutableCopy]; 
    } 

    return self; 
} 

// *** Super's Required Methods (because you're going to use them) *** 

-(void)addObject:(id)anObject { 
    [_backendArray addObject:anObject]; 
} 

-(void)insertObject:(id)anObject atIndex:(NSUInteger)index { 
    [_backendArray insertObject:anObject atIndex:index]; 
} 

-(void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject { 
    [_backendArray replaceObjectAtIndex:index withObject:anObject]; 
} 

-(id)objectAtIndex:(NSUInteger)index { 
    return [_backendArray objectAtIndex:index]; 
} 

-(NSUInteger)count { 
    return _backendArray.count; 
} 

-(void)removeObject:(id)anObject { 
    [_backendArray removeObject:anObject]; 
} 

-(void)removeLastObject { 
    [_backendArray removeLastObject]; 
} 

-(void)removeAllObjects { 
    [_backendArray removeAllObjects]; 
} 

-(void)removeObjectAtIndex:(NSUInteger)index { 
    [_backendArray removeObjectAtIndex:index]; 
} 

// *** YOUR CUSTOM METHODS *** 

-(bool)isEmpty { 
    return _backendArray.count == 0; 
} 

-(id)nameAtIndex:(int)index { 
    return ((MyObject *)_backendArray[index]).name; 
} 

-(int)rowAtIndex:(int)index { 
    return ((MyObject *)_backendArray[index]).row; 
} 

-(int)columnAtIndex:(int)index { 
    return ((MyObject *)_backendArray[index]).column; 
} 

@end 

Entonces para usar como lo siguiente:

MyCustomArrayClass *customArray = [[MyCustomArrayClass alloc] init]; 

// Your custom method 
int row = [customArray rowAtIndex:10]; 

// NSMutableArray method 
[customArray removeLastObject]; 

// Your custom class used just like an array !!! 
index = 20; 
MyObject *obj = customArray[index]; 

Todo funciona muy bien, es limpio y realmente muy bueno para implementar y usar.

Espero que ayude.

Cuestiones relacionadas