objective c - La mejor manera de definir métodos privados para una clase en Objective-C
cocoa code-completion (12)
Acabo de comenzar a programar Objective-C y, teniendo antecedentes en Java, me pregunto cómo las personas que escriben programas de Objective-C tratan con los 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 usa la gente para lidiar con los métodos privados en Objective-C.
Por favor incluya un argumento para su enfoque cuando lo publique. ¿Por qué es bueno? ¿Qué inconvenientes tiene (que usted conoce) y cómo los maneja?
En cuanto a mis conclusiones hasta ahora.
Es posible usar categories [por ejemplo, MyClass (Private)] definidas en el archivo MyClass.m para agrupar métodos privados.
Este enfoque tiene 2 problemas:
- Xcode (¿y el compilador?) No comprueba si define todos los métodos en la categoría privada en el bloque de implementación correspondiente
- Debe colocar @interface declarando su categoría privada al comienzo del archivo MyClass.m, de lo contrario Xcode se queja con un mensaje como "self" puede no responder al mensaje "privateFoo".
El primer problema se puede solucionar con una categoría vacía [p. Ej., 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.
Como otras personas dijeron que la definición de métodos privados en el bloque @implementation
está bien para la mayoría de los propósitos.
Sobre el tema de la organización de códigos : me gusta mantenerlos juntos bajo la pragma mark private
para una navegación más fácil en Xcode
@implementation MyClass
// .. public methods
# pragma mark private
// ...
@end
Definir sus métodos privados en el bloque @implementation
es ideal para la mayoría de los propósitos. Clang los verá dentro de la @implementation
, independientemente del orden de declaración. 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 utiliza 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 la anulación accidental de métodos privados (encuentro que el nombre de la clase es seguro como prefijo).
Las categorías con nombre (por ejemplo, @interface MONObject (PrivateStuff)
) no son una idea particularmente buena debido a las posibles colisiones de nombres al cargar. Realmente solo son útiles para amigos o métodos protegidos (que rara vez son una buena opción). Para asegurarse de que se le advierte de implementaciones de categorías incompletas, debe implementarlo:
@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
// (e.g. 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 C ++ puede ser muy rápido y proporcionar un grado de control mucho mayor, al mismo tiempo que minimiza el número de métodos objc exportados y cargados.
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 que el diseño de su proyecto sea mejor. Porque tus clases se vuelven más simples y tus métodos se agrupan en clases con nombres propios.
No hay manera de solucionar el problema # 2. Así es como funciona el compilador de C (y, por tanto, el compilador de Objective-C). Si utiliza el editor XCode, la función emergente debería facilitar la navegación por los bloques @interface
y @implementation
en el archivo.
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 pregunta antigua, pero es una de las primeras que encontré cuando estaba buscando una respuesta a esta pregunta. No he visto esta solución discutida en ningún otro lugar, así que avíseme si hay algo absurdo en hacer esto.
Puede intentar definir una función estática por debajo o por encima de su implementación que lleve 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
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 formen parte de la interfaz documentada. Para esos métodos creo que una categoría está bien. En lugar de colocar la @interface
en la parte superior del archivo .m como en el 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 da soporte automático para ella) es nombrar a ese archivo después de su clase y categoría con a + separándolos, por lo que @interface GLObject (PrivateMethods)
puede se encuentra en GLObject+PrivateMethods.h
. El motivo para proporcionar el archivo de encabezado es para que pueda importarlo en sus clases de prueba de 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 llamas una "categoría vacía"), simplemente define esos métodos al final. Los métodos Objective-C pueden definirse y usarse en cualquier orden en la implementación, por lo que no hay nada que le 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".
Desde que esta respuesta se escribió originalmente, el compilador clang comenzó a hacer dos pases para los métodos de Objective-C. Esto significa que puede evitar declarar sus métodos "privados" completamente, y si están por encima o por debajo del sitio de llamada, el compilador los encontrará.
Si bien no soy un experto en Objective-C, personalmente defino el método en la implementación de mi clase. Por supuesto, debe definirse antes (arriba) de cualquier método que lo llame, pero definitivamente se necesita la menor cantidad de trabajo por hacer.
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 está saturando 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 visto mencionada 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 reconocerá esto y lo incluirá en la lista de "Contrapartes" en el Editor Asistente.
//MyClass.m
#import "MyClass.h"
#import "MyClass_private.h"
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.
No hay, como otros ya han dicho, algo como un método privado en Objective-C. Sin embargo, al comenzar en Objective-C 2.0 (es decir, Mac OS X Leopard, iPhone OS 2.0 y versiones posteriores) puede crear una categoría con un nombre vacío (es decir, @interface MyClass ()
) llamada Class Extension . Lo que es único acerca de una extensión de clase es que las implementaciones del método deben ir en la misma @implementation MyClass
implementación que los métodos públicos. Entonces estructuro mis clases así:
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 le permite agrupar las implementaciones de sus métodos por funcionalidad, no por la distinción público / privada (a veces arbitraria).