¿El contenido de NSData por separado count de reference?

Tengo una gran región malloc'd que quiero envolver en un object NSData. Algún time después, hago una copy de ese object NSData. Quiero que los dos objects NSData vivan vidas independientes. ARC se encarga de volver a contar los objects NSData ellos mismos, pero estoy tratando de aclarar la duración de la región malloc'd contenida . Aquí hay un boceto de código:

float* cubeData = (float*)malloc(cubeDataSize); printf("cubeData=%p\n", cubeData); // cubeData=0x01beef00 for (...) { /* fill the cubeData array */ } NSData* data = [NSData dataWithBytesNoCopy:cubeData length:cubeDataSize freeWhenDone:YES]; NSData* data2 = [data copyWithZone:nil] printf("data.bytes=%p data2.bytes=%p\n", data.bytes, data2.bytes); // data.bytes=0x01beef00 data2.bytes=0x01beef00 

Está bien conmigo que copyWithZone no copy en profundidad la región malloc'd, puedo usar [NSData dataWithData:] si quiero una copy profunda. Lo que no está claro para mí (y no estoy seguro de cuál es la mejor manera de probar) es qué object NSData posee el búfer malloc'd subyacente. Si ambos tienen una reference al búfer malloc'd (usando alguna forma de recuento de reference opaco) ¡eso es genial! Pero si el búfer malloc se libera cuando se libera el object de data (como lo implica freeWhenDone:YES ), data2 tendrá problemas en sus manos.

¿Alguien puede explicar qué hace NSData en este caso? Alternativamente, ¿alguien puede sugerir una testing definitiva para probarme lo que está sucediendo?

A la pregunta subyacente:

¿El contenido de NSData por separado count de reference?

No. (Pero mirando su código, no debería importar. Vea a continuación después de esta diversión.)

— Inicio de desvío —

ARC administra las retenciones y liberaciones en objects Objective-C enviando el equivalente de retain y release posts en los momentos apropiados. Los "times apropiados" se determinan en el momento de la compilation mediante la inspección del código. Eso es exactamente todo lo que hace. Cuando comienza a crear pointers a objects que no son objects de esos objects (es decir, bytes ), usted está solo para administrar la vida.

@CouchDeveloper proporciona buena información sobre objc_precise_lifetime . Colocar este atributo en los objects de datos puede protegerlo de las optimizaciones de ARC cuando se trata de pointers internos, pero no es realmente relevante aquí. El punto de objc_precise_lifetime es decirle a ARC que no está permitido lanzar un object antes de que la variable de reference quede fuera de scope. El problema que resuelve se ve así:

 NSData *data = ...; void *stuff = data.bytes; // (1) doSomething(stuff); // (2) 

ARC tiene una optimization que dice que está permitido destruir data entre la línea (1) y la línea (2), ya que nunca volverá a hacer reference a los data , aunque los data estén en el scope. Al agregar el atributo objc_precise_lifetime prohíbe esa optimization. Cuando comienza a usar mucho NSData , este atributo puede volverse importante.

— fin de desvío —

Está bien, pero ¿qué pasa con tu situación?

 float* cubeData = (float*)malloc(cubeDataSize); NSData* data = [NSData dataWithBytesNoCopy:cubeData length:cubeDataSize freeWhenDone:YES]; NSData* data2 = [data copyWithZone:nil] 

Una vez ejecutado este código, hay dos posibilidades (y se supone que no te importa la mayor parte del time, ya que son objects inmutables):

  • data y data2 son pointers fuertes para el mismo object NSData . Ese object posee la memory malloced y lo liberará cuando se desasigne. (Este es el que es casi seguro que sucederá en este caso particular, pero ese es un detalle de implementación).
  • data apuntan a un object NSData que posee la memory malloced y lo liberará cuando se desasigne. data2 apunta a un object NSData diferente con su propia memory (que liberará cuando se desasigne).

(Hay otras opciones, tal vez NSData utiliza un dispatch_data subyacente o un esquema copy-on-write. Pero todas las opciones deberían verse como las anteriores desde el exterior).

En el primer caso, si los data se salen del ámbito, pero data2 todavía está presente, entonces se conserva el NSData propietario. No hay problema. En el segundo caso, cuando los data data2 fuera del scope, destruye su memory, pero data2 tiene una copy independiente de la misma, por lo que nuevamente no hay problema.

Creo que su confusión proviene de pensar que los data propiedad de la memory. No lo hace El object NSData que apunta a los data posee la memory. data y los data2 son solo indicadores.