iphone tamaño - ¿Cuáles son las mejores prácticas que utiliza al escribir Objective-C y Cocoa?




recortar se (25)

Sé sobre el HIG (¡que es bastante útil!), Pero qué prácticas de programación utiliza al escribir Objective-C, y más específicamente cuando usa Cocoa (o CocoaTouch).


Answers

Active todas las advertencias de GCC, luego desactive las que son causadas regularmente por los encabezados de Apple para reducir el ruido.

También ejecute el análisis estático Clang con frecuencia; puede habilitarlo para todas las compilaciones a través de la configuración de compilación "Run Static Analyzer".

Escribe pruebas unitarias y ejecútalas con cada compilación.



Evitar autorelease

Debido a que normalmente (1) no tiene control directo sobre su vida útil, los objetos lanzados automáticamente pueden persistir por un tiempo comparativamente largo y aumentar innecesariamente la huella de memoria de su aplicación. Si bien en el escritorio esto puede tener poca importancia, en plataformas más restringidas puede ser un problema importante. Por lo tanto, en todas las plataformas, y especialmente en las plataformas más restringidas, se considera la mejor práctica para evitar el uso de métodos que conduzcan a la publicación automática de objetos y, en su lugar, se recomienda utilizar el patrón alloc / init.

Así, en lugar de:

aVariable = [AClass convenienceMethod];

donde sea posible, deberías usar:

aVariable = [[AClass alloc] init];
// do things with aVariable
[aVariable release];

Cuando está escribiendo sus propios métodos que devuelven un objeto recién creado, puede aprovechar la convención de nomenclatura de Cocoa para señalar al receptor que debe liberarse al añadir el nombre del método con "nuevo".

Así, en lugar de:

- (MyClass *)convenienceMethod {
    MyClass *instance = [[[self alloc] init] autorelease];
    // configure instance
    return instance;
}

usted podría escribir:

- (MyClass *)newInstance {
    MyClass *instance = [[self alloc] init];
    // configure instance
    return instance;
}

Dado que el nombre del método comienza con "nuevo", los consumidores de su API saben que son responsables de liberar el objeto recibido (consulte, por ejemplo, el método newObject de newObject ).

(1) Puede tomar el control utilizando sus propios grupos locales de autorelease. Para más sobre esto, vea Grupos de Autorelease .


Ordenar las cadenas como el usuario quiera

Cuando ordena cadenas para presentar al usuario, no debe usar el método simple compare: En su lugar, siempre debe utilizar métodos de comparación localizedCompare: como localizedCompare: o localizedCaseInsensitiveCompare:

Para obtener más detalles, consulte Buscar, comparar y ordenar cadenas .


Sé que pasé por alto esto cuando entré por primera vez en la programación de Cocoa.

Asegúrese de que comprende las responsabilidades de gestión de memoria con respecto a los archivos NIB. Usted es responsable de liberar los objetos de nivel superior en cualquier archivo NIB que cargue. Lea la documentación de Apple sobre el tema.


Si está utilizando Leopard (Mac OS X 10.5) o posterior, puede usar la aplicación Instruments para encontrar y rastrear pérdidas de memoria. Después de construir su programa en Xcode, seleccione Ejecutar> Iniciar con herramienta de rendimiento> Fugas.

Incluso si su aplicación no muestra ninguna fuga, es posible que mantenga los objetos por mucho tiempo. En Instrumentos, puede usar el instrumento ObjectAlloc para esto. Seleccione el instrumento ObjectAlloc en su documento de Instrumentos y muestre los detalles del instrumento (si aún no se muestra) seleccionando Ver> Detalles (debería tener una marca de verificación al lado). En "Vida útil de asignación" en el detalle de ObjectAlloc, asegúrese de elegir el botón de opción junto a "Creado y vivo".

Ahora, cuando deje de grabar su aplicación, al seleccionar la herramienta ObjectAlloc le mostrará cuántas referencias hay de cada objeto que aún vive en su aplicación en la columna "# Net". Asegúrese de no solo ver sus propias clases, sino también las clases de los objetos de nivel superior de sus archivos NIB. Por ejemplo, si no tiene ventanas en la pantalla y ve referencias a una ventana sin conexión de NS, es posible que no la haya publicado en su código.


No escriba Objective-C como si fuera Java / C # / C ++ / etc.

Una vez vi a un equipo acostumbrado a escribir aplicaciones web Java EE intentar escribir una aplicación de escritorio Cocoa. Como si fuera una aplicación web Java EE. Había muchos AbstractFooFactory y FooFactory e IFoo y Foo volando cuando lo único que necesitaban era una clase de Foo y posiblemente un protocolo de Fooable.

