In my project (Cocoa) I need to compare the visual similarity of two NSView
. Here's the function for the comparison:
- (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);
});
});
}
}
When I run it I noticed huge memory leak, which can be over 2GB. In Xcode Instruments I found that these two lines take up most memory:
NSBitmapImageRep *bitmap1 = [_lennaImgView bitmapImageRepForCachingDisplayInRect:[_lennaImgView bounds]];
NSBitmapImageRep *bitmap2 = [backgroundView bitmapImageRepForCachingDisplayInRect:[backgroundView bounds]];
I've read some similar questions on SO but none of them seem to help.
Update
Rob suggest that I might be calling this function too frequently. Yes I am calling this repeatedly but I don't think I am calling it before the last call finished.
Here's how I use the function:
- (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];
}];
}
And I am not running this with zombie mode on