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
,然后与子resultHolder
的mData
结合得到图像。 如果我在主线程中加载图像,这些方法效果很好,但如果我使用GCD或NSThread
在后台加载图像,则很容易崩溃,尤其是在后台加载复杂图像时。 当应用程序崩溃时,主线程状态为CGSConvertBGR888toRGBA8888方法错误,其他一个线程正在运行[ResultHolder dealloc]
方法,实际上是free(mData)
。 似乎加载线程和主线程之间存在内存冲突。
当应用程序崩溃时,错误是这样的:
我已经为这个bug挣扎了几天,但仍然找不到如何修复它。 我希望有人可以帮助我。 任何建议表示赞赏。
更新:我制作一个演示项目ReaderDemo来模拟情况。 如果您有兴趣,可以下载查看错误。 这个项目中有15个图像,5,7,14个图像在滚动时会导致崩溃,它们比其他图像有点复杂。 但如果您滚动缩略图滚动视图然后单击,它们都可以显示。
你有很多问题但我们从第一个发现开始:
-
测试不当
if (index > [mPageNames count]) {
那需要> =或你崩溃。
-
你在mainQueue上调用dispatch_sync,这似乎不是一个好的决定(但也许你有一个非常好的) – 我把它改为异步,似乎工作正常
-
如果您在此项目中启用例外,它将真正帮助您。 单击Xcode工具栏中的Break Points按钮。 然后选择BreakPoints选项左侧窗格,右侧第二个。 点击左下方的“+”图标,然后添加一个All Exceptions断点。 现在,当您运行调试器时会停止发生问题的位置。
-
我得到了最后的崩溃,我会让你解决:
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函数。