Parte de asegurarse de no hacer esto es entender realmente las diferencias en el idioma. Por ejemplo, no necesita las clases abstractas de fábrica y de fábrica anteriores porque los métodos de clase de Objective-C se distribuyen de forma tan dinámica como los métodos de instancia, y se pueden anular en subclases.


Ser más funcional .

Objective-C es un lenguaje orientado a objetos, pero consciente del estilo funcional de Cocoa Framework, y en muchos casos está diseñado para ser funcional.

  1. Hay separación de mutabilidad. Usa clases inmutables como objeto primario, y objeto mutable como secundario. Por ejemplo, use NSArray principalmente, y use NSMutableArray solo cuando lo necesite.

  2. Hay funciones puras. No tantos, comprar muchas de las API de framework están diseñadas como función pura. Mira funciones como CGRectMake()o CGAffineTransformMake(). Obviamente la forma del puntero se ve más eficiente. Sin embargo, los argumentos indirectos con punteros no pueden ofrecer efectos secundarios libres. Diseñar estructuras puramente tanto como sea posible. Separar los objetos de estado par. Se usa en -copylugar de -retaincuando se pasa un valor a otro objeto. Porque el estado compartido puede influir en la mutación para valorar en otro objeto en silencio. Así que no puede ser libre de efectos secundarios. Si tiene un valor externo desde el objeto, cópielo. Así que también es importante diseñar el estado compartido lo más mínimo posible.

Sin embargo, no tengas miedo de usar funciones impuras también.

  1. Hay perezosa evaluación. Ver algo como -[UIViewController view]propiedad. La vista no se creará cuando se crea el objeto. Se creará cuando la persona que llama lea la viewpropiedad por primera vez. UIImageno se cargará hasta que esté siendo dibujado. Hay muchas implementaciones como este diseño. Este tipo de diseños son muy útiles para la administración de recursos, pero si no conoce el concepto de evaluación perezosa, no es fácil entender el comportamiento de los mismos.

  2. Hay cierre. Use bloques de C tanto como sea posible. Esto simplificará tu vida enormemente. Pero lea una vez más acerca de la administración de bloque de memoria antes de usarlo.

  3. Hay semiautomático GC. NSAutoreleasePool. Utilizar -autoreleaseprimaria. Use manual -retain/-releasesecundario cuando realmente lo necesite. (por ejemplo: optimización de memoria, eliminación explícita de recursos)


Hay algunas cosas que comencé a hacer que no creo que sean estándar:

1) Con el advenimiento de las propiedades, ya no uso "_" para prefijar variables de clase "privadas". Después de todo, si otras clases pueden acceder a una variable, ¿no debería haber una propiedad para ella? Siempre me disgustó el prefijo "_" para hacer el código más feo, y ahora puedo omitirlo.

2) Hablando de cosas privadas, prefiero ubicar las definiciones de métodos privados dentro del archivo .m en una extensión de clase como tal:

#import "MyClass.h"

@interface MyClass ()
- (void) someMethod;
- (void) someOtherMethod;
@end

@implementation MyClass

¿Por qué desordenar el archivo .h con cosas que los extraños no deberían preocuparse? El empty () funciona para categorías privadas en el archivo .m, y emite advertencias de compilación si no implementa los métodos declarados.

3) He comenzado a colocar dealloc en la parte superior del archivo .m, justo debajo de las directivas de @synthesize. ¿No debería estar en la parte superior de la lista de cosas que quiere pensar en una clase? Eso es especialmente cierto en un entorno como el iPhone.

3.5) En las celdas de la tabla, haga que todos los elementos (incluida la celda) sean opacos para el rendimiento. Eso significa establecer el color de fondo apropiado en todo.

3.6) Al usar un NSURLConnection, como regla, es posible que desee implementar el método delegado:

- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
                  willCacheResponse:(NSCachedURLResponse *)cachedResponse
{
      return nil;
}

Me parece que la mayoría de las llamadas web son muy singulares y es más una excepción que la regla que deseará que las respuestas se almacenen en caché, especialmente para las llamadas de servicios web. Implementar el método como se muestra deshabilita el almacenamiento en caché de respuestas.

También de interés, son algunos buenos consejos específicos para iPhone de Joseph Mattiello (recibidos en una lista de correo de iPhone). Hay más, pero pensé que estos fueron los más útiles en general (tenga en cuenta que algunos bits ahora se han editado ligeramente desde el original para incluir detalles ofrecidos en las respuestas):

4) Solo use precisión doble si tiene que hacerlo, como cuando trabaja con CoreLocation. Asegúrese de terminar sus constantes en 'f' para hacer que gcc las almacene como flotadores.

