La mejor forma de definir métodos privados para una clase en Objective-C

Empecé a progtwigr Objective-C y, con experiencia en Java, me pregunto cómo las personas que escriben Objective-C tratan con métodos privados.

Entiendo que puede haber varias convenciones y hábitos y pienso en esta pregunta como un agregador de las mejores técnicas que las personas usan para tratar con métodos privados en Objective-C.

Incluya un argumento para su enfoque al publicarlo. ¿Por qué es bueno? ¿Qué inconvenientes tiene (que usted conoce) y cómo lidia con ellos?


En cuanto a mis hallazgos hasta ahora.

Es posible usar categorías [por ejemplo, MyClass (Private)] definidas en el archivo MyClass.m para agrupar métodos privados.

Este enfoque tiene 2 problemas:

  1. Xcode (¿y el comstackdor?) No comprueba si define todos los métodos en la categoría privada en el bloque @implementation correspondiente
  2. Debe poner @interface declarando su categoría privada en el inicio del archivo MyClass.m, de lo contrario, Xcode se queja con un mensaje como “self not not responce to message” privateFoo “.

El primer problema se puede solucionar con una categoría vacía [por ejemplo, MyClass ()].
El segundo me molesta mucho. Me gustaría ver métodos privados implementados (y definidos) cerca del final del archivo; No sé si eso es posible.

No hay, como ya han dicho otros, algo así como un método privado en Objective-C. Sin embargo, comenzando en Objective-C 2.0 (que significa Mac OS X Leopard, iPhone OS 2.0 y posterior) puede crear una categoría con un nombre vacío (es decir, @interface MyClass () ) llamada Class Extension . Lo que es único de una extensión de clase es que las implementaciones de métodos deben ir en la misma @implementation MyClass que los métodos públicos. Así que estructurar mis clases de esta manera:

