2009-09-15 15 views
5

Bien Tengo un mundo de dificultad para rastrear esta pérdida de memoria. Cuando ejecuto este script, no veo ningún error de memoria, pero mi objectalloc está subiendo. Instruments apunta a CGBitmapContextCreateImage> create_bitmap_data_provider> malloc, esto ocupa el 60% de mi objectalloc.iPhone - UIImage Leak, CGBitmapContextCreateImage Leak

Este código se invoca varias veces con un NSTimer.

//GET IMAGE FROM RESOURCE DIR 
    NSString * fileLocation = [[NSBundle mainBundle] pathForResource:imgMain ofType:@"jpg"]; 
    NSData * imageData = [NSData dataWithContentsOfFile:fileLocation]; 
    UIImage * blurMe = [UIImage imageWithData:imageData]; 

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 

    UIImage * scaledImage = [blurMe _imageScaledToSize:CGSizeMake(blurMe.size.width/dblBlurLevel, blurMe.size.width/dblBlurLevel) interpolationQuality:3.0]; 
    UIImage * labelImage = [scaledImage _imageScaledToSize:blurMe.size interpolationQuality:3.0]; 
    UIImage * imageCopy = [[UIImage alloc] initWithCGImage:labelImage.CGImage]; 

    [pool drain]; // deallocates scaledImage and labelImage 

    imgView.image = imageCopy; 
    [imageCopy release]; 

A continuación se muestra la función de desenfoque. Creo que el problema objectalloc se encuentra aquí. Tal vez solo necesito un par de ojos frescos. Sería genial si alguien pudiera resolver esto. Lo siento, es un poco largo ... Trataré de acortarlo.

@implementation UIImage(Blur) 
    - (UIImage *)blurredCopy:(int)pixelRadius 
    { 
     //VARS 
     unsigned char *srcData, *destData, *finalData; 
     CGContextRef context = NULL; 
     CGColorSpaceRef colorSpace; 
     void *   bitmapData; 
     int    bitmapByteCount; 
     int    bitmapBytesPerRow; 

     //IMAGE SIZE 
     size_t pixelsWide = CGImageGetWidth(self.CGImage); 
     size_t pixelsHigh = CGImageGetHeight(self.CGImage); 
     bitmapBytesPerRow = (pixelsWide * 4); 
     bitmapByteCount  = (bitmapBytesPerRow * pixelsHigh); 

     colorSpace = CGColorSpaceCreateDeviceRGB(); 
     if (colorSpace == NULL) { return NULL; } 

     bitmapData = malloc(bitmapByteCount); 
     if (bitmapData == NULL) { CGColorSpaceRelease(colorSpace); } 

     context = CGBitmapContextCreate (bitmapData, 
         pixelsWide, 
         pixelsHigh, 
         8,  
         bitmapBytesPerRow, 
         colorSpace, 
         kCGImageAlphaPremultipliedFirst); 
     if (context == NULL) { free (bitmapData); } 

     CGColorSpaceRelease(colorSpace); 
     free (bitmapData); 

     if (context == NULL) { return NULL; } 

     //PREPARE BLUR 
     size_t width = CGBitmapContextGetWidth(context); 
     size_t height = CGBitmapContextGetHeight(context); 
     size_t bpr = CGBitmapContextGetBytesPerRow(context); 
     size_t bpp = (CGBitmapContextGetBitsPerPixel(context)/8); 
     CGRect rect = {{0,0},{width,height}}; 

     CGContextDrawImage(context, rect, self.CGImage); 

     // Now we can get a pointer to the image data associated with the bitmap 
     // context. 
     srcData = (unsigned char *)CGBitmapContextGetData (context); 
     if (srcData != NULL) 
     { 

      size_t dataSize = bpr * height; 
      finalData = malloc(dataSize); 
      destData = malloc(dataSize); 
      memcpy(finalData, srcData, dataSize); 
      memcpy(destData, srcData, dataSize); 

      int sums[5]; 
      int i, x, y, k; 
      int gauss_sum=0; 
      int radius = pixelRadius * 2 + 1; 
      int *gauss_fact = malloc(radius * sizeof(int)); 

      for (i = 0; i < pixelRadius; i++) 
      { 
      .....blah blah blah... 
       THIS IS JUST LONG CODE THE CREATES INT FIGURES 
       ........blah blah blah...... 
       } 


      if (gauss_fact) { free(gauss_fact); } 
     } 

     size_t bitmapByteCount2 = bpr * height; 
     //CREATE DATA PROVIDER 
     CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, srcData, bitmapByteCount2, NULL); 

     //CREATE IMAGE 
     CGImageRef cgImage = CGImageCreate(
           width, 
           height, 
           CGBitmapContextGetBitsPerComponent(context), 
           CGBitmapContextGetBitsPerPixel(context), 
           CGBitmapContextGetBytesPerRow(context), 
           CGBitmapContextGetColorSpace(context), 
           CGBitmapContextGetBitmapInfo(context), 
           dataProvider, 
           NULL, 
           true, 
           kCGRenderingIntentDefault 
            ); 

     //RELEASE INFORMATION 
     CGDataProviderRelease(dataProvider); 
     CGContextRelease(context); 

     if (destData) { free(destData); } 
     if (finalData) { free(finalData); } 
     if (srcData) { free(srcData); } 


     UIImage *retUIImage = [UIImage imageWithCGImage:cgImage]; 
     CGImageRelease(cgImage); 

     return retUIImage; 
    } 