float val = someFloat * 2.2f;

Esto es sobre todo importante cuando algo de someFloat puede ser doble, no necesitas las matemáticas de modo mixto, ya que estás perdiendo precisión en "val" en el almacenamiento. Si bien los números de punto flotante son compatibles con el hardware en los iPhones, aún puede tomar más tiempo hacer aritmética de doble precisión en lugar de precisión simple. Referencias:

En los teléfonos más antiguos, los cálculos supuestamente funcionan a la misma velocidad, pero puede tener más componentes de precisión simple en los registros que en los dobles, por lo que para muchos cálculos, la precisión única terminará siendo más rápida.

5) Establecer sus propiedades como no nonatomic . Son atomic por defecto y en síntesis, se creará un código de semáforo para evitar problemas de subprocesos múltiples. Es probable que el 99% de usted no tenga que preocuparse por esto y el código está mucho menos hinchado y es más eficiente en memoria cuando se configura en no atómico.

6) SQLite puede ser una forma muy rápida de almacenar grandes conjuntos de datos. Una aplicación de mapas, por ejemplo, puede almacenar en caché sus mosaicos en archivos SQLite. La parte más cara es la E / S del disco. Evita muchas escrituras pequeñas enviando BEGIN; y COMMIT; Entre grandes bloques. Usamos un temporizador de 2 segundos, por ejemplo, que se reinicia en cada nuevo envío. Cuando expira, enviamos COMMIT; , lo que hace que todas tus escrituras vayan en una gran parte. SQLite almacena los datos de la transacción en el disco y, al hacer esto, el ajuste Begin / End evita la creación de muchos archivos de transacción, agrupando todas las transacciones en un solo archivo.

Además, SQL bloqueará su GUI si está en su hilo principal. Si tiene una consulta muy larga, es una buena idea almacenar sus consultas como objetos estáticos y ejecutar su SQL en un hilo separado. Asegúrese de envolver todo lo que modifique la base de datos para las cadenas de consulta en los @synchronize() {} . Para consultas cortas, simplemente deje las cosas en el hilo principal para una mayor comodidad.

Más consejos de optimización de SQLite están aquí, aunque el documento parece desactualizado, muchos de los puntos probablemente todavía sean buenos;

http://web.utk.edu/~jplyon/sqlite/SQLite_optimization_FAQ.html


Utilice NSAssert y amigos. Uso nil como objeto válido todo el tiempo ... especialmente el envío de mensajes a nil es perfectamente válido en Obj-C. Sin embargo, si realmente quiero asegurarme del estado de una variable, utilizo NSAssert y NSParameterAssert, que ayuda a localizar problemas fácilmente.


IBOutlets

Históricamente, la gestión de memoria de las salidas ha sido deficiente. La mejor práctica actual es declarar puntos de venta como propiedades:

@interface MyClass :NSObject {
    NSTextField *textField;
}
@property (nonatomic, retain) IBOutlet NSTextField *textField;
@end

El uso de propiedades hace que la semántica de administración de memoria sea clara; también proporciona un patrón consistente si utiliza la síntesis de variables de instancia.


Trate de evitar lo que ahora he decidido llamar Newbiecategoryaholism. Cuando los recién llegados a Objective-C descubren categorías, a menudo se vuelven locos, agregando pequeñas categorías útiles a cada clase existente ( "¿Qué? ¡Puedo agregar un método para convertir un número a números romanos a NSNumber rock on!" ).

No hagas esto

Su código será más portátil y más fácil de entender sin docenas de pequeños métodos de categorías sobre dos docenas de clases básicas.

La mayoría de las veces, cuando realmente crees que necesitas un método de categoría para ayudar a simplificar algunos códigos, verás que nunca terminarás reutilizando el método.

También hay otros peligros, a menos que esté marcando el nombre de sus métodos de categoría (¿y quién además de la drenota totalmente loca es?) Existe la posibilidad de que Apple, o un complemento, o alguna otra cosa que se ejecute en su espacio de direcciones, también definirá la misma categoría Método con el mismo nombre con un efecto secundario ligeramente diferente ....

DE ACUERDO. Ahora que ha sido advertido, ignore "no haga esta parte". Pero ejercite extrema moderación.


No use cadenas desconocidas como cadenas de formato

Cuando los métodos o funciones toman un argumento de cadena de formato, debe asegurarse de que tiene control sobre el contenido de la cadena de formato.

Por ejemplo, al registrar cadenas, es tentador pasar la variable de cadena como único argumento a NSLog :

    NSString *aString = // get a string from somewhere;
    NSLog(aString);

