Agregar NSObjects a NSMutableArray

Esta reciente discusión SO me ha confundido. El prototipo addObject: para addObject: es

 - (void)addObject:(id)anObject 

y id se define en objc.h como

 typedef struct objc_class *Class; typedef struct objc_object { Class isa; } *id; 

Cuando agrego un NSObject o subclass a un NSMutableArray , su recuento de retención se incrementa y, cuando lo NSMutableArray un NSMutableArray , se networkinguce. ¿Esto significa que si se agrega un id type que no es un NSObject o subclass a un NSMutableArray , tiene que responder para retener y liberar posts? La definición de id no parece forzar esto. ¿Es una directiva C objetiva que cualquier id type debe responder a los posts de gestión de memory estándar?

La dura verdad sobre la mayoría de los contenedores de Foundation (y en la medida de la mayoría de las classs desarrolladas por Apple, y en la medida también de la mayoría de las classs desarrolladas por terceros) es que cuando un método acepta el tipo de id , realmente debería leer id<NSObject> , lo que significa tipo que responde al protocolo NSObject . Las instancias de classs que no son parte de la jerarquía de NSObject probablemente no respondan a -retain and -retain , lo cual es especialmente inconveniente cuando se trata de agregarlas a un contenedor. También es poco probable que respondan a -hash , -isEqual: , -description , -copy , y a todos los demás methods que los contenedores Foundation pueden usar en sus contenidos por cualquier motivo.

Por ejemplo, si intenta agregar objects Class a un contenedor Foundation (que no sea NSMapTable ya que este fue diseñado con mucha flexibilidad en mente), chocará contra un muro porque se espera que las classs ObjC "modernas" henetworkingen de NSObject , o al less implementar el protocolo NSObject .

Esta es una situación bastante rara, sin embargo. Class es prácticamente la única class útil que no henetworkinga de NSObject .

¿Esto significa que si se agrega un tipo de ID que no es un NSObject o subclass a un NSMutableArray, tiene que responder para retener y liberar posts?

De forma pnetworkingeterminada, Sí, aunque hay soluciones a esto mediante CF-API.

La definición de id no parece forzar esto. ¿Es una directiva C objetiva que cualquier tipo de id debe responder a los posts de gestión de memory estándar?

Es como se escribieron las bibliotecas; Las classs de raíz (no henetworkingan de NSObject) son muy inusuales. Una alternativa podría ser - (void)addObject:(id<NSObject>) , pero eso requeriría una extensión bastante grande para su class raíz … quizás una solución mejor hubiera sido un protocolo NSReferenceCounted que toma los bits relevantes de NSObject .

Sin embargo, los types de collections de NS realmente suponen que están tratando con NSObjects (por ejemplo, los dictionarys usan hash y description ).

No, no hay convención de que objects de tipo "id" respondan para retener / liberar posts; de hecho, uno podría decir que garantizar la existencia de esos types de methods es el propósito del protocolo NSObject (no la class). Sin embargo, "id" le dice al comstackdor "no se moleste en comprobar el tipo", así que cuando agrega un object a un nsarray que no implementa esos methods, comstackrá, pero obtendrá un error de time de ejecución. Consulte http://unixjunkie.blogspot.com/2008/03/id-vs-nsobject-vs-id.html para get una explicación más detallada.

No debe poner types de objects en un NSArray , por ejemplo, si desea almacenar una matriz de CGRects , los envuelve en objects de NSValue y los almacena (esto es lo mismo para CGPoint, CGSize etc.).

Si tiene una estructura personalizada, necesitará un NSObject descendente de NSObject , ¡qué tipo de derrota el propósito en primer lugar!

Si realmente necesita una matriz que contiene elementos que no desea retener / liberar, puede crearla utilizando CFArrayCreateMutable y pasarle las devoluciones de llamada adecuadas. ( CFMutableArray es un puente sin NSMutableArray a NSMutableArray ).

Además de las discusiones técnicas anteriores, creo que la respuesta está relacionada con la historia del lenguaje y su preference por las convenciones sobre la syntax. Los protocolos formales no eran originalmente parte del lenguaje, todo era informal. Posteriormente, Apple se ha desplazado principalmente a los protocolos formales, pero los protocolos informales forman parte del lenguaje y son utilizados por partes de la API oficial. Por lo tanto, son una parte de Objective-C totalmente compatible y de primera class.

Si miras la documentation a NSArray , dice, entre otras cosas:

En la mayoría de los casos, su class NSArray personalizada debe cumplir con las convenciones de propiedad de object de Cocoa. Por lo tanto, debe enviar retener a cada object que agregue a su colección y liberar a cada object que elimine de la colección. Por supuesto, si la razón para subclasificar NSArray es implementar un comportamiento de retención de objects diferente de la norma (por ejemplo, una matriz que no retiene), entonces puede ignorar este requisito.

Y:

NSArray es "puente libre" con su homólogo de la Base Core, CFArray, […] a veces puede hacer cosas con CFArray que no puede hacer fácilmente con NSArray. Por ejemplo, CFArray proporciona un set de devoluciones de llamada, algunas de las cuales son para implementar un comportamiento personalizado de liberación retenida. Si especifica implementaciones NULL para estas devoluciones de llamada, puede get fácilmente una matriz sin retención.

Por lo tanto, su pregunta se basa en una premisa falsa, específicamente una lectura parcial de la documentation o una consideración incompleta del idioma.

id <NSObject> no se utiliza correctamente porque el protocolo NSObject no coincide con el protocolo que los objects deben implementar para poder utilizarlo con NSArray . En su lugar, un protocolo informal, que pasa a ser un subset de NSObject , se establece en la documentation (aunque con una segunda eliminación de la informalidad, lo que es raro). Aunque los protocolos informales son cada vez más infrecuentes, son válidos y aún no excepcionales.

Entonces, la versión corta de mi respuesta es: NSArray documenta un protocolo informal, ese protocolo no es lo mismo que NSObject . Los protocolos informales no están representados en la syntax, por lo tanto, id .