La única cosa que puedo pensar en que se mantiene la ObjectAlloc está presente UIImage * retUIImage = [UIImage imageWithCGImage: CGImage]; ... pero ¿cómo hacerlo Libero que después se ha vuelto? Espero que alguien pueda ayudar por favor.

+1

tengo ni idea de si está relacionado a la fuga, pero no debería estar llamando 'libre (bitmapData)' antes de que haya soltado el contexto. Los bloques de memoria asignados con 'malloc()' no tienen contadores de retención, por lo que si llama a 'free()' se libera inmediatamente, incluso si el CGContext lo está usando. – benzado

+0

¿Está dirigido a iPhone OS 3.1 o superior? Puede ser un error en el marco 3.0 o anterior. Si está en 3.1, no necesita asignar bitmapData para 'CGBitmapContextCreate'. También '_imageScaledToSize' es una llamada no documentada. Por favor no lo uses Algunas personas pueden no estar dispuestas a responder su pregunta si está utilizando llamadas no documentadas. – lucius

+0

hey bbullis21 ¿su código de desenfoque funciona bien en el dispositivo? – Leena

Respuesta

0

He visto comportamiento similar y leí varios mensajes similares. Crear un grupo de autorrelease no parece ayudar. Parece que la biblioteca está filtrando la memoria o almacenándola en una agrupación de autoelementos de mayor nivel para que no se lance hasta que sea demasiado tarde. Sospecho que hay un error en el marco, pero no puedo probarlo.

0

ayuda si en realidad se envuelve la asignación de imagen y liberar en el ciclo de la existencia de objetos piscina,

pool = [[NSAutoreleasePool alloc] init]; 

[imageView setImage: [UIImage imageNamed:"image1.png"]]; 
... 
[imageView setImage: [UIImage imageNamed:"image2.png"]]; 
... 
[imageView setImage: [UIImage imageNamed:"image3.png"]]; 
.... 
[pool drain]; 
[pool release]; 

En este ejemplo image3.png no será liberado, pero imagen1 imagen2 y habrá.

+0

No debe '-release' después de' -drain' (que es equivalente a '-release' en tiempos de ejecución no recolectados como basura) ya que causa una liberación doble. – kennytm

1

Obtenga clang y ejecute su proyecto a través de él. No solo encontrará sus fugas (estáticas), sino que lo ayudará a comprender mejor las reglas de recuento de referencias, al menos lo hizo por mí.

+1

O ejecute "Build & Analyze" en Xcode 3.2 si está ejecutando Snow Leopard. Es clang y hace un buen trabajo al diagramar el flujo de ejecución que conduce al problema. –

0

Algunas ideas/pensamientos:

Por un lado, este trozo de código claramente tiene problemas ya que se puede liberar bitmapData dos veces si el contexto no puede asignar.

if (context == NULL) { free (bitmapData); } 

    CGColorSpaceRelease(colorSpace); 
    free (bitmapData); 

    if (context == NULL) { return NULL; } 

También estoy de acuerdo con benzado que bitmapData hasta que haya terminado con el contexto ... aunque no se bloquea el cual es desconcertante. suerte tal vez.

Tenga en cuenta que estoy bastante seguro de que el proveedor de datos y los bits se refiere a van a formar parte de la CGImage regresar de CGImageCreate:

proveedor de La fuente de datos para el mapa de bits. Para obtener información acerca de los formatos de datos admitidos, consulte la discusión a continuación. Cuarzo retiene este objeto; a su regreso, puede liberarlo de manera segura.