El problema con esto es que la cadena puede contener caracteres que se interpretan como cadenas de formato. Esto puede llevar a problemas erróneos de salida, bloqueos y seguridad. En su lugar, debe sustituir la variable de cadena en una cadena de formato:

    NSLog(@"%@", aString);

Asegúrese de marcar la página Magia de depuración . Esta debería ser tu primera parada cuando te golpees la cabeza contra una pared mientras intentas encontrar la fuente de un error de cacao.

Por ejemplo, le dirá cómo encontrar el método donde primero asignó la memoria que más tarde está causando bloqueos (como durante la finalización de la aplicación).


Propiedades declaradas

Normalmente debe usar la característica de Propiedades declaradas de Objective-C 2.0 para todas sus propiedades. Si no son públicos, agréguelos en una extensión de clase. El uso de propiedades declaradas hace que la semántica de administración de memoria sea clara de inmediato, y le facilita la verificación del método dealloc. Si agrupa las declaraciones de propiedad, puede escanearlas rápidamente y compararlas con la implementación de su método dealloc.

Debe pensar mucho antes de no marcar las propiedades como 'no atómicas'. Como se señala en la Guía de lenguaje de programación de Objective C , las propiedades son atómicas por defecto e incurren en una sobrecarga considerable. Además, el simple hecho de hacer que todas sus propiedades sean atómicas no hace que su aplicación sea segura para subprocesos. También tenga en cuenta, por supuesto, que si no especifica "no atómico" e implementa sus propios métodos de acceso (en lugar de sintetizarlos), debe implementarlos de forma atómica.


Todos estos comentarios son excelentes, pero estoy realmente sorprendido de que nadie haya mencionado la Guía de estilo Objective-C de Google que se publicó hace un tiempo. Creo que han hecho un trabajo muy completo.


Escribir pruebas unitarias. Puedes probar muchas cosas en Cocoa que podrían ser más difíciles en otros marcos. Por ejemplo, con el código UI, generalmente puede verificar que las cosas estén conectadas como deberían estar y confiar en que funcionarán cuando se utilicen. Y puede configurar los métodos de estado e invocación de delegado fácilmente para probarlos.

Tampoco tiene visibilidad de métodos públicos frente a protegidos frente a métodos privados que le impiden escribir pruebas para sus aspectos internos.


Resista subclasificar el mundo. En Cocoa, mucho se hace a través de la delegación y el uso del tiempo de ejecución subyacente que en otros marcos se realiza a través de subclases.

Por ejemplo, en Java usa mucho las subclases de *Listener anónimas y en .NET usa mucho las subclases EventArgs . En Cocoa, tampoco lo haces, en su lugar se usa la acción de destino.


Use las convenciones y terminología estándar de nomenclatura y formato de Cocoa en lugar de lo que esté acostumbrado en otro entorno. Hay muchos desarrolladores de Cocoa por ahí, y cuando otro de ellos comience a trabajar con su código, será mucho más accesible si se ve y se siente similar a otro código de Cocoa.

Ejemplos de qué hacer y qué no hacer:

  • No declare id m_something; en la interfaz de un objeto y llámelo una variable o campo miembro ; use something o _something para su nombre y _something una variable de instancia .
  • No nombre un getter -getSomething ; El nombre apropiado de Cacao es solo -something .
  • No nombrar un setter -something: debe ser -setSomething:
  • El nombre del método está entremezclado con los argumentos e incluye dos puntos; es -[NSObject performSelector:withObject:] , no NSObject::performSelector .
  • Use inter-caps (CamelCase) en los nombres de métodos, parámetros, variables, nombres de clases, etc. en lugar de barras inferiores (guiones bajos).
  • Los nombres de clase comienzan con una letra mayúscula, una variable y un nombre de método en minúscula.

Hagas lo que hagas, no uses la notación húngara al estilo de Win16 / Win32. Incluso Microsoft renunció a eso con el cambio a la plataforma .NET.


Piensa en valores nulos.

Como señala esta pregunta , los mensajes a nil son válidos en Objective-C. Si bien esto es con frecuencia una ventaja, lo que lleva a un código más limpio y más natural, la característica puede llevar ocasionalmente a errores peculiares y difíciles de rastrear si obtiene un valor nil cuando no lo esperaba.


Simple pero a menudo olvidado. Según la especificación:

En general, los métodos en diferentes clases que tienen el mismo selector (el mismo nombre) también deben compartir los mismos tipos de retorno y argumento. Esta restricción es impuesta por el compilador para permitir el enlace dinámico.

