NSView cacheDisplayInRect:toBitmapImageRep: 内存泄漏

NSView cacheDisplayInRect:toBitmapImageRep: Memory Leak

在我的项目中(Cocoa)我需要比较两个NSView的视觉相似度。这是用于比较的函数:

- (void)compareWithHandler: (void (^)(CGFloat fitness)) handler {
    @autoreleasepool {
        __block CGFloat fitness = 0;
        __block NSInteger count = 0;

        NSBitmapImageRep *bitmap1 = [_lennaImgView bitmapImageRepForCachingDisplayInRect:[_lennaImgView bounds]];
        NSBitmapImageRep *bitmap2 = [backgroundView bitmapImageRepForCachingDisplayInRect:[backgroundView bounds]];

        [_lennaImgView cacheDisplayInRect:_lennaImgView.bounds toBitmapImageRep:bitmap1];
        [backgroundView cacheDisplayInRect:backgroundView.bounds toBitmapImageRep:bitmap2];

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSInteger w = [bitmap1 pixelsWide];
            NSInteger h = [bitmap1 pixelsHigh];
            NSInteger rowBytes = [bitmap1 bytesPerRow];
            unsigned char* pixels1 = [bitmap1 bitmapData];
            unsigned char* pixels2 = [bitmap2 bitmapData];

            int row, col;
            for (row = 0; row < h; row++){
                @autoreleasepool {
                    unsigned char* rowStart1 = (unsigned char*)(pixels1 + (row * rowBytes));
                    unsigned char* rowStart2 = (unsigned char*)(pixels2 + (row * rowBytes));
                    unsigned char* nextChannel1 = rowStart1;
                    unsigned char* nextChannel2 = rowStart2;
                    for (col = 0; col < w; col++){
                        unsigned char r1, g1, b1, a1, r2, g2, b2, a2;

                        r1 = *nextChannel1;
                        nextChannel1++;
                        g1 = *nextChannel1;
                        nextChannel1++;
                        b1 = *nextChannel1;
                        nextChannel1++;
                        a1 = *nextChannel1;
                        nextChannel1++;
                        r2 = *nextChannel2;
                        nextChannel2++;
                        g2 = *nextChannel2;
                        nextChannel2++;
                        b2 = *nextChannel2;
                        nextChannel2++;
                        a2 = *nextChannel2;
                        nextChannel2++;

                        unsigned char ary[] = {r1, r2, g1, g2, b1, b2};
                        double dist = vectorDistance(ary);
                        fitness += (CGFloat)map(dist, 0, 442, 1, 0);
                        count ++;
                    }
                }
            }
            fitness /= count;

            dispatch_async(dispatch_get_main_queue(), ^{
                handler(fitness);
            });
        });
    }
}

当我 运行 它时,我注意到巨大的内存泄漏,可能超过 2GB。在 Xcode Instruments 中,我发现这两行占用了大部分内存:

    NSBitmapImageRep *bitmap1 = [_lennaImgView bitmapImageRepForCachingDisplayInRect:[_lennaImgView bounds]];
    NSBitmapImageRep *bitmap2 = [backgroundView bitmapImageRepForCachingDisplayInRect:[backgroundView bounds]];

仪器中的屏幕截图:

我在 SO 上读过一些类似的问题,但 none 似乎有帮助。


更新

Rob 建议我可能过于频繁地调用此函数。是的,我反复调用它,但我不认为我在最后一次调用完成之前调用它。

以下是我使用函数的方式:

- (void)draw {      
    // Here I make some changes to the two NSView
    // Blablabla

    // Call the function
    [self compareWithHandler:^(CGFloat fitness) {
        // Use the fitness value to determine how we are going to change the two views

        [self draw];
    }];
}

而且我不会运行开启僵尸模式

我能证明这个问题的唯一方法是重复调用这个例程,而不是等待之前的调用完成。在那种情况下,我的增长曲线非常激进:

但是,如果我给它一点喘息的空间(即,我不是不停地循环,而是让例程 dispatch_async 在完成时调用自身,每个路标表示对例程的单独调用),很好:

所以要么你调用这个例程太频繁而没有给 OS 清理这些缓存的机会,要么你打开了僵尸程序或其他内存调试选项。但是内存问题似乎并不是来自这段代码本身。

如果你不是不停地调用它,也没有打开任何内存调试选项,我可能建议使用 "debug memory graph"(参见 )功能来识别任何强参考循环。此外,虽然在这里看起来不太可能,但有时您会看到泄漏,因为某些父对象正在泄漏(因此寻找可能未被释放的各种父对象 类 的实例,而不是专注于正在释放的大项目泄漏)。