6

Ho un'app basata su ARC che carica circa 2.000 immagini codificate Base64 di dimensioni abbastanza grandi (1-4MB) da un webservice. Converte le stringhe decodificate Base64 nei file immagine .png e li salva sul disco. Questo è tutto fatto in un ciclo in cui non dovrei avere riferimenti persistenti.Problema di memoria Objective C UIImagePNGRresentation (usando ARC)

Ho profilato la mia app e ho scoperto che UIImagePNGR rappresentava circa il 50% della memoria disponibile.

Il modo in cui lo vedo, UIImagePNGRepresentation sta memorizzando nella cache le immagini che crea. Un modo per risolvere il problema sarebbe quello di svuotare quella cache. Qualche idea su come si potrebbe fare?

Un'altra soluzione sarebbe utilizzare qualcosa di diverso da UIImagePNGRepresentation?

Ho già provato questo senza fortuna: Memory issue in using UIImagePNGRepresentation. Per non parlare del fatto che non posso davvero usare la soluzione fornita lì perché renderebbe la mia app troppo lenta.

Questo è il metodo che chiamo dal mio ciclo. UIImage è l'immagine convertita da Base64:

+ (void)saveImage:(UIImage*)image:(NSString*)imageName:(NSString*)directory { 
    NSData *imageData = UIImagePNGRepresentation(image); //convert image into .png format. 
    NSFileManager *fileManager = [NSFileManager defaultManager]; 
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); //create an array and store result of our search for the documents directory in it 
    NSString *documentsDirectory = [paths objectAtIndex:0]; //create NSString object, that holds our exact path to the documents directory 
    NSString *pathToFolder = [documentsDirectory stringByAppendingPathComponent:directory]; 

    if (![fileManager fileExistsAtPath:pathToFolder]) { 
    if(![fileManager createDirectoryAtPath:pathToFolder withIntermediateDirectories:YES attributes:nil error:NULL]) { 
     // Error handling removed for brevity 
    } 
    } 

    NSString *fullPath = [pathToFolder stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.png", imageName]]; //add our image to the path 
    [fileManager createFileAtPath:fullPath contents:imageData attributes:nil]; //finally save the path (image) 

    // clear memory (this did nothing to improve memory management) 
    imageData = nil; 
    fileManager = nil; 
} 

EDIT: dimensioni immagine variano approssimativamente da 1000 * 800-3000 * 2000.

risposta

4

Si potrebbe avvolgere il corpo del metodo da un pool autorelease

+ (void)saveImage:(UIImage*)image:(NSString*)imageName:(NSString*)directory { 

    @autoreleasepool 
    { 
     NSData *imageData = UIImagePNGRepresentation(image); //convert image into .png format. 
     NSFileManager *fileManager = [NSFileManager defaultManager]; 
     NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); //create an array and store result of our search for the documents directory in it 
     NSString *documentsDirectory = [paths objectAtIndex:0]; //create NSString object, that holds our exact path to the documents directory 
     NSString *pathToFolder = [documentsDirectory stringByAppendingPathComponent:directory]; 

     if (![fileManager fileExistsAtPath:pathToFolder]) { 
      if(![fileManager createDirectoryAtPath:pathToFolder withIntermediateDirectories:YES attributes:nil error:NULL]) { 
      // Error handling removed for brevity 
      } 
     } 

     NSString *fullPath = [pathToFolder stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.png", imageName]]; //add our image to the path 
     [fileManager createFileAtPath:fullPath contents:imageData attributes:nil]; //finally save the path (image) 
    } 
} 

Ma in realtà potrebbe essere utile, se ci forniscono alcuni altri numeri:
Quali dimensioni fanno le immagini hanno. Questo è importante, dato che i dati dell'immagine sono memorizzati in memoria in pixel non elaborati. iE un'immagine 2000px width * 2000px height * 4 Bytes (RGBA) ~ 15MB. Ora immagina, che l'algoritmo di conversione dovrà memorizzare le informazioni per ogni pixel o almeno qualche area. Sono previsti numeri enormi

+0

Ho provato l'autorespiratore ma non è stato d'aiuto. Per qualche motivo l'app si arresta in modo anomalo quando provo a configurarlo con l'autoreleasepool. Aggiungo le dimensioni dell'immagine alla mia domanda. – JHollanti

