ARC中的这个dealloc出了什么问题?

我正在开发一个进行图像处理并显示结果图像的应用程序。 我使用UIScrollView让用户滚动所有图像,因为图像不是标准的jpg或png,加载需要时间。 我在完成调度到主队列显示时使用GCD异步加载。 摘录如下:

 - (void)loadImage:(NSString *)name { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ UIImage *image = [Reader loadImage:name]; dispatch_sync(dispatch_get_main_queue(), ^{ [self displayImage:image]; }); }); } 

Reader的loadImage方法是这样的:

 + (UIImage *)loadImage:(NSString *)name { UInt8 *data = NULL; NSString *mfjPath = [TMP stringByAppendingPathComponent:name]; NSData *mfjData = [NSData dataWithContentsOfFile:mfjPath]; if(mfjData){ data = malloc(sizeof(UInt8)*mfjData.length); [mfjData getBytes:data]; } if(data){ ResultHolder *result = [sDecoder decodeData:data withOffset:0];// static id sDecoder; in Reader.m before @implementation Reader. return [result bitmap]; } retun nil; } 

IDCoder是一个协议

 @protocol IDecoder  - (ResultHolder *)decodeData:(UInt8 *) withOffset:(int)offset; @end 

ResultHolder是一个加载简单图像并组合复杂图像的类。 如下:

ResultHolder.h

 typedef struct color24{ UInt8 R; UInt8 G; UInt8 B; } Color24; @interface ResultHolder : NSObject { unsigned long mWidth; unsigned long mHeight; UInt8 *mData; CGImageRef mBitmap; BOOL isMonoColor; Color24 mMonoColor; } + (ResultHolder *)resultHolderWithCGImage:(CGImageRef)image; + (ResultHolder *)resultHolderWithData:(UInt8 *)data Width:(unsigned long)width andHeight:(unsigned long)height; + (ResultHolder *)resultHolderWithMonoColor:(Color24)monoColor withWidth:(unsigned long)width andHeight:(unsigned long)height; - (ResultHolder *)initWithData:(UInt8 *)data Width:(unsigned long)width andHeight:(unsigned long) height; - (ResultHolder *)initWithCGImage:(CGImageRef)image; - (ResultHolder *)initWithMonoColor:(Color24)monoColor withWidth:(unsigned long)width andHeight:(unsigned long)height; - (BOOL)isSuccess; - (UIImage *)bitmap; - (void)combineFixResultHolder:(ResultHolder *)child Rect:(CGRect)bounds Width:(unsigned long)width andHeight:(unsigned long)height; - (void)combineResultHolder:(ResultHolder *)child Bounds:(CGRect)bounds Width:(unsigned long)width andHeight:(unsigned long)height; @end 

ResultHolder.m

 @implementation ResultHolder @synthesize width = mWidth; @synthesize height = mHeight; @synthesize isMonoColor; @synthesize monoColor = mMonoColor; - (ResultHolder *)initWithData:(UInt8 *)data Width:(unsigned long)width andHeight:(unsigned long)height { if (self = [super init]) { mWidth = width; mHeight = height; mData = malloc(mWidth*mHeight*sizeof(Color24)); memcpy(mData, data, mWidth*mHeight*sizeof(Color24)); mBitmap = NULL; } return self; } - (ResultHolder *)initWithCGImage:(CGImageRef)image { if (self = [super init]) { mBitmap = CGImageRetain(image); mWidth = CGImageGetWidth(image); mHeight = CGImageGetHeight(image); } return self; } - (ResultHolder *)initWithMonoColor:(Color24)monoColor withWidth:(unsigned long)width andHeight:(unsigned long)height { if (self = [super init]) { mMonoColor = monoColor; isMonoColor = YES; mWidth = width; mHeight = height; mBitmap = NULL; mData = NULL; } return self; } + (ResultHolder *)resultHolderWithCGImage:(CGImageRef)image { ResultHolder *resultHolder = [[ResultHolder alloc] initWithCGImage:image]; return resultHolder; } + (ResultHolder *)resultHolderWithData:(UInt8 *)data Width:(unsigned long)width andHeight:(unsigned long)height { ResultHolder *resultHolder = [[ResultHolder alloc] initWithData:data Width:width andHeight:height]; return resultHolder; } + (ResultHolder *)resultHolderWithMonoColor:(Color24)monoColor withWidth:(unsigned long)width andHeight:(unsigned long)height { ResultHolder *resultHolder = [[ResultHolder alloc] initWithMonoColor:monoColor withWidth:width andHeight:height]; return resultHolder; } - (BOOL)isSuccess { if ([ReaderConfigures CodecDebug]) NSLog(@"ResultHolder isSuccess"); return (mData != NULL || isMonoColor || mBitmap != nil); } - (void)fillMonoColor { if (isMonoColor) { if (mData) { free(mData); } mData = (UInt8 *)malloc(mWidth*mHeight*sizeof(Color24)); for (int i = 0; i < mHeight; i++) { for (int j = 0; j < mWidth; j++) { memcpy(mData+(i*mWidth+j)*3, &mMonoColor, sizeof(Color24)); } } isMonoColor = NO; } } - (void)extractBitmap { if (mBitmap) { CGDataProviderRef dataProvider = CGImageGetDataProvider(mBitmap); CFDataRef bitmapData = CGDataProviderCopyData(dataProvider); UInt8 * dataSource = (UInt8 *)CFDataGetBytePtr(bitmapData); size_t width = CGImageGetWidth(mBitmap); size_t height = CGImageGetHeight(mBitmap); if(mData) free(mData); mData = malloc(width*height*3); for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { memcpy(mData+(i*width+j)*3, dataSource+(i*width+j)*4, sizeof(Color24)); } } CFRelease(bitmapData); CGImageRelease(mBitmap); mBitmap = NULL; } } - (UInt8 *)getRawData { if (mBitmap) { [self extractBitmap]; } if (isMonoColor) { [self fillMonoColor]; } return mData; } - (UIImage *)bitmap { if (mBitmap) { UIImage *image = [[UIImage alloc] initWithCGImage:mBitmap]; CGImageRelease(mBitmap); mBitmap = NULL; return image; } if (isMonoColor) { [self fillMonoColor]; } if (mData) { CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, mData, mWidth*mHeight*3, NULL); CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGImageRef bitmap = CGImageCreate(mWidth, mHeight, 8, 24, mWidth*3, colorSpace, kCGBitmapByteOrderDefault, dataProvider, NULL, YES, kCGRenderingIntentDefault); CGColorSpaceRelease(colorSpace); CGDataProviderRelease(dataProvider); UIImage *image = [[UIImage alloc] initWithCGImage:bitmap]; CGImageRelease(bitmap); return image; } return nil; } - (void)combineResultHolder:(ResultHolder *) child Bounds:(CGRect) bounds Width:(unsigned long)width andHeight:(unsigned long)height { CGRect rect = CGRectMake(MAX(0, bounds.origin.x), MAX(0, bounds.origin.y),MIN(width - 1, bounds.origin.x + bounds.size.width), MIN(height - 1, bounds.origin.y + bounds.size.height)); int w = MIN(rect.size.width + 1, child.width); int h = MIN(rect.size.height + 1, child.height); int dstPos = (height - 1 - (rect.origin.y + h - 1))*width; UInt8 *dataParent = [self getRawData]; if (child.isMonoColor) { Color24 childMonoColor = child.monoColor; for (int i = 0; i < h; i++) { memcpy(dataParent+(dstPos+(int)rect.origin.x)*3, &childMonoColor, w*3); dstPos += width; } } else { UInt8 *dataChild = [child getRawData]; if (dataChild != nil) { int srcPos = 0; for (int i = 0; i < h; i++) { memcpy(dataParent+dstPos*3+((int)rect.origin.x)*3, dataChild+srcPos*3, w*3); srcPos += child.width; dstPos += width; } } } } - (void)combineFixResultHolder:(ResultHolder *)child Rect:(CGRect)bounds Width:(unsigned long)width andHeight:(unsigned long)height { CGRect rect = CGRectMake(bounds.origin.x, height-1-bounds.origin.y-bounds.size.height, bounds.origin.x+bounds.size.width, height-1-bounds.origin.y); [self combineResultHolder:child Bounds:rect Width:width andHeight:height]; } - (void)dealloc { if (mData) { free(mData); mData = NULL; } if (mBitmap) { CGImageRelease(mBitmap); mBitmap = NULL; } } @end 

