2010-01-26 23 views
14

Tengo una función que toma algunos datos de mapa de bits y devuelve un UIImage * de él. Parece algo así:¿Cuál es el patrón de administración de memoria correcto para buffer-> CGImageRef-> UIImage?

UIImage * makeAnImage() 
{ 
    unsigned char * pixels = malloc(...); 
    // ... 
    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, pixels, pixelBufferSize, NULL); 
    CGImageRef imageRef = CGImageCreate(..., provider, ...); 
    UIImage * image = [[UIImage alloc] initWithCGImage:imageRef]; 
    return [image autorelease]; 
} 

¿Alguien puede explicar exactamente quién posee qué memoria aquí? Quiero limpiarlo correctamente, pero no estoy seguro de cómo hacerlo de manera segura. Los documentos son borrosos en esto. Si yo free píxeles al final de esta función después de crear el UIImage, y luego uso el UIImage, me cuelgo. Si lanzo el proveedor o el imageRef después de crear el UIImage, no veo un bloqueo, pero aparentemente están pasando los píxeles por completo, así que estoy asustado por la liberación de estos estados intermedios.

(Sé por CF documentos que debo llamar a la versión de estos últimos porque provienen de las funciones Crear, pero ¿puedo hacerlo antes de utilizar el UIImage?) Presumiblemente puedo usar la devolución de llamada dealloc del proveedor para limpiar el buffer de píxeles, pero ¿qué más?

Gracias!

Respuesta

21

La regla de pulgar aquí es "-release * si no la necesita".

Debido a que ya no es necesario provider y imageRef después, usted debe -release todos ellos, es decir

UIImage * image = [[UIImage alloc] initWithCGImage:imageRef]; 
CGDataProviderRelease(provider); 
CGImageRelease(imageRef); 
return [image autorelease]; 

pixel no está gestionada por ref-cuenta, por lo que necesita para contar la API CG para liberarlos de cuando sea necesario Haga lo siguiente:

void releasePixels(void *info, const void *data, size_t size) { 
    free((void*)data); 
} 
.... 

CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, pixels, pixelBufferSize, releasePixels); 

Por cierto, se puede utilizar en lugar de +imageWithCGImage:[[[* alloc] initWithCGImage:] autorelease]. Aún mejor, hay +imageWithData: por lo que no necesita meterse con las cosas CG y malloc.

(*:. Excepto cuando el retainCount ya está supuestamente cero desde el principio)

+0

Gracias Kenny. Esa es una descripción muy sucinta; Creo que me sorprendió un poco la imprevisibilidad del almacenamiento intermedio de pila en bruto, pero como siempre, confía en las reglas y serás recompensado. Aclamaciones. –

+1

Solo para agregar algunas aclaraciones, las palabras clave en las funciones principales son "Crear" y "Nuevo". Si la función contiene cualquiera de estas palabras, debe liberar la memoria devuelta. La mayoría de los tipos de datos principales son compatibles con CFType. Lo que significa que puede usar el Objective-C para retener/liberar/liberar automáticamente las llamadas si es más fácil. es decir, el lanzamiento de [(id) imageRef]; o CFRelease (imageRef); –

+0

Recuerde comprobar si 'imageRef' es' NULL' si usa 'CFRelease'. – kennytm

-2

Sí, este código me causa náuseas. Como regla antigua para vivir, trato de no mezclar y combinar C y C++, y C/Objective-C en la misma función/método/selector.

¿Qué tal dividir esto en dos métodos. Haga que cambie makeAnImage en makeAnImageRef y despliegue la creación UIImage en otro selector Obj-C.

+0

Aunque entiendo la reacción :) hacerlo sigue sin ayudar a mi problema de administración de memoria. Todavía tengo el problema de pasar bytes sin formato al inicio de la canalización me deja con un UIImage que de alguna manera todavía necesita esos bytes. Entonces, este código está más relacionado de lo que parece. –

8
unsigned char * pixels = malloc(...); 

Usted es propietario del búfer pixels porque mallocked ella.

CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, pixels, pixelBufferSize, NULL); 

núcleo gráfico sigue las reglas de la Fundación Core. Usted es el propietario del proveedor de datos porque Created.

No proporcionó una devolución de llamada de liberación, por lo que todavía posee el búfer pixels. Si hubiera proporcionado una devolución de llamada de liberación, el objeto CGDataProvider tomaría posesión del búfer aquí. (Generalmente es una buena idea.)

CGImageRef imageRef = CGImageCreate(..., provider, ...); 

Tiene el objeto CGImage porque lo ha creado.

UIImage * image = [[UIImage alloc] initWithCGImage:imageRef]; 

Tienes el objeto UIImage porque lo has desbloqueado.

También es propietario del objeto CGImage. Si el objeto UIImage desea poseer el objeto CGImage, lo retendrá o hará su propia copia.

return [image autorelease]; 

Usted renuncia a su propiedad de la imagen.

De modo que su código pierde los píxeles (no transfirió la propiedad al proveedor de datos y usted no los liberó), el proveedor de datos (no lo lanzó) y el CGImage (no lo hizo) t lanzarlo). Una versión fija transferiría la propiedad de los píxeles al proveedor de datos y liberaría tanto el proveedor de datos como CGImage para cuando el UIImage esté listo. O simplemente use imageWithData:, como sugirió KennyTM.

+0

Peter: Gracias por tomarse el tiempo para revisar esto, especialmente después de que finalizó la sesión de preguntas y respuestas. –

1
unsigned char * pixels = malloc(...); 

También tuve un problema con malloc/libre después de usar CGImageCreate Finalmente encontré solución buena y sencilla. acabo de cambiar la línea:

CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, pixels, pixelBufferSize, NULL); 

con:

NSData *data = [NSData dataWithBytes:pixels length:pixelBufferSize]; 
CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)data); 

Justo después de que yo pudiera liberar memoria mallocked:

free (pixels); 
+1

Esto requiere copiar todos los bytes, lo que puede ser ineficiente para algunos casos de uso. En su lugar, use [NSData dataWithBytesNoCopy: length] que tomará posesión de los bytes y los liberará cuando ya no los necesiten. –

Cuestiones relacionadas