en cuyo caso, se considerará que todos los mismos selectores con nombre, incluso en clases diferentes , tienen tipos de devolución / argumento idénticos. Aquí hay un ejemplo simple.

@interface FooInt:NSObject{}
-(int) print;
@end

@implementation FooInt
-(int) print{
    return 5;
}
@end

@interface FooFloat:NSObject{}
-(float) print;
@end

@implementation FooFloat
-(float) print{
    return 3.3;
}
@end

int main (int argc, const char * argv[]) {

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];    
    id f1=[[FooFloat alloc]init];
    //prints 0, runtime considers [f1 print] to return int, as f1's type is "id" and FooInt precedes FooBar
    NSLog(@"%f",[f1 print]);

    FooFloat* f2=[[FooFloat alloc]init];
    //prints 3.3 expectedly as the static type is FooFloat
    NSLog(@"%f",[f2 print]);

    [f1 release];
    [f2 release]
    [pool drain];

    return 0;
}   

Regla de oro: si alloc entonces release !

ACTUALIZACIÓN: A menos que estés usando ARC


Algunos de estos ya se han mencionado, pero esto es lo que se me ocurre de la cabeza:

  • Siga las reglas de denominación de KVO. Incluso si no usa KVO ahora, en mi experiencia muchas veces sigue siendo beneficioso en el futuro. Y si está utilizando KVO o enlaces, necesita saber que las cosas van a funcionar de la manera en que deben hacerlo. Esto abarca no solo los métodos de acceso y las variables de instancia, sino también las relaciones a muchos, la validación, las claves dependientes de notificación automática, etc.
  • Pon los métodos privados en una categoría. No solo la interfaz, sino también la implementación. Es bueno tener una cierta distancia conceptual entre los métodos privados y no privados. Incluyo todo en mi archivo .m
  • Poner métodos de hilo de fondo en una categoría. Lo mismo que arriba. Descubrí que es bueno mantener una barrera conceptual clara cuando estás pensando qué hay en el hilo principal y qué no.
  • Utilice la #pragma mark [section] . Por lo general, agrupo por mis propios métodos, las anulaciones de cada subclase y cualquier información o protocolo formal. Esto hace que sea mucho más fácil saltar exactamente a lo que estoy buscando. En el mismo tema, agrupe los métodos similares (como los métodos de delegado de una vista de tabla), no los pegue en ningún lugar.
  • Prefijo los métodos privados y ivars con _. Me gusta su apariencia, y es menos probable que use un ivar cuando me refiero a una propiedad por accidente.
  • No utilice métodos / propiedades de mutador en init y dealloc. Nunca me ha pasado nada malo por eso, pero puedo ver la lógica si cambias el método para hacer algo que depende del estado de tu objeto.
  • Poner IBOutlets en propiedades. De hecho, acabo de leer esto aquí, pero voy a empezar a hacerlo. Independientemente de los beneficios de la memoria, parece mejor estilísticamente (al menos para mí).
  • Evite escribir código que no necesita absolutamente. Esto realmente cubre muchas cosas, como hacer ivars cuando un #define , o almacenar en caché una matriz en lugar de clasificarla cada vez que se necesiten los datos. Hay mucho que puedo decir sobre esto, pero la conclusión es que no escriba código hasta que lo necesite, o que el generador de perfiles le indique que lo haga. Hace que las cosas sean mucho más fáciles de mantener a largo plazo.
  • Termina lo que empieces. Tener un montón de código de buggy a medio terminar es la forma más rápida de matar a un proyecto. Si necesita un método de stub que esté bien, simplemente indíquelo colocando NSLog( @"stub" ) dentro, o como quiera que haga un seguimiento de las cosas.

No olvide que NSWindowController y NSViewController liberarán los objetos de nivel superior de los archivos NIB que rigen.

Si carga manualmente un archivo NIB, es responsable de liberar los objetos de nivel superior de ese NIB cuando haya terminado con ellos.


todos los objetos en Objective C cumplen con el protocolo NSObject, que se mantiene en el método performSelector : . También estaba buscando una forma de crear algunos métodos "auxiliares o privados" que no necesitaba exponer a nivel público. Si desea crear un método privado sin gastos generales y no tener que definirlo en su archivo de encabezado, entonces pruebe esto ...

define el método con una firma similar al código siguiente ...

-(void)myHelperMethod: (id) sender{
     // code here...
}

luego, cuando necesite hacer referencia al método, simplemente llámelo como selector ...

[self performSelector:@selector(myHelperMethod:)];

esta línea de código invocará el método que creó y no tendrá una advertencia molesta por no tenerlo definido en el archivo de encabezado.







iphone objective-c cocoa cocoa-touch ios