6

Tôi có một ứng dụng dựa trên ARC tải khoảng 2.000 hình ảnh mã hóa Base64 khá lớn (1-4MB) từ một dịch vụ web. Nó chuyển đổi các chuỗi đã giải mã Base64 thành .png các tệp hình ảnh và lưu chúng vào đĩa. Đây là tất cả được thực hiện trong một vòng lặp mà tôi không nên có bất kỳ tài liệu tham khảo kéo dài.Mục tiêu C UIImagePNGGiới thiệu về vấn đề bộ nhớ (sử dụng ARC)

Tôi đã lược tả ứng dụng của mình và phát hiện ra rằng UIImagePNGRepresentation đang chiếm khoảng 50% bộ nhớ có sẵn.

Cách tôi xem, UIImagePNGĐại diện đang lưu vào bộ nhớ cache hình ảnh mà nó tạo. Một cách để khắc phục điều này là xóa bộ nhớ cache đó. Bất kỳ ý tưởng làm thế nào người ta có thể làm điều đó?

Một giải pháp khác là sử dụng thứ gì đó khác với UIImagePNGĐại diện?

Tôi đã thử điều này mà không có may mắn: Memory issue in using UIImagePNGRepresentation. Chưa kể rằng tôi không thể thực sự sử dụng giải pháp được cung cấp ở đó vì nó sẽ làm cho ứng dụng của tôi quá chậm.

Đây là phương thức tôi gọi từ vòng lặp của tôi. UIImage là hình ảnh chuyển đổi từ 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: Kích thước hình ảnh khác nhau khoảng từ 1000 * 800-3000 * 2000.

Trả lời

4

Bạn có thể quấn cơ thể phương pháp bởi một hồ bơi 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) 
    } 
} 

Nhưng trên thực tế nó có thể là hữu ích, nếu bạn cung cấp cho chúng ta một số con số hơn:
gì kích thước làm những hình ảnh có. Điều này rất quan trọng vì dữ liệu hình ảnh được lưu trữ trong bộ nhớ trong pixel thô. iE a image 2000px width * 2000px height * 4 Bytes (RGBA) ~ 15MB. Bây giờ hãy tưởng tượng, thuật toán chuyển đổi sẽ phải lưu trữ thông tin cho mỗi pixel hoặc ít nhất một số khu vực. Con số khổng lồ được mong đợi.

+0

Tôi đã thử autoreleasepool nhưng nó không giúp ích gì. Đối với một số lý do các ứng dụng bị treo khi tôi cố gắng để hồ sơ nó với autoreleasepool .. Tôi sẽ thêm các kích thước hình ảnh cho câu hỏi của tôi. – JHollanti

+0

Tôi đã thêm điều khiển autoreleasepool vào một loạt các địa điểm khác và toàn bộ sự việc bắt đầu hoạt động. Nó sẽ được dễ dàng hơn nhiều để bắt đầu với nếu tôi có thể đã profiled ứng dụng của tôi khi nó chứa autoreleasepools. @vikingosegundo Đã từng có vấn đề về hồ sơ này mà tôi đang gặp phải chưa? – JHollanti

+0

không. nhưng tôi thường xuyên gỡ rối và các vấn đề về lược tả với lldb. Tôi đã chuyển về gdb. – vikingosegundo

0

Cần chuyển đổi dữ liệu ở đây, có thể chuyển đổi dữ liệu đó trên tải từ đĩa?

Đối tượng NSData có thể biến nó thành NSMutableData theo cách này bộ nhớ cho nó được cấp phát một lần và tăng khi cần.

+0

Đó là một ý tưởng tốt, nhưng tôi muốn có hình ảnh như một hình ảnh vì vậy tôi không phải lo lắng về việc chuyển đổi nó ở những nơi mà tôi cần phải sử dụng nó. – JHollanti

0

Bạn có thể may mắn hơn với The ImageIO Framework (PDF). Nó có nhiều quyền kiểm soát hơn đối với bộ nhớ và bộ nhớ đệm hơn UIKit.

+0

Bất kỳ con trỏ nào có thể chuyển chuỗi base64 thành hình ảnh bằng cách sử dụng Khung ImageIO? – JHollanti

+0

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

0

Hãy thử điều này:

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

tôi đã cùng một vấn đề với UIImagePNGRepresentation và ARC. Dự án của tôi đang tạo các ô xếp và bộ nhớ được phân bổ bởi UIImagePNGRepresentation vừa bị xóa ngay cả khi cuộc gọi UIImagePNGRepresentation là một phần của @autoreleasepool.

Tôi không có may mắn rằng vấn đề này biến mất bằng cách thêm một vài @ autoreleasepool giống như đã làm cho JHollanti.

Giải pháp của tôi được dựa trên EricS ý tưởng, sử dụng khung ImageIO để lưu các tập tin 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); 
} 

}

quan trọng nhất là để phát hành hình ảnh sau: CGImageRelease (hình ảnh);

+0

Điều này hoàn toàn cố định vấn đề của chúng tôi. –

0

Chỉ một trong những cách có thể là sử dụng libs của github để tải xuống và lưu trữ UIImage/NSData từ Internet. Nó có thể bởi SDWebImage (https://github.com/rs/SDWebImage) hoặc APSmartStorage (https://github.com/Alterplay/APSmartStorage). Mới nhất nhận được một hình ảnh từ Internet và lưu trữ nó một cách thông minh trên đĩa và trong bộ nhớ.

0

Tôi giải quyết vấn đề này bằng cách gửi hình ảnh 4 kênh (RGBA hoặc RGBX) thay vì hình ảnh 3 kênh (RGB). Bạn có thể kiểm tra xem có bất kỳ cơ hội nào để thay đổi các thông số của hình ảnh của bạn hay không.

Khi bạn chuyển đổi Base64 thành UIImage, hãy thử sử dụng kCGImageAlphaNoneSkipLast thay vì kCGImageAlphaNone.

Các vấn đề liên quan