objective c - ¿Por qué NSRect, NSPoint, etc. estructura, no las clases?
objective-c class (9)
Hace poco tuve que crear mi propio tipo similar a NSRect que tiene un punto de anclaje (esencialmente un NSRect con otro NSPoint).
Después de algunas investigaciones , descubrí que, en realidad, probablemente sería mejor hacer de esto una clase (como en la subclase NSObject), en lugar de usar struct. ¿Por qué entonces Apple ha hecho que estos tipos sean estructuras, no clases? Parece que tendría muchos beneficios, como no tener que envolverlos en valores NSV, no tener que usar funciones C para interactuar con ellos, etc.
Estoy seguro de que tendrían una razón. ¿Es simplemente el uso de memoria ligeramente menor? ¿Es solo histórico? ¿O me he perdido algo más grande?
Cada paradigma de programación tiene sus propios pros y contras, y programación orientada a objetos (OOP) en no una excepción.
Todos los principales beneficios de tener clases (es decir, escribir en un estilo orientado a objetos) son la encapsulación , el polimorfismo y la herencia .
Si declaramos unidades simples como Rect o Point una clase, no usaremos nada de esa lista, pero presentaremos todas las desventajas que tiene OOP, incluyendo mutabilidad implícita, administración de memoria más compleja, referencias de punteros, actores implícitos, encapsulamiento roto (con setters / getters), etc. Y no enumero aquí cientos de anti-patrones que sí existen en OOP.
Dudo que la elección de las estructuras para Rect y Point in Cocoa fuera justificada por C legacy, primero porque Objective-C apoyó las clases desde el principio, y segundo porque el nuevo lenguaje Swift no tiene este legado, pero casi todas los tipos estándar y los contenedores se declaran en su biblioteca estándar como estructuras, no como clases.
En general, si no necesita encapsulación, polimorfismo y herencia, debería considerar declarar su nueva entidad como una estructura y evitar OOP siempre que sea posible.
Creo que tiene que ver con el hecho de que cada estructura contiene tipos primitivos de datos (en general flotantes y dobles) sin ninguna acción propia (como funciones o métodos), por lo que no era necesario algo más complejo como una clase con todas las funciones (sin importaciones y sin un código repetitivo, por ejemplo, inicializadores) y, por supuesto, como otros colegas mencionaron, porque Objective-C es un superconjunto de C.
En general, una clase se utiliza cuando hay métodos que operan en los datos. Si solo tiene datos, simplemente use una estructura. Parece que no hay ningún método útil para seguir el punto o rect, así que no hay necesidad de una clase.
Porque necesitan ser tipos de valores. El punto o el rectángulo no son objetos en realidad. Son solo un montón de datos. No se comportan , solo almacenan los datos que les pasas. Puedes escribir
CGRect frame = ...
UIView *view1 = [[UIView alloc] initWithFrame:frame];
frame.origin = ...
UIView *view2 = [[UIView alloc] initWithFrame:frame];
Y funciona bien Con los tipos de referencia, qué clases son, modificará el marco de view1
mientras mueve el marco a otro origen.
[myRect intersectsWithRect: otherRect]
definitivamente estaría bien y en realidad Swift permite hacerlo. Los tipos de valores como estructuras y enumeraciones pueden incluir métodos en Swift, lo que hace posible
Si has estado jugando con Objective-C por un tiempo, sabrás que es una capa OOP encima de coreFoundation, que es todo estructuras y ''tipos opacos'' (estructuras con secretos).
Por ejemplo, NSDictionary es realmente un envoltorio alrededor del CFtype, CFDictionaryRef. Bueno, las cosas que está preguntando aquí, tamaño, punto, rect, edgeInsets, etc. son solo pequeños tipos de datos que se determinaron para no garantizar la actualización. Los primitivos (int, double, bool, etc.) tampoco obtuvieron una clase (pública) para peaje gratis. Obtuvieron el clúster NSNumber, y todos estos tipos se agruparon en el contenedor NSValue. Puede escribir una clase concreta para envolver una específica si necesita ...
También un buen punto es la velocidad de operación. Estos puntos y rects se utilizan principalmente para representar algo y, por lo tanto, necesitan un funcionamiento más rápido que nunca. En lugar de asignar un objeto, que desencadenará init
recursivo, gastar muchos bytes y ciclos de CPU para el trabajo encubierto, es más rápido decir "aquí tengo 16 bytes, que son 2 CGFloats de estructura NSPoint". Esto también genera mucha confianza cuando se delega un trabajo en una GPU, que no sabe nada sobre OOP y métodos de sus objetos, sino que necesita dibujar mucho en la pantalla sin problemas técnicos.
Usa estructuras cuando quiere pasar cosas por valor y clases cuando quiere pasarlas por referencia.
¿Por qué entonces Apple ha hecho que estos tipos sean estructuras, no clases?
Por motivos de rendimiento, ya que estas entidades se utilizan con frecuencia, se procesan con frecuencia y son pequeñas. Y la ventaja de tenerlos encapsulados en objetos no se justifica por las ventajas de que sean objetos.
La razón primordial es el rendimiento: todas las API de rendimiento crítico de Cocoa (gráficos, audio) tienden a usar estructuras C y funciones para este tipo de propósitos.
Las razones son que el compilador C puede optimizarlas mucho más fácilmente. Las funciones C se pueden llamar de forma más económica que los métodos, y el compilador las puede insertar automáticamente, eliminando por completo la tara de llamadas de función. Las estructuras C se asignan en la pila en lugar del montón, lo que significa que el procesamiento se puede realizar principalmente en registros o cachés de CPU de bajo nivel en lugar de llamar a la memoria principal, que es cientos de veces más lenta.
Los métodos de cacao se envían dinámicamente. Pueden ser anulados por una subclase o swizzling, por lo que el compilador no puede alinearlos porque no puede estar seguro de lo que harán hasta que se ejecuten en tiempo de ejecución. Las clases de cacao se asignan en el montón, por lo que deben asignarse / desasignarse, y existe toda la otra sobrecarga, como el recuento de referencias. También es probable que terminen diseminados en la memoria, lo que significa que puede haber pérdidas de rendimiento significativas debido a los bloques de memoria que residen al tener que entrar y salir de la memoria caché.