¿Por qué Objective-C no admite métodos privados?

123

He visto una serie de estrategias para declarar métodos semiprivados en Objective-C , pero no parece haber una manera de hacer un método verdaderamente privado. Yo acepto que. ¿Pero por qué es esto así? Cada explicación que he dicho esencialmente es: "no puedes hacerlo, pero aquí hay una aproximación cercana".

Hay una serie de palabras clave se aplica a ivars(miembros) que controlan su ámbito de aplicación, por ejemplo @private, @public, @protected. ¿Por qué no se puede hacer esto también por métodos? Parece algo que el tiempo de ejecución debería ser capaz de soportar. ¿Hay una filosofía subyacente que me falta? ¿Es esto deliberado?

Rob Jones
fuente
15
Buena pregunta, por cierto.
bbum
55
Para sus ediciones: sí, la ejecución en tiempo de ejecución sería muy costosa para muy poco beneficio. La aplicación en tiempo de compilación sería una adición bienvenida al lenguaje y es bastante factible. Sí, podría evitarse en tiempo de ejecución, pero está bien. El programador no es un enemigo. El objetivo del alcance es ayudar a proteger al programador del error.
Rob Napier el
1
No puede "omitir el tiempo de ejecución": el tiempo de ejecución es lo que envía el mensaje.
Chuck
"No habría forma de que un método privado cortocircuite esta verificación" ¿Quiso decir "eludir"? ;-)
Constantino Tsarouhas

Respuestas:

103

La respuesta es ... bueno ... simple. Simplicidad y consistencia, de hecho.

Objective-C es puramente dinámico en el momento del envío del método. En particular, cada envío de método pasa por el mismo punto de resolución de método dinámico que cualquier otro envío de método. En tiempo de ejecución, cada implementación de método tiene exactamente la misma exposición y todas las API proporcionadas por el tiempo de ejecución de Objective-C que funcionan con métodos y selectores funcionan de la misma manera en todos los métodos.

Como muchos han respondido (tanto aquí como en otras preguntas), se admiten métodos privados en tiempo de compilación; si una clase no declara un método en su interfaz disponible públicamente, entonces ese método podría no existir en lo que respecta a su código. En otras palabras, puede lograr todas las diversas combinaciones de visibilidad deseadas en el momento de la compilación organizando su proyecto adecuadamente.

Hay pocos beneficios al duplicar la misma funcionalidad en el tiempo de ejecución. Agregaría una enorme cantidad de complejidad y sobrecarga. E incluso con toda esa complejidad, todavía no evitaría que todos, excepto el desarrollador más informal, ejecuten sus métodos supuestamente "privados".

EDITAR: Una de las suposiciones que he notado es que los mensajes privados tendrían que pasar por el tiempo de ejecución, lo que generaría una sobrecarga potencialmente grande. ¿Es esto absolutamente cierto?

Sí lo es. No hay razón para suponer que el implementador de una clase no quiera usar todo el conjunto de características de Objective-C en la implementación, y eso significa que debe ocurrir un despacho dinámico. Sin embargo , no hay una razón particular por la cual los métodos privados no puedan ser enviados por una variante especial de objc_msgSend(), ya que el compilador sabría que son privados; es decir, esto podría lograrse agregando una tabla de método solo privado a la Classestructura.

¿No habría forma de que un método privado haga un cortocircuito en esta verificación o se salte el tiempo de ejecución?

No podía omitir el tiempo de ejecución, pero el tiempo de ejecución no necesariamente tendría que hacer ninguna comprobación de métodos privados.

Dicho esto, no hay razón para que un tercero no pueda invocar deliberadamente objc_msgSendPrivate()un objeto, fuera de la implementación de ese objeto, y algunas cosas (KVO, por ejemplo) tendrían que hacer eso. En efecto, sería una convención y un poco mejor en la práctica que prefijar los selectores de métodos privados o no mencionarlos en el encabezado de la interfaz.

Sin embargo, hacerlo socavaría la naturaleza puramente dinámica del lenguaje. Ya no todos los envíos de métodos pasarían por un mecanismo de envío idéntico. En cambio, quedaría en una situación en la que la mayoría de los métodos se comportan de una manera y un pequeño puñado son simplemente diferentes.

Esto se extiende más allá del tiempo de ejecución ya que hay muchos mecanismos en Cocoa construidos sobre el dinamismo constante de Objective-C. Por ejemplo, tanto la codificación del valor clave como la observación del valor clave tendrían que modificarse en gran medida para admitir métodos privados, muy probablemente creando una escapatoria explotable, o los métodos privados serían incompatibles.

