Macos NSView cacheDisplayInRect:toBitmapImageRep:内存泄漏

Macos NSView cacheDisplayInRect:toBitmapImageRep:内存泄漏,macos,cocoa,memory-leaks,nsbitmapimagerep,Macos,Cocoa,Memory Leaks,Nsbitmapimagerep,在我的项目(Cocoa)中,我需要比较两个视图的视觉相似性。下面是用于比较的函数: - (void)compareWithHandler: (void (^)(CGFloat fitness)) handler { @autoreleasepool { __block CGFloat fitness = 0; __block NSInteger count = 0; NSBitmapImageRep *bitmap1 = [_lennaI

在我的项目(Cocoa)中,我需要比较两个视图的视觉相似性。下面是用于比较的函数:

- (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);
            });
        });
    }
}
仪器屏幕截图:

我读过一些类似的问题,但似乎都没有帮助


更新 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\u async
在完成后调用自己,每个路标都指示对例程的单独调用),这很好:

因此,要么你调用这个例程太频繁,没有给操作系统清理这些缓存的机会,要么你打开了僵尸或者其他一些内存调试选项。但是内存问题似乎不是源于代码本身


如果没有暂停调用此函数,或者没有打开任何内存调试选项,我建议使用“调试内存图”(请参阅)功能来识别任何强引用周期。此外,虽然在这里看起来不太可能,但有时您会看到泄漏,因为某些父对象正在泄漏(因此,请查找可能未发布的各种父类的实例,而不是关注泄漏的大项目)。

嗨,Rob,请检查更新的问题。我使用函数的方式正确吗?请注意,我不是在僵尸模式下运行的。或者,如果可能的话,你能告诉我你是如何“给这一点喘息空间”(通过GitHub gist或其他方式)的吗?我做了一些“一点喘息空间”的翻译,范围从
NSTimer
[NSThread sleepForTimeInterval]
,但最终会做一些类似于添加到修改后的问题中的
draw
方法的事情(即,从完成处理程序调用它)。如果您想特别小心,暂时而不是
dispatch\u async
,只是为了确保这不是一个计时问题。但是,归根结底,我不能用你的
draw
例子重现你的问题,除非我们能重现它,否则我们真的帮不了你。
- (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];
    }];
}