如何修复 CFRuntimeCreateInstance 对象泄漏?
使用仪器泄漏工具分析我的应用程序时,我遇到了看不见的对象泄漏(CFRuntimeCreateInstance)。我对泄漏发生的地方有粗略的想象,但无法发现它:)调用树将我指向从精灵表加载图像的类方法。下面是调用树的一个片段(直到泄漏对象 CFRuntimeCreateInstance):
+[ImageCache loadImageOfType:andIndex:]
+[NSString stringWithFormat:]
_CFStringCreateWithFormatAndArgumentsAux
CFStringCreateCopy
__CFStringCreateImmutableFunnel3
_CFRuntimeCreateInstance
我正在使用这个辅助类方法来加载和缓存图像。该方法使用静态 NSMutableDictionary 来推送加载的图像以供进一步使用。我的图像被分组到精灵表中,因此该方法读取相应的图像文件并读取相应的图像区域。这是我的方法:
+(UIImage*)loadImageOfType:(int)type andIndex:(int)index{
UIImage *image;
if (!dict) dict = [[NSMutableDictionary dictionary] retain];
//First, trying to get already loaded image
int ind = IMAGECOUNT * type + index; //calculating the index that is used for storing image in dict
image = [dict objectForKey:[NSString stringWithFormat:@"%d", ind]]; //trying to get image
if (!image){ //if image is not loaded then read the spriteimage file
NSString *spritesheetName = [NSString stringWithFormat:@"itemsprite%d.png", type];
NSString* imagePath = [[NSBundle mainBundle] pathForResource:spritesheetName ofType:nil];
image = [UIImage imageWithContentsOfFile:imagePath];
if (image) //if spritesheet exists
{
int loopInd;
CGFloat x = 0, y = 0;
//load all images from it
for (int i=0; i<IMAGECOUNT; i++) {
UIImage *img;
loopInd = IMAGECOUNT * type + i;
CGImageRef imageRef = CGImageCreateWithImageInRect( [image CGImage], CGRectMake(x, y, ITEMIMAGESIZE, ITEMIMAGESIZE));
img = [UIImage imageWithCGImage:imageRef];
[dict setObject:img forKey:[NSString stringWithFormat:@"%d", loopInd]];
CGImageRelease(imageRef);
x += ITEMIMAGESIZE;
}
}
//set image var to be image needed
image = [dict objectForKey:[NSString stringWithFormat:@"%d", ind]];
}
return image;
}
有什么建议,从哪里开始?
更新:我在调试区域也有很多消息,所有消息都非常相似:
__NSAutoreleaseNoPool(): Object 0x4c55e80 of class NSCFNumber autoreleased with no pool in place - just leaking
__NSAutoreleaseNoPool(): Object 0x4c69060 of class __NSCFDictionary autoreleased with no pool in place - just leaking
__NSAutoreleaseNoPool(): Object 0x4c6a620 of class NSCFString autoreleased with no pool in place - just leaking
__NSAutoreleaseNoPool(): Object 0x4e75fe0 of class NSPathStore2 autoreleased with no pool in place - just leaking
__NSAutoreleaseNoPool(): Object 0x4e312d0 of class UIImage autoreleased with no pool in place - just leaking
I'm having unseen object leak (CFRuntimeCreateInstance) when profiling my app with instrument leak tool. I have rough imagination where the leak occurs but unable to spot it :) The call tree points me to my class method for loading an image from a spritesheet. Here is a fragment of the call tree (until leaking object CFRuntimeCreateInstance):
+[ImageCache loadImageOfType:andIndex:]
+[NSString stringWithFormat:]
_CFStringCreateWithFormatAndArgumentsAux
CFStringCreateCopy
__CFStringCreateImmutableFunnel3
_CFRuntimeCreateInstance
I'm using this helper class method for loading and caching the image. The method uses static NSMutableDictionary where it pushes loaded image for further use. My images are grouped into spritesheets, so the method reads corresponding image file and reads corresponding image area. Here is my method:
+(UIImage*)loadImageOfType:(int)type andIndex:(int)index{
UIImage *image;
if (!dict) dict = [[NSMutableDictionary dictionary] retain];
//First, trying to get already loaded image
int ind = IMAGECOUNT * type + index; //calculating the index that is used for storing image in dict
image = [dict objectForKey:[NSString stringWithFormat:@"%d", ind]]; //trying to get image
if (!image){ //if image is not loaded then read the spriteimage file
NSString *spritesheetName = [NSString stringWithFormat:@"itemsprite%d.png", type];
NSString* imagePath = [[NSBundle mainBundle] pathForResource:spritesheetName ofType:nil];
image = [UIImage imageWithContentsOfFile:imagePath];
if (image) //if spritesheet exists
{
int loopInd;
CGFloat x = 0, y = 0;
//load all images from it
for (int i=0; i<IMAGECOUNT; i++) {
UIImage *img;
loopInd = IMAGECOUNT * type + i;
CGImageRef imageRef = CGImageCreateWithImageInRect( [image CGImage], CGRectMake(x, y, ITEMIMAGESIZE, ITEMIMAGESIZE));
img = [UIImage imageWithCGImage:imageRef];
[dict setObject:img forKey:[NSString stringWithFormat:@"%d", loopInd]];
CGImageRelease(imageRef);
x += ITEMIMAGESIZE;
}
}
//set image var to be image needed
image = [dict objectForKey:[NSString stringWithFormat:@"%d", ind]];
}
return image;
}
Any suggestions, where to start?
UPDATE: I'm also having a lot of messages in debug area, all are quite similar:
__NSAutoreleaseNoPool(): Object 0x4c55e80 of class NSCFNumber autoreleased with no pool in place - just leaking
__NSAutoreleaseNoPool(): Object 0x4c69060 of class __NSCFDictionary autoreleased with no pool in place - just leaking
__NSAutoreleaseNoPool(): Object 0x4c6a620 of class NSCFString autoreleased with no pool in place - just leaking
__NSAutoreleaseNoPool(): Object 0x4e75fe0 of class NSPathStore2 autoreleased with no pool in place - just leaking
__NSAutoreleaseNoPool(): Object 0x4e312d0 of class UIImage autoreleased with no pool in place - just leaking
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我发现了问题。在游戏开始时,我会预加载图像。我有一个方法“loadGameResourses”,它循环并对单个图像加载方法“loadImageOfType...”进行多次调用。现在,我正在单独的线程中执行“loadGameResourses”的调用:
当我将其替换为:我听说你需要在主线程中处理 UIKit 的东西时,泄漏就消失了
,所以看起来我已经选择了使用这种适当的方法时,一切都会按预期进行:
I found the problem. At the beginning of the game, I'm preloading the images. I have a method "loadGameResourses" that loops and does multiple calls to single image loading method "loadImageOfType...". Now, I'm performing the call of ""loadGameResourses" in separate thread:
The leaks just disappeared when I replaced that with:
I heard that you need to deal with UIKit stuff in the main thread, so it looks like I have picked the wrong method. Everything works as expected when using this appropriate method:
我听说仪器的泄漏工具可以报告错误的泄漏。您是否尝试过在应用程序运行时打开活动监视器并观察内存?如果存在泄漏,内存量就会不断变大。在挖得太深之前确保确实有泄漏。
I have heard that the instrument's leak tool can report false leaks. Have you tried opening Activity Monitor while your app is running and watching the memory? If there is a leak, the amount of memory will keep getting larger and larger. Make sure you actually have a leak before you dig too deep.