bbum
fuente
49
+1 Objective-C es (de alguna manera) un lenguaje de "niño grande" en el que se espera que los programadores exhiban una cierta cantidad de disciplina, en lugar de ser golpeados por el compilador / tiempo de ejecución en todo momento. Lo encuentro refrescante. Para mí, restringir la visibilidad del método durante la compilación / vinculación es suficiente y preferible.
Quinn Taylor el
11
Más python siendo "obj-c-ic" :). Guido fue bastante proactivo en el mantenimiento de Python en sistemas NeXT, incluida la creación de la primera versión de PyObjC. Por lo tanto, ObjC influyó un poco en Python .
bbum
3
+1 por la interesante historia histórica del dictador benevolente para la vida y sus cruces con el mítico sistema NeXT.
pokstad
55
Gracias. Python, Java y Objective-C tienen modelos de objetos en tiempo de ejecución que son bastante similares [a SmallTalk]. Java se deriva directamente de Objective-C. Python realizó un curso paralelo más de uno de inspiración, pero Guido ciertamente estudió NeXTSTEP de cerca.
bbum
1
Excepto que no confío en su licencia y definitivamente preferiría el sonido metálico a gcc. Pero es un buen primer paso seguro.
Cthutu
18

El tiempo de ejecución podría soportarlo, pero el costo sería enorme. Debería comprobarse si cada selector que se envía es privado o público para esa clase, o cada clase necesitaría administrar dos tablas de despacho separadas. Esto no es lo mismo para las variables de ejemplo porque este nivel de protección se realiza en tiempo de compilación.

Además, el tiempo de ejecución necesitaría verificar que el remitente de un mensaje privado sea de la misma clase que el receptor. También puede omitir los métodos privados; Si la clase se utiliza instanceMethodForSelector:, podría devolver IMPa cualquier otra clase para que invoquen el método privado directamente.

Los métodos privados no pudieron omitir el envío de mensajes. Considere el siguiente escenario:

  1. Una clase AllPublictiene un método de instancia públicadoSomething

  2. Otra clase HasPrivatetiene un método de instancia privada también llamadodoSomething

  3. Crea una matriz que contiene cualquier número de instancias de ambos AllPublicyHasPrivate

  4. Tienes el siguiente bucle:

    for (id anObject in myArray)
        [anObject doSomething];

    Si ejecutó ese ciclo desde adentro AllPublic, el tiempo de ejecución tendría que detener el envío doSomethingde las HasPrivateinstancias, sin embargo, este ciclo sería útil si estuviera dentro de la HasPrivateclase.

dreamlax
fuente
1
Estoy de acuerdo en que habría un costo involucrado. Quizás no sea algo que desee en un dispositivo portátil. ¿Puedes apoyar el enorme calificador de lo contrario? ¿Importaría realmente el costo en un sistema de escritorio?
Rob Jones
1
No lo he hecho, pero puede que tengas razón. Objective-C tiene un despacho de mensajes en tiempo de ejecución frente a la llamada al método de tiempo de compilación de C ++, por lo que estaría hablando de una verificación de tiempo de compilación frente a una verificación de tiempo de ejecución.
Remus Rusanu
Parece realmente extraño que la razón principal para no admitir métodos privados se deba al rendimiento. Creo que Apple simplemente no creía que los métodos privados fueran muy necesarios, independientemente de si hubo un impacto en el rendimiento. De lo contrario, deberían haber elegido un idioma diferente para su empresa multimillonaria.
pokstad
1
Agregar métodos privados no solo complicaría la implementación del tiempo de ejecución, sino que complicaría la semántica del lenguaje. El control de acceso es un concepto simple en lenguajes estáticos, pero es una mala combinación para el despacho dinámico, que conllevaría una gran sobrecarga conceptual y conduciría a muchos, muchos "¿por qué esto no funciona?" preguntas
Jens Ayton el
77
¿Qué te comprarían realmente los métodos privados? Objective-C es, después de todo, un lenguaje basado en C. Por lo tanto, si alguien tiene código ejecutándose en su proceso, puede fácilmente ponerlo en práctica independientemente de cuán privado u ofuscado pueda ser cualquier API.
bbum
13

Las respuestas publicadas hasta ahora hacen un buen trabajo al responder la pregunta desde una perspectiva filosófica, por lo que voy a plantear una razón más pragmática: ¿qué se ganaría al cambiar la semántica del lenguaje? Es lo suficientemente simple como para "ocultar" efectivamente métodos privados. A modo de ejemplo, imagine que tiene una clase declarada en un archivo de encabezado, así:

@interface MyObject : NSObject {}
- (void) doSomething;
@end

Si necesita métodos "privados", también puede poner esto en el archivo de implementación:

@interface MyObject (Private)
- (void) doSomeHelperThing;
@end

@implementation MyObject