En el archivo .h:

 @interface MyClass { // My Instance Variables } - (void)myPublicMethod; @end 

Y en el archivo .m:

 @interface MyClass() - (void)myPrivateMethod; @end @implementation MyClass - (void)myPublicMethod { // Implementation goes here } - (void)myPrivateMethod { // Implementation goes here } @end 

Creo que la mayor ventaja de este enfoque es que te permite agrupar tus implementaciones de métodos por funcionalidad, no por la distinción público / privado (a veces arbitraria).

Realmente no hay un “método privado” en Objective-C, si el tiempo de ejecución puede determinar qué implementación usarlo lo hará. Pero eso no quiere decir que no haya métodos que no sean parte de la interfaz documentada. Para esos métodos, creo que una categoría está bien. En lugar de poner la @interface en la parte superior del archivo .m como su punto 2, lo pondría en su propio archivo .h. Una convención que sigo (y he visto en otra parte, creo que es una convención de Apple ya que Xcode ahora brinda soporte automático) es nombrar un archivo así después de su clase y categoría con un + separándolos, así que @interface GLObject (PrivateMethods) puede se puede encontrar en GLObject+PrivateMethods.h . La razón para proporcionar el archivo de encabezado es para que pueda importarlo en las clases de prueba de su unidad :-).

Por cierto, en lo que respecta a la implementación / definición de métodos cerca del final del archivo .m, puede hacerlo con una categoría implementando la categoría en la parte inferior del archivo .m:

 @implementation GLObject(PrivateMethods) - (void)secretFeature; @end 

o con una extensión de clase (lo que llama una “categoría vacía”), simplemente defina esos métodos al final. Los métodos de Objective-C se pueden definir y usar en cualquier orden en la implementación, por lo que no hay nada que te impida poner los métodos “privados” al final del archivo.

Incluso con las extensiones de clase, a menudo crearé un encabezado separado ( GLObject+Extension.h ) para que pueda usar esos métodos si es necesario, imitando la visibilidad de “amigo” o “protegido”.

Dado que esta respuesta se escribió originalmente, el comstackdor de clang ha comenzado a hacer dos pasadas para los métodos de Objective-C. Esto significa que puede evitar declarar sus métodos “privados” por completo, y ya sea que estén por encima o por debajo del sitio de llamadas, el comstackdor los encontrará.

Si bien no soy un experto en Objective-C, personalmente defino el método en la implementación de mi clase. De acuerdo, debe definirse antes (arriba) cualquier método que lo llame, pero definitivamente requiere la menor cantidad de trabajo por hacer.

La definición de sus métodos privados en el bloque @implementation es ideal para la mayoría de los propósitos. Clang verá estos dentro de @implementation , independientemente del orden de la statement. No es necesario declararlos en una continuación de clase (también conocida como extensión de clase) o en una categoría con nombre.

En algunos casos, deberá declarar el método en la continuación de la clase (por ejemplo, si usa el selector entre la continuación de la clase y la @implementation ).

static funciones static son muy buenas para métodos privados particularmente sensibles o de velocidad crítica.

Una convención para nombrar prefijos puede ayudarlo a evitar reemplazar accidentalmente los métodos privados (el nombre de la clase como prefijo es seguro).

Las categorías con nombre (por ejemplo, @interface MONObject (PrivateStuff) ) no son una idea particularmente buena debido a posibles colisiones de nombres al cargar. En realidad, solo son útiles para amigos o métodos protegidos (que rara vez son una buena opción). Para asegurarse de que está advertido de implementaciones de categoría incompletas, debe implementarlo realmente:

 @implementation MONObject (PrivateStuff) ...HERE... @end 

Aquí hay una pequeña hoja de trucos anotada:

MONObject.h

 @interface MONObject : NSObject // public declaration required for clients' visibility/use. @property (nonatomic, assign, readwrite) bool publicBool; // public declaration required for clients' visibility/use. - (void)publicMethod; @end 

MONObject.m

 @interface MONObject () @property (nonatomic, assign, readwrite) bool privateBool; // you can use a convention where the class name prefix is reserved // for private methods this can reduce accidental overriding: - (void)MONObject_privateMethod; @end // The potentially good thing about functions is that they are truly // inaccessible; They may not be overridden, accidentally used, // looked up via the objc runtime, and will often be eliminated from // backtraces. Unlike methods, they can also be inlined. If unused // (eg diagnostic omitted in release) or every use is inlined, // they may be removed from the binary: static void PrivateMethod(MONObject * pObject) { pObject.privateBool = true; } @implementation MONObject { bool anIvar; } static void AnotherPrivateMethod(MONObject * pObject) { if (0 == pObject) { assert(0 && "invalid parameter"); return; } // if declared in the @implementation scope, you *could* access the // private ivars directly (although you should rarely do this): pObject->anIvar = true; } - (void)publicMethod { // declared below -- but clang can see its declaration in this // translation: [self privateMethod]; } // no declaration required. - (void)privateMethod { } - (void)MONObject_privateMethod { } @end 

Otro enfoque que puede no ser obvio: un tipo de C ++ puede ser muy rápido y proporcionar un mayor grado de control, al tiempo que minimiza el número de métodos objc exportados y cargados.

Podría intentar definir una función estática debajo o encima de su implementación que tome un puntero a su instancia. Podrá acceder a cualquiera de sus variables de instancias.

 //.h file @interface MyClass : Object { int test; } - (void) someMethod: anArg; @end //.m file @implementation MyClass static void somePrivateMethod (MyClass *myClass, id anArg) { fprintf (stderr, "MyClass (%d) was passed %p", myClass->test, anArg); } - (void) someMethod: (id) anArg { somePrivateMethod (self, anArg); } @end 

cada objeto en el Objetivo C se ajusta al protocolo NSObject, que se aferra al método performSelector : . También estaba buscando una forma de crear algunos métodos “auxiliares o privados” que no necesitaba expuestos 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 …

defina su método con una firma similar como el siguiente código …

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

luego, cuando necesites hacer referencia al método, simplemente llámalo como selector …

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

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

Podrías usar bloques?

 @implementation MyClass id (^createTheObject)() = ^(){ return [[NSObject alloc] init];}; NSInteger (^addEm)(NSInteger, NSInteger) = ^(NSInteger a, NSInteger b) { return a + b; }; //public methods, etc. - (NSObject) thePublicOne { return createTheObject(); } @end 

Soy consciente de que esta es una vieja pregunta, pero es una de las primeras que encontré cuando estaba buscando una respuesta a esta pregunta. No he visto esta solución discutida en otro lado, así que avíseme si hay algo tonto en hacer esto.

Si quisiera evitar el bloque @interface en la parte superior, siempre podría colocar las declaraciones privadas en otro archivo MyClassPrivate.h no ideal, pero no satura la implementación.

MyClass.h

 interface MyClass : NSObject { @private BOOL publicIvar_; BOOL privateIvar_; } @property (nonatomic, assign) BOOL publicIvar; //any other public methods. etc @end 

MyClassPrivate.h

 @interface MyClass () @property (nonatomic, assign) BOOL privateIvar; //any other private methods etc. @end 

MyClass.m

 #import "MyClass.h" #import "MyClassPrivate.h" @implementation MyClass @synthesize privateIvar = privateIvar_; @synthesize publicIvar = publicIvar_; @end 

Una cosa más que no he mencionado aquí: Xcode admite archivos .h con “_private” en el nombre. Digamos que tienes una clase MyClass: tienes MyClass.m y MyClass.h y ahora también puedes tener MyClass_private.h. Xcode lo reconocerá e incluirá en la lista de “Contrapartes” en el Editor Asistente.

 //MyClass.m #import "MyClass.h" #import "MyClass_private.h" 

No hay forma de evitar el problema # 2. Así es como funciona el comstackdor de C (y, por lo tanto, el comstackdor de Objective-C). Si usa el editor de XCode, la ventana emergente de función debería facilitar la navegación por los bloques @interface y @implementation en el archivo.

Hay un beneficio de la ausencia de métodos privados. Puede mover la lógica que pretendía ocultar a la clase separada y usarla como delegado. En este caso, puede marcar el objeto delegado como privado y no será visible desde el exterior. Mover la lógica a la clase separada (tal vez varias) hace un mejor diseño de su proyecto. Porque tus clases se vuelven más simples y tus métodos están agrupados en clases con nombres propios.