尽管ARC存在大量内存使用

时间:2014-03-24 22:11:56

标签: objective-c cocoa memory-management automatic-ref-counting autorelease


我有以下功能可以打开图像,缩放图像并将其保存到另一个文件中。

-(void)writeFileToIcon:(NSString *)fullPath :(NSString *)finalPath :(NSSize)outputSize
{
    NSData *dataToWrite;
    NSBitmapImageRep *rep;

    rep = [NSBitmapImageRep imageRepWithData:[[self scaleImage:[[NSImage alloc]initWithContentsOfFile:fullPath] toSize:outputSize] TIFFRepresentation]];
    dataToWrite = [rep representationUsingType:NSPNGFileType properties:nil];
    [dataToWrite writeToFile:finalPath atomically:YES];
}

- (NSImage *)scaleImage:(NSImage *)image toSize:(NSSize)targetSize
{
    if ([image isValid])
    {
        NSSize imageSize = [image size];
        float width  = imageSize.width;
        float height = imageSize.height;
        float targetWidth  = targetSize.width;
        float targetHeight = targetSize.height;
        float scaleFactor  = 0.0;
        float scaledWidth  = targetWidth;
        float scaledHeight = targetHeight;

        NSPoint thumbnailPoint = NSZeroPoint;

        if (!NSEqualSizes(imageSize, targetSize))
        {
            float widthFactor  = targetWidth / width;
            float heightFactor = targetHeight / height;

            if (widthFactor < heightFactor)
            {
                scaleFactor = widthFactor;
            }
            else
            {
                scaleFactor = heightFactor;
            }

            scaledWidth  = width  * scaleFactor;
            scaledHeight = height * scaleFactor;

            if (widthFactor < heightFactor)
            {
                thumbnailPoint.y = (targetHeight - scaledHeight) * 0.5;
            }

            else if (widthFactor > heightFactor)
            {
                thumbnailPoint.x = (targetWidth - scaledWidth) * 0.5;
            }

            NSImage *newImage = [[NSImage alloc] initWithSize:NSMakeSize(scaledWidth, scaledHeight)];

            [newImage lockFocus];

            NSRect thumbnailRect;
            thumbnailRect.origin = NSZeroPoint;
            thumbnailRect.size.width = scaledWidth;
            thumbnailRect.size.height = scaledHeight;

            [image drawInRect:thumbnailRect
                     fromRect:NSZeroRect
                    operation:NSCompositeSourceOver
                     fraction:1.0];

            [newImage unlockFocus];
            return newImage;
        }
        return nil;
    }
    return nil;
}

但是每次调用此函数时,内存使用率都会越来越高(1000次调用最多可达5 GB) 问题是drawRect函数,它似乎需要大量内存(根据分析器),但不会释放它。
我如何“要求”ARC释放它?

感谢。

3 个答案:

答案 0 :(得分:2)

可能需要查看整个代码才能找到问题。不过有一个想法是:在ARC下,你不能打电话给#34;发布&#34;在对象上,但如果将对象的指针设置为&#34; nil&#34;,则将释放该对象(除非在某处存在对该对象的其他强引用)。

我建议您跟踪您的代码并确保您不再拥有您不再需要的对象。如果您的代码封装和结构良好,那么这不应该发生。

但是,如果你的代码设计得很好,那么实际需要这么多的内存(不太可能,但不知道没有更多细节)。如果是这种情况,那么让系统管理内存,它将在适当时释放对象。如果内存使用是一个问题,请尝试在某处进行优化。

偏离主题:这些长嵌套的方法在方法中有多个返回点并不是一个好主意;我建议你稍微重新构建你的代码。如果您编写更清晰的代码,您将对其进行更多控制,并且您将更快地找到问题的解决方案。

答案 1 :(得分:1)

您是从循环调用此函数还是不返回主事件循环?添加明确的@autoreleasepool可能有所帮助。

-(void)writeFileToIcon:(NSString *)fullPath :(NSString *)finalPath :(NSSize)outputSize
{
    @autoreleasepool {
        NSBitmapImageRep *rep = [NSBitmapImageRep imageRepWithData:[[self scaleImage:[[NSImage alloc]initWithContentsOfFile:fullPath] toSize:outputSize] TIFFRepresentation]];
        NSData *dataToWrite = [rep representationUsingType:NSPNGFileType properties:nil];
        [dataToWrite writeToFile:finalPath atomically:YES];
    }
}

理论上,这并不是必需的,因为在某些情况下,使用ARC编译的代码会使自动释放池短路。但是,你可能会以某种方式击败这种优化。

请注意,在逻辑上内存分配成为问题的地方,通常最好这样做。因此,您调用此方法的for循环将是@autoreleasepool更好的地方。

答案 2 :(得分:1)

我的猜测是你的问题与图像类中的缓存有关,但这可能是错误的。什么似乎改善了问题:

-(void)writeFileToIcon:(NSString *)fullPath :(NSString *)finalPath :(NSSize)outputSize
{
    // wrap in autorelease pool to localise any use of this by the image classes
    @autoreleasepool
    {
        NSImage *dstImage = [self scaleImageFile:finalPath toSize:outputSize];
        NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] initWithData:[dstImage TIFFRepresentation]];
        NSData *dataToWrite = [rep representationUsingType:NSPNGFileType properties:nil];
        [dataToWrite writeToFile:finalPath atomically:YES];
    }
}

- (NSImage *)scaleImageFile:(NSString *)fullPath toSize:(NSSize)targetSize
{
    NSImageRep *srcImageRep = [NSImageRep imageRepWithContentsOfFile:fullPath];
    if (srcImageRep == nil)
        return nil;

    NSSize imageSize = NSMakeSize(srcImageRep.pixelsWide, srcImageRep.pixelsHigh);
    NSSize scaledSize;
    NSPoint thumbnailPoint = NSZeroPoint;
    NSRect thumbnailRect;

    if (!NSEqualSizes(imageSize, targetSize))
    {
        // your existing scale calculation
        ...

        scaledSize = NSMakeSize(scaledWidth, scaledHeight);
    }
    else
        scaledSize = imageSize;

    srcImageRep.size = scaledSize;

    NSImage *newImage = [[NSImage alloc] initWithSize:scaledSize];

    [newImage lockFocus];

    thumbnailRect.origin = NSZeroPoint;
    thumbnailRect.size = scaledSize;

    [srcImageRep drawInRect:thumbnailRect];

    [newImage unlockFocus];
    return newImage;
}

这使用NSImageRep,在这种情况下出现以减少内存占用。在使用缩放到32x32的全屏桌面图像的示例运行中,上面徘徊在16Mb左右,而原始NSImage版本稳定地增长到32Mb。 YMMV当然。

HTH