- (void) doSomething
{
    // Do some stuff
    [self doSomeHelperThing];
    // Do some other stuff;
}

- (void) doSomeHelperThing
{
    // Do some helper stuff
}

@end

Claro, no es exactamente lo mismo que los métodos privados de C ++ / Java, pero es lo suficientemente cerca, así que ¿por qué alterar la semántica del lenguaje, así como el compilador, el tiempo de ejecución, etc., para agregar una característica que ya está emulada de manera aceptable? ¿camino? Como se señaló en otras respuestas, la semántica de paso de mensajes, y su dependencia de la reflexión en tiempo de ejecución, haría que el manejo de mensajes "privados" no sea trivial.

mipadi
fuente
1
El problema con este enfoque es que alguien puede anular los métodos privados (incluso sin saberlo) cuando subclasifican su clase. Supongo que, esencialmente, debe elegir nombres que es poco probable que se anulen.
dreamlax
3
Lo cual me parece un truco encima de un truco.
Rob Jones
1
@ Mike: Apple ha declarado que los nombres de métodos con guiones bajos están reservados explícitamente para Apple únicamente: developer.apple.com/mac/library/documentation/Cocoa/Conceptual/… . La alternativa recomendada es el prefijo con dos letras mayúsculas y luego un guión bajo.
dreamlax
8
Además, incluya su SSN después del método para que otros programadores sepan quién lo escribió. = D Espacios de nombres, los necesita !!!
pokstad
2
Si usted está preocupado acerca de los métodos que definen el ser reemplazado, no se ha adoptado adecuadamente los niveles inherentes de la verbosidad que Objective-C anima ...
Kendall Helmstetter Gelner
7

La solución más fácil es declarar algunas funciones C estáticas en sus clases Objective-C. Estos solo tienen un alcance de archivo según las reglas C para la palabra clave estática y por eso solo pueden ser utilizados por métodos de esa clase.

Sin problemas en absoluto.

Cromulento
fuente
¿Deben definirse fuera de la sección @implementation?
Rob Jones
@Rob: no, ellos pueden (y probablemente deberían ser) definidos dentro de la implementación de sus clases.
Cromulento
6

Sí, se puede hacer sin afectar el tiempo de ejecución utilizando una técnica ya empleada por el (los) compilador (es) para manejar C ++: cambio de nombre.

No se ha hecho porque no se ha establecido que resolvería una dificultad considerable en el espacio del problema de codificación que otras técnicas (p. Ej., Prefijos o subrayados) son capaces de sortear suficientemente. IOW, necesitas más dolor para superar los hábitos arraigados.

Puede contribuir parches a clang o gcc que agreguen métodos privados a la sintaxis y generen nombres mutilados que solo él reconoció durante la compilación (y rápidamente olvidó). Luego, otros miembros de la comunidad de Objective-C podrían determinar si realmente valió la pena o no. Es probable que sea más rápido de esa manera que tratar de convencer a los desarrolladores.

Huperniketes
fuente
2
La selección de nombres en tiempo de compilación es mucho más difícil de lo que uno podría suponer, ya que tiene que modificar todos los selectores de métodos, incluidos los que pueden aparecer en los archivos XIB y los que pueden pasarse a cosas como NSSelectorFromString (), así como KeyValueCoding y amigos ...
bbum
1
Sí, estaría más involucrado si quisiera brindar soporte para métodos privados en toda la cadena de herramientas. La tabla de símbolos del compilador necesitaría ser almacenada y accesible a través de alguna API de cadena de herramientas. No sería la primera vez que Apple tendría que desplegar gradualmente capacidades para desarrolladores que fueran lo suficientemente importantes como lo permitiera el tiempo y la estabilidad. Sin embargo, en lo que respecta a los métodos privados: es cuestionable si es una victoria suficiente para emprender el esfuerzo. Es aún más sospechoso que estos otros mecanismos deberían poder acceder a ellos fuera de la clase.
Huperniketes
1
¿Cómo podría una aplicación de tiempo de compilación solo + cambio de nombre evitar que alguien recorra la lista de métodos de una clase y la encuentre allí?
dreamlax
No lo haría Mi respuesta solo aborda la pregunta planteada por el OP.
Huperniketes
He pensado en intentar agregar métodos privados a Clang, una razón por la que pensé que los métodos privados serían buenos es que podrían llamarse directamente como funciones simples de C y luego podría tener todas las optimizaciones de funciones de C habituales. Nunca me he molestado por el tiempo y ya puedes hacerlo simplemente usando c.
Nathan Day
4