+0

Ho aggiunto il gioco di ruolo autoreleasepool ad un sacco di altri posti e l'intera cosa ha iniziato a funzionare. Sarebbe stato molto più facile iniziare se avessi potuto profilare la mia app quando conteneva autoreleasepools. @vikingosegundo Mai avuto questo problema di profilazione che sto affrontando? – JHollanti

+0

no. ma ho spesso problemi di debugger e di profilazione con lldb. Sono tornato a gdb. – vikingosegundo

0

È necessario convertire i dati qui, magari convertirli sul carico dal disco?

L'oggetto NSData forse lo rende NSMutableData in questo modo la memoria viene assegnata una volta e aumenta quando necessario.

+0

Questa è una buona idea, ma preferirei avere l'immagine come immagine, quindi non devo preoccuparmi di convertirla in luoghi in cui ho bisogno di usarla. – JHollanti

0

Potresti avere più fortuna con The ImageIO Framework (PDF). Ha un po 'più di controllo sulla memoria e sul caching rispetto a UIKit.

+0

Qualsiasi suggerimento su come convertire una stringa base64 in un'immagine utilizzando ImageIO Framework? – JHollanti

+0

http://cocoawithlove.com/2009/06/base64-encoding-options-on-mac-and.html?m=1 – EricS

0

Prova questo:

+ (void)saveImage:(UIImage*)image:(NSString*)imageName:(NSString*)directory { 
    NSData *imageData = UIImagePNGRepresentation(image); //convert image into .png format. 
    CFDataRef imageDataRef = (__bridge_retained CFDataRef) imageData; // ARC fix 
    NSFileManager *fileManager = [NSFileManager defaultManager]; 
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); //create an array and store result of our search for the documents directory in it 
    NSString *documentsDirectory = [paths objectAtIndex:0]; //create NSString object, that holds our exact path to the documents directory 
    NSString *pathToFolder = [documentsDirectory stringByAppendingPathComponent:directory]; 

    if (![fileManager fileExistsAtPath:pathToFolder]) { 
    if(![fileManager createDirectoryAtPath:pathToFolder withIntermediateDirectories:YES attributes:nil error:NULL]) { 
     // Error handling removed for brevity 
    } 
    } 

    NSString *fullPath = [pathToFolder stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.png", imageName]]; //add our image to the path 
    [fileManager createFileAtPath:fullPath contents:imageData attributes:nil]; //finally save the path (image) 

    // clear memory (this did nothing to improve memory management) 
    imageData = nil; 
    CFRelease(imageDataRef); // ARC fix 
    fileManager = nil; 
} 
1

ho avuto lo stesso problema con UIImagePNGRepresentation e ARC. Il mio progetto sta generando riquadri e la memoria allocata da UIImagePNGRepresentation non è stata rimossa neanche quando la chiamata a UIImagePNGR è parte di un @autoreleasepool.

Non ho avuto la fortuna che il problema sia scomparso aggiungendo alcuni altri @ autoreleasepool come per JHollanti.

La mia soluzione è basata su Erics idea, utilizzando il Framework ImageIO per salvare il file PNG:

-(void)saveImage:(CGImageRef)image directory:(NSString*)directory filename:(NSString*)filename { 
@autoreleasepool { 
    CFURLRef url = (__bridge CFURLRef)[NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/%@", directory, filename]]; 
    CGImageDestinationRef destination = CGImageDestinationCreateWithURL(url, kUTTypePNG, 1, NULL); 
    CGImageDestinationAddImage(destination, image, nil); 

    if (!CGImageDestinationFinalize(destination)) 
     NSLog(@"ERROR saving: %@", url); 

    CFRelease(destination); 
    CGImageRelease(image); 
} 

}

più importante è di rilasciare l'immagine in seguito: CGImageRelease (immagine);

+0

Questo ha completamente risolto il nostro problema. –

0

Ho risolto questo problema inviando un'immagine a 4 canali (RGBA o RGBX) anziché un'immagine a 3 canali (RGB). Puoi controllare se c'è qualche possibilità di cambiare i parametri della tua immagine.

Quando si converte Base64 in UIImage, provare a utilizzare kCGImageAlphaNoneSkipLast anziché kCGImageAlphaNone.

Problemi correlati