Entonces, eso significa que hasta que se libere el UIImage que devuelve, no creo que cgImage o el proveedor de datos de bits desaparezcan. Entonces, ¿quizás el problema es que el UIImage no se va?

¿Por qué no solo utiliza CGBitmapContextCreateImage() para crear la imagen resultante? (es posible que necesite llamar a CGContextFlush() para forzar el dibujo al mapa de bits, pero parece que está haciendo todos los cambios de píxel manualmente, por lo que posiblemente no sea necesario).

Basándome en la vaga documentación, no creo que deba estar libre() 'el puntero devuelto por CGBitmapContextGetData (pero eso es una suposición debido a la imprecisión de los documentos).

también:

no está inicializando srcData, destData & FinalData a NULL para que su prueba antes de free() 'ing les parece arriesgado.

todo lo dicho (y no probar algunas de esas cosas), que tenía una fuga en nuestra aplicación para Mac en un momento dado porque en 10.5.6 y anteriores algún aspecto de CIImage imageWithCGImage, imageByApplyingTransofrm, imageByCroppingToRect, NSBitmapImageRep initWithCIImage o NSBitmapImageRep CGImage filtrado. Esto se corrigió en 10.5.7, por lo que es posible que exista algo similar en la versión del SO del iPhone con la que está probando.

4

He usado Quartz muchas veces. Cada vez que lo hago es una pesadilla. Por lo que me di cuenta, y yo he llenado un error en el Apple el envío de un proyecto como prueba de choque, una de las 4 cosas es cierta:

  1. fugas de cuarzo como el infierno
  2. Se tarda demasiado tiempo para liberar la memoria
  3. usa demasiada memoria innecesariamente o
  4. una combinación de todos ellos.

Una vez creé un proyecto simple para demostrarlo. El proyecto tenía un botón. Cada vez que se presiona el botón, se agrega una pequeña imagen (100x100 píxeles) a la pantalla. La imagen estaba compuesta por dos capas, la imagen misma y una capa adicional que contiene una línea punteada dibujada alrededor del borde de la imagen. Este dibujo fue hecho en Quartz. Al presionar 8 veces el botón, la aplicación falla. Usted puede decir: presionando el botón 8 veces y se agregaron 16 imágenes a la pantalla, esta es la razón de la falla, ¿no?

En lugar de dibujar el borde con Cuarzo, decidí hacer un dibujo previo en un PNG y simplemente agregarlo como una capa al objeto. Las mismas dos capas por objeto creado. ¿Derecha?

Hice clic 100 veces, agregué 200 imágenes a la pantalla y no se bloqueó. La memoria nunca superó los 800 Kb. Podría continuar haciendo clic ... la aplicación continuó más ágil y rápida. Sin advertencia de memoria, sin bloqueo.

Apple tiene que revisar Quartz urgentemente.

0

Tuve el mismo problema al usar CGBitmapContextCreate (..) con fugas que ocurren aleatoriamente y se acumulan.

He resuelto mediante la creación de la UIImage partir de los datos de mapa de bits utilizando la función diferente:

Probar:

CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data); 

o

provider = CGDataProviderCreateWithData((void *)pixelBuffer, sourceBaseAddr, sourceRowBytes * height, ReleaseCVPixelBufferFunction); 


CGImageRef imageRef = CGImageCreate(cvMat.cols,          // Width 
            cvMat.rows,          // Height 
            8,            // Bits per component 
            8 * cvMat.elemSize(),       // Bits per pixel 
            cvMat.step,          // Bytes per row 
            colorSpace,          // Colorspace 
            kCGImageAlphaNone | kCGBitmapByteOrderDefault, // Bitmap info flags 
            provider,          // CGDataProviderRef 
            NULL,           // Decode 
            false,           // Should interpolate 
            kCGRenderingIntentDefault);      // Intent 
0

Si está utilizando la recolección de basura, utilizar CFMakeCollectable (posterFrame).Si está utilizando la gestión de memoria tradicional, es muy sencillo:

return (CGImageRef)[(id)posterFrame autorelease]; 

lanzas un CFTypeRef (en este caso, un CGImageRef) a un puntero de objeto de Objective-C, enviar el mensaje -autorelease, y luego fundido el resultado vuelve a CGImageRef. Este patrón funciona para (casi) cualquier tipo que sea compatible con CFRetain() y CFRelease().

How to Autorelease a CGImageRef?