Esencialmente, tiene que ver con la forma de paso de mensajes de Objective-C de llamadas a métodos. Cualquier mensaje puede enviarse a cualquier objeto, y el objeto elige cómo responder al mensaje. Normalmente responderá ejecutando el método nombrado después del mensaje, pero también podría responder de varias otras maneras. Esto no hace que los métodos privados sean completamente imposibles, Ruby lo hace con un sistema similar de transmisión de mensajes, pero los hace algo incómodos.

Incluso la implementación de métodos privados por parte de Ruby es un poco confusa para las personas debido a lo extraño (¡puede enviarle al objeto cualquier mensaje que desee, excepto los que están en esta lista !). Esencialmente, Ruby hace que funcione al prohibir que se llame a métodos privados con un receptor explícito. En Objective-C requeriría aún más trabajo ya que Objective-C no tiene esa opción.

Arrojar
fuente
1

Es un problema con el entorno de tiempo de ejecución de Objective-C. Si bien C / C ++ se compila en un código de máquina ilegible, Objective-C aún mantiene algunos atributos legibles por humanos, como los nombres de métodos como cadenas . Esto le da a Objective-C la capacidad de realizar características reflexivas .

EDITAR: Ser un lenguaje reflexivo sin métodos privados estrictos hace que Objective-C sea más "pitónico" en el sentido de que confía en otras personas que usan su código en lugar de restringir los métodos que pueden llamar. El uso de convenciones de nomenclatura como guiones bajos dobles tiene el objetivo de ocultar su código a un codificador de cliente informal, pero no detendrá a los codificadores que necesiten hacer un trabajo más serio.

Pokstad
fuente
Y si los consumidores de tu biblioteca pudieran reflejar tus métodos privados, ¿no podrían llamarlos también?
Jared Updike
Si saben dónde buscar, ¿no es así como la gente ha estado usando bibliotecas indocumentadas en el iPhone? El problema con el iPhone es que corre el riesgo de que su aplicación no sea aceptada por usar una API privada.
pokstad
Si acceden a métodos privados a través de la reflexión, obtienen lo que se merecen. Cualquier error no es tu culpa.
gnasher729
1

Hay dos respuestas dependiendo de la interpretación de la pregunta.

La primera es ocultar la implementación del método de la interfaz. Esto se usa, típicamente con una categoría sin nombre (por ejemplo @interface Foo()). Esto permite que el objeto envíe esos mensajes pero no otros, aunque uno podría anular accidentalmente (o de otro modo).

La segunda respuesta, en el supuesto de que se trata de rendimiento e inline, es posible, pero como una función C local. Si quieres un 'foo privada ( NSString *arg)' método, que haría void MyClass_foo(MyClass *self, NSString *arg)y lo llaman como una función de C como MyClass_foo(self,arg). La sintaxis es diferente, pero actúa con el tipo de características de rendimiento sensatas de los métodos privados de C ++.

Aunque esto responde a la pregunta, debo señalar que la categoría sin nombre es, con mucho, la forma más común de hacer esto.

AlBlue
fuente
0

Objective-C no admite métodos privados porque no los necesita.

En C ++, cada método debe ser visible en la declaración de la clase. No puede tener métodos que alguien que incluya el archivo de encabezado no pueda ver. Entonces, si desea métodos que el código fuera de su implementación no debería usar, no tiene otra opción, el compilador debe darle alguna herramienta para que pueda decirle que el método no debe usarse, esa es la palabra clave "privada".

En Objective-C, puede tener métodos que no están en el archivo de encabezado. Entonces logra el mismo propósito muy fácilmente al no agregar el método al archivo de encabezado. No hay necesidad de métodos privados. Objective-C también tiene la ventaja de que no necesita recompilar a cada usuario de una clase porque cambió los métodos privados.

Por ejemplo, las variables que solía declarar en el archivo de encabezado (ya no están disponibles), @private, @public y @protected están disponibles.

gnasher729
fuente
0

Una respuesta que falta aquí es: porque los métodos privados son una mala idea desde el punto de vista de la evolución. Puede parecer una buena idea hacer que un método sea privado al escribirlo, pero es una forma de enlace temprano. El contexto puede cambiar, y un usuario posterior puede querer usar una implementación diferente. Un poco provocativo: "Los desarrolladores ágiles no usan métodos privados"

En cierto modo, al igual que Smalltalk, Objective-C es para programadores adultos. Valoramos saber lo que el desarrollador original asumió que debería ser la interfaz, y asumimos la responsabilidad de lidiar con las consecuencias si necesitamos cambiar la implementación. Entonces sí, es filosofía, no implementación.

Stephan Eggermont
fuente
He votado a favor porque no merece el voto negativo. Hay, como dice Stephan, un argumento de que los métodos "privados" no son necesarios, y es similar a los argumentos sobre la escritura dinámica frente a la estática, ya que, cualquiera que sea su conclusión preferida, ambas partes tienen un punto.
alastair