对于简单图像,例如仅JPEG图像, + (ResultHolder *)resultHolderWithCGImage:(CGImageRef)image;- (UIImage *)bitmap; 方法被称为。 对于一些复杂的, ResultHolder会将ResultHolder提取到mData ,然后与子resultHoldermData结合得到图像。 如果我在主线程中加载图像,这些方法效果很好,但如果我使用GCD或NSThread在后台加载图像,则很容易崩溃,尤其是在后台加载复杂图像时。 当应用程序崩溃时,主线程状态为CGSConvertBGR888toRGBA8888方法错误,其他一个线程正在运行[ResultHolder dealloc]方法,实际上是free(mData) 。 似乎加载线程和主线程之间存在内存冲突。

当应用程序崩溃时,错误是这样的: 在此处输入图像描述

我已经为这个bug挣扎了几天,但仍然找不到如何修复它。 我希望有人可以帮助我。 任何建议表示赞赏。

更新:我制作一个演示项目ReaderDemo来模拟情况。 如果您有兴趣,可以下载查看错误。 这个项目中有15个图像,5,7,14个图像在滚动时会导致崩溃,它们比其他图像有点复杂。 但如果您滚动缩略图滚动视图然后单击,它们都可以显示。

你有很多问题但我们从第一个发现开始:

  1. 测试不当

     if (index > [mPageNames count]) { 

    那需要> =或你崩溃。

  2. 你在mainQueue上调用dispatch_sync,这似乎不是一个好的决定(但也许你有一个非常好的) – 我把它改为异步,似乎工作正常

  3. 如果您在此项目中启用例外,它将真正帮助您。 单击Xco​​de工具栏中的Break Points按钮。 然后选择BreakPoints选项左侧窗格,右侧第二个。 点击左下方的“+”图标,然后添加一个All Exceptions断点。 现在,当您运行调试器时会停止发生问题的位置。

  4. 我得到了最后的崩溃,我会让你解决:

     2012-09-26 08:55:12.378 ReaderDemo[787:11303] MFJAtIndex index out of bounds,index:15,bounds:15 2012-09-26 08:55:12.379 ReaderDemo[787:11303] *** Assertion failure in -[ImageScrollView showLoadingForMFJ:], /Volumes/Data/Users/dhoerl/Downloads/ReaderDemo/ReaderDemo/ImageScrollView.m:247 

这应该可以帮助你。


编辑:您的问题涉及mData内存的管理。 您正尝试在代码中管理它的生命周期,但此管理不会与尝试使用它的CGImageDataProvider 。 崩溃几乎是肯定的(这意味着我99.99%确信)由CGImageProvided创建的CGDataProviderCreateWithData的副产品试图在你的类释放dealloc中的内存后访问数据。 我有过与数据提供者类似的经历。

正确的解决方案是删除所有free(data)调用,或至少删除大部分调用。 鉴于代码的当前结构,您需要仔细考虑这一点 – 您可能希望用标志替换所有测试和malloc / frees。 最后,你想要做的是一旦将内存指针交给ovdr到CGDataProviderCreateWithData ,你需要将mData NULL出来并让数据提供者处理删除。

这样做的方法是在过去的参数中提供一个指向CGDataProviderCreateWithData的函数指针:

 CGDataProviderReleaseDataCallback A callback function that releases data you supply to the function CGDataProviderCreateWithData. typedef void (*CGDataProviderReleaseDataCallback) ( void *info, const void *data, size_t size ); 

所有function需要做的就是free(data);呼叫free(data); 。 因此,每当数据提供者完成分配的内存时,它就会释放它(你不必担心它)。

如果要在启用ARC的环境中的任何类中释放()或释放资源,则必须在“构建阶段”中为类设置适当的标记。 为此,在XCode中选择项目文件,选择目标,转到“构建阶段”部分,找到您的类,并为该类添加-fno-objc-arc标志。 或者,也许是另一个原因,您正在调用一些必须仅在另一个线程中从主线程调用的CoreGraphics函数。