6

Mam aplikację opartą na ARC, która ładuje około 2000 dość dużych (1-4 MB) zakodowanych obrazów Base64 z usługi internetowej. Konwertuje ciągi dekodowane Base64 na pliki obrazów .png i zapisuje je na dysku. Wszystko to odbywa się w pętli, w której nie powinienem mieć żadnych wzmianek źródłowych.Obiektywny C UIImagePNGReprezentacja pamięci operacyjnej (przy użyciu ARC)

Sprofilowałem swoją aplikację i okazało się, że UIImagePNGRepresentation zajmowało około 50% dostępnej pamięci.

Sposób, w jaki go widzę, UIImagePNGReprezentacja buforuje obrazy, które tworzy. Jednym ze sposobów rozwiązania tego problemu jest wyczyszczenie pamięci podręcznej. Wszelkie pomysły, jak można to zrobić?

Innym rozwiązaniem byłoby użycie czegoś innego niż UIImagePNGRepresentation?

Już wypróbowałem to bez powodzenia: Memory issue in using UIImagePNGRepresentation. Nie wspominając o tym, że nie mogę naprawdę skorzystać z dostarczonego tam rozwiązania, ponieważ spowolniłoby to moją aplikację.

Jest to metoda, którą wywołuję z mojej pętli. UIImage jest obraz konwertowany z 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: wymiary obrazu różnią się mniej więcej od 1000 do 3000 * 800 * 2000.

Odpowiedz

4

Można owinąć ciało metody przez autorelease basenie

+ (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) 
    } 
} 

Ale faktycznie może to być pomocne, jeśli podasz nam trochę więcej numerów:
Co zrobić wymiary obrazy mają. Jest to ważne, ponieważ dane obrazu są przechowywane w pamięci w pikselach. iE a image 2000px width * 2000px height * 4 Bytes (RGBA) ~ 15MB. Teraz wyobraź sobie, że algorytm konwersji będzie musiał przechowywać informacje dla każdego piksela lub przynajmniej jakiegoś obszaru. Należy się spodziewać ogromnych liczb.

+0

Próbowałem autoreleasepool, ale to nie pomogło. Z jakiegoś powodu aplikacja ulega awarii, gdy próbuję profilować ją z autoreleasepool. Dodam wymiary obrazu do mojego pytania. – JHollanti

+0

Dodałem sprawę autoreleasepool do kilku innych miejsc i cała sprawa zaczęła działać. Byłoby o wiele łatwiej zacząć, gdybym mógł profilować moją aplikację, gdyby zawierał autoreleasepools. @vikingosegundo Miałeś kiedyś problem z profilowaniem, z którym się borykam? – JHollanti

+0

nie. ale często mam problemy debuggera i profilowania z lldb. Wróciłem do gdb. – vikingosegundo

0

Czy należy konwertować dane tutaj, może przekonwertować je na obciążenie z dysku?

Obiekt NSData może uczynić go NSMutableData w ten sposób pamięć dla niego jest przydzielana raz i rośnie w miarę potrzeb.

+0

To dobry pomysł, ale wolałbym mieć obraz jako obraz, więc nie muszę się martwić o jego konwersję w miejscach, w których potrzebuję go użyć. – JHollanti

0

Możesz mieć więcej szczęścia z The ImageIO Framework (PDF). Ma nieco większą kontrolę nad pamięcią i pamięcią podręczną niż UIKit.

+0

Jakieś wskazówki, w jaki sposób można przekonwertować ciąg base64 na obraz za pomocą ImageIO Framework? – JHollanti

+0

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

0

Spróbuj tego:

+ (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

miałem ten sam problem z UIImagePNGRepresentation i ARC. Mój projekt generuje kafelki, a przydzielona pamięć według UIImagePNGRepresentation nie została usunięta, nawet jeśli wywołanie UIImagePNGRepresentation jest częścią @autoreleasepool.

Nie miałem szczęścia, że ​​problem zniknął, dodając kolejne @ autoreleasepool, podobnie jak w przypadku JHollanti.

Moje rozwiązanie opiera się na idei Erics, używając ramy ImageIO zapisać plik 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); 
} 

}

najważniejsze jest, aby zwolnić obraz potem: CGImageRelease (image);

+0

To całkowicie naprawiło nasz problem. –

0

Rozwiązuję ten problem, wysyłając obraz 4-kanałowy (RGBA lub RGBX) zamiast obrazu 3-kanałowego (RGB). Możesz sprawdzić, czy jest jakaś szansa na zmianę parametrów obrazu.

Po konwersji Base64 na UIImage spróbuj użyć kCGImageAlphaNoneSkipLast zamiast kCGImageAlphaNone.

Powiązane problemy