CGDataProviderCopyData在内存中累积导致崩溃

时间:2013-08-28 19:25:53

标签: ios objective-c memory-management memory-leaks cfdata

好的,所以我从服务器上下载了一堆大图像(5mb),然后将这些碎片拼接在一起并从一个字节数组中渲染出整个图像。但是,我意识到每个图像的数据都没有被释放,因此会导致内存警告和我的应用程序崩溃。我认为,由于我的显式(__bridge_transfer NSData *)转换,ARC将负责释放对象,但它仍然被证明是一个问题。在仪器中,~1mb的称为“CGDataProviderCopyData”的对象会建立起来,并且不会为每个被拼接到整个图像中的文件丢弃。任何想法或任何能引导我朝正确方向前进的人?很有责任。

 // Create  array to add all files into total image
NSMutableArray *byteArray = [[NSMutableArray alloc] initWithCapacity:(imageHeight * imageWidth)];

// Iterate through each file in files array
for (NSString *file in array)
{        
    // Set baseURL for individual file path
    NSString *baseURL = [NSString stringWithFormat:@"http://xx.225.xxx.xxx%@",[imageInfo objectForKey:@"BaseURL"]];

    // Specify imagePath by appending baseURL to file name
    NSString *imagePath = [NSString stringWithFormat:@"%@%@", baseURL, file];

    // Change NSString --> NSURL --> NSData
    NSURL *imageUrl = [NSURL URLWithString:imagePath];
    NSData *imageData = [NSData dataWithContentsOfURL:imageUrl];

    // Create image from imageData
    UIImage *image = [UIImage imageWithData:imageData];
    CGImageRef cgimage = image.CGImage;

    size_t width  = CGImageGetWidth(cgimage);
    size_t height = CGImageGetHeight(cgimage);

    size_t bpr = CGImageGetBytesPerRow(cgimage);
    size_t bpp = CGImageGetBitsPerPixel(cgimage);
    size_t bpc = CGImageGetBitsPerComponent(cgimage);
    size_t bytes_per_pixel = bpp / bpc;

    // Get CGDataProviderRef from cgimage
    CGDataProviderRef provider = CGImageGetDataProvider(cgimage);

    // This is the object that is not being released
    NSData *data = (__bridge_transfer NSData *)CGDataProviderCopyData(provider);      //Using (__bridge_transfer NSData *) casts the provider to type NSData and gives ownership to ARC, but still not discarded

    const UInt8 *bytes = (Byte *)[data bytes];

    // Log which file is currently being iterated through 
    NSLog(@"---Stitching png file to total image: %@", file);

    // Populate byte array with channel data from each pixel
    for(size_t row = 0; row < height; row++)
    {
        for(size_t col = 0; col < width; col++)
        {
            const UInt8* pixel =
            &bytes[row * bpr + col * bytes_per_pixel];

            for(unsigned short i = 0; i < 4; i+=4)
            {
                __unused unsigned short red = pixel[i];         // red channel - unused
                unsigned short green = pixel[i+1];              // green channel
                unsigned short blue = pixel[i+2];               // blue channel
                __unused unsigned short alpha = pixel[i+3];     // alpha channel - unused

                // Create dicom intensity value from intensity = [(g *250) + b]
                unsigned short dicomInt = ((green * 256) + blue);

                //Convert unsigned short intensity value to NSNumber so can store in array as object
                NSNumber *DICOMvalue = [NSNumber numberWithInt:dicomInt];

                // Add to image array (total image)
                [byteArray addObject:DICOMvalue];
            }
        }
    }
    data = nil;
}
return byteArray;

通过Xcode运行“Analyze”也没有显示任何明显的泄漏。

1 个答案:

答案 0 :(得分:1)

我几乎逐字逐句地接受了这个代码,并做了一些调查。使用CFDataRef / NSData,我能够看到你看到NSDatas没有消失的问题,我能够通过在@autoreleasepool范围内包装使用NSData的代码部分来解决它,像这样:

 // Create  array to add all files into total image
NSMutableArray *byteArray = [[NSMutableArray alloc] initWithCapacity:(imageHeight * imageWidth)];

// Iterate through each file in files array
for (NSString *file in array)
{        
    // Set baseURL for individual file path
    NSString *baseURL = [NSString stringWithFormat:@"http://xx.225.xxx.xxx%@",[imageInfo objectForKey:@"BaseURL"]];

    // Specify imagePath by appending baseURL to file name
    NSString *imagePath = [NSString stringWithFormat:@"%@%@", baseURL, file];

    // Change NSString --> NSURL --> NSData
    NSURL *imageUrl = [NSURL URLWithString:imagePath];
    NSData *imageData = [NSData dataWithContentsOfURL:imageUrl];

    // Create image from imageData
    UIImage *image = [UIImage imageWithData:imageData];
    CGImageRef cgimage = image.CGImage;

    size_t width  = CGImageGetWidth(cgimage);
    size_t height = CGImageGetHeight(cgimage);

    size_t bpr = CGImageGetBytesPerRow(cgimage);
    size_t bpp = CGImageGetBitsPerPixel(cgimage);
    size_t bpc = CGImageGetBitsPerComponent(cgimage);
    size_t bytes_per_pixel = bpp / bpc;

    // Get CGDataProviderRef from cgimage
    CGDataProviderRef provider = CGImageGetDataProvider(cgimage);

    @autoreleasepool
    {
        // This is the object that is not being released
        NSData *data = (__bridge_transfer NSData *)CGDataProviderCopyData(provider);      //Using (__bridge_transfer NSData *) casts the provider to type NSData and gives ownership to ARC, but still not discarded

        const UInt8 *bytes = (Byte *)[data bytes];

        // Log which file is currently being iterated through 
        NSLog(@"---Stitching png file to total image: %@", file);

        // Populate byte array with channel data from each pixel
        for(size_t row = 0; row < height; row++)
        {
            for(size_t col = 0; col < width; col++)
            {
                const UInt8* pixel =
                &bytes[row * bpr + col * bytes_per_pixel];

                for(unsigned short i = 0; i < 4; i+=4)
                {
                    __unused unsigned short red = pixel[i];         // red channel - unused
                    unsigned short green = pixel[i+1];              // green channel
                    unsigned short blue = pixel[i+2];               // blue channel
                    __unused unsigned short alpha = pixel[i+3];     // alpha channel - unused

                    // Create dicom intensity value from intensity = [(g *250) + b]
                    unsigned short dicomInt = ((green * 256) + blue);

                    //Convert unsigned short intensity value to NSNumber so can store in array as object
                    NSNumber *DICOMvalue = [NSNumber numberWithInt:dicomInt];

                    // Add to image array (total image)
                    [byteArray addObject:DICOMvalue];
                }
            }
        }
        data = nil;
    }
}
return byteArray;

添加@autoreleasepool后,我注释掉你创建NSNumbers的部分并将它们放入数组,我能够在仪器的Allocations模板中看到确实现在,每次循环都会释放CFData对象。

我注释掉你创建NSNumbers并将它们放入数组的部分的原因是,那里有那个代码,你最终会将width * height * 4 NSNumbers添加到byteArray。这意味着即使NSData正确发布,无论如何,您的堆使用量都会增加width * height * 4 * <at least 4 bytes, maybe more>。也许这就是你需要做的事情,但它确实让我更难看到NSDatas发生了什么,因为它们的大小与NSNumbers的数量相形见绌。

希望有所帮助。