我正在处理一个执行图像处理并显示结果图像的应用。我使用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<IDecoder> sDecoder; in Reader.m before @implementation Reader.
return [result bitmap];
}
retun nil;
}
IDCoder
是
@protocol IDecoder <NSObject>
- (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
会将mBitmap
提取到mData
,然后与子resultHolder
的{{1}}合并以获取图像。如果我在主线程中加载图像,这些方法效果很好,但如果我使用GCD或mData
在后台加载图像,则很容易崩溃,尤其是在后台加载复杂图像时。当应用程序崩溃时,主线程声明CGSConvertBGR888toRGBA8888方法错误,其他一个线程正在运行NSThread
方法,实际上是[ResultHolder dealloc]
。似乎加载线程和主线程之间存在内存冲突。
当应用崩溃时,错误是这样的:
我已经为这个问题奋斗了好几天,但仍然无法找到解决方法。 我希望有人可以帮助我。 任何建议都表示赞赏。
更新: 我制作了一个演示项目ReaderDemo来模拟情况。如果您有兴趣,可以下载查看错误。这个项目中有15个图像,5,7,14个图像在滚动时会导致崩溃,它们比其他图像有点复杂。但是如果你滚动缩略图滚动视图然后点击,它们都可以显示。
答案 0 :(得分:3)
你有很多问题,但我们先从我找到的第一个开始:
测试不当
if (index > [mPageNames count]) {
那需要是&gt; =或者你崩溃。
你在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
后,需要NULL
输出mData
并让数据提供程序处理删除。
执行此操作的方法是在过去的参数中提供指向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
);
所有这些功能只需要调用free(data);
。因此,每当数据提供者完成分配的内存时,它就会释放它(你不需要担心它)。
答案 1 :(得分:-2)
如果要在启用ARC的环境中的任何类中释放()或释放资源,则必须在“构建阶段”中为类设置适当的标记。为此,在XCode中选择项目文件,选择目标,转到“构建阶段”部分,找到您的类,并为该类添加-fno-objc-arc
标志。
或者,也许是另一个原因,你正在调用一些必须在另一个线程中从主线程调用的CoreGraphics函数。