1

在我的项目(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 的类似问题,但似乎都没有帮助。


更新

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];
    }];
}

而且我没有在僵尸模式下运行它

4

1 回答 1

1

我可以证明此问题的唯一方法是重复调用此例程,而无需等待先前的调用完成。在那种情况下,我有一个非常激进的增长曲线:

在此处输入图像描述

But if I gave this a bit of breathing room (i.e. rather than looping without stopping, I had the routine dispatch_async a call to itself upon completion, each signpost indicating a separate call to the routine), it was fine:

在此处输入图像描述

So either you are calling this routine too frequently and not giving the OS a chance to clean up these caches or maybe you have zombies or some other memory debugging option turned on. But the memory problem doesn't appear to be coming from this code, itself.

如果您不是在没有暂停的情况下调用它,也没有打开任何这些内存调试选项,我可能会建议使用“调试内存图”(请参阅​​ https://stackoverflow.com/a/30993476/1271826)功能确定任何强参考周期。此外,虽然这里似乎不太可能,但有时您会看到由于某些父对象正在泄漏而出现泄漏(因此请查找可能未释放的各种父类的实例,而不是关注正在泄漏的大项目)。

于 2016-12-30T22:16:06.057 回答