3

画像処理を行い、結果の画像を表示するアプリに取り組んでいます。ユーザーがすべての画像をスクロールできるようにするために使用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];
        });
    });
}

loadImageReaderのメソッドは次のようになります。

+ (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し、次に subとmData組み合わせて画像を取得します。これらのメソッドは、メイン スレッドでイメージをロードする場合はうまく機能しますが、GCD を使用するか、バックグラウンドでイメージをロードする場合、特に複雑なものをバックグラウンドでロードする場合にクラッシュしやすくなります。アプリがクラッシュすると、メイン スレッドは CGSConvertBGR888toRGBA8888 メソッド エラーを状態にします。他のスレッドの 1 つがメソッドを実行していますが、実際には. ロード スレッドとメイン スレッドの間でメモリの競合が発生しているようです。resultHoldermDataNSThread[ResultHolder dealloc]free(mData)

アプリがクラッシュすると、エラーは次のようになります。 ここに画像の説明を入力

このバグに何日も苦労しましたが、まだ修正方法が見つかりません。誰かが私を助けてくれることを願っています。任意の提案をいただければ幸いです。

更新:状況をシミュレートするデモ プロジェクトReaderDemoを作成します。興味がある場合は、ダウンロードしてエラーを確認できます。このプロジェクトには 15 枚の画像があり、5、7、14 枚の画像はスクロール時にクラッシュを引き起こします。他の画像よりも少し複雑です。ただし、サムネイルのスクロールビューをスクロールしてクリックすると、すべて表示されます。

4

2 に答える 2

3

いくつかの問題がありますが、最初に見つけた問題から始めましょう。

  1. 不適切なテスト

    if (index > [mPageNames count]) {
    

    それは >= である必要があり、さもないとクラッシュします。

  2. あなたはmainQueueでdispatch_syncを呼び出していますが、それは良い決定ではないようです(しかし、あなたは本当に良い決定をしているかもしれません)-私はそれをasyncに変更しました、うまくいくようです

  3. このプロジェクトで例外を有効にすると、本当に役に立ちます。Xcode ツールバーの [ブレーク ポイント] ボタンをクリックします。次に、BreakPoints オプションの左ペイン、右から 2 番目を選択します。左下の「+」アイコンをタップして、すべての例外ブレークポイントを追加します。デバッガーを実行すると、問題が発生した場所で停止します。

  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% 確信しています)、クラスが dealloc でそのメモリを解放した後にデータにアクセスしようとすることCGImageProvidedによって作成された副産物です。CGDataProviderCreateWithData私は、データ プロバイダーで同様の経験をしたことがあります。

free(data)適切な解決策は、すべての呼び出し、または少なくともほとんどの呼び出しを削除することです。コードの現在の構造を考えると、これについて慎重に考える必要があります。すべてのテストと malloc/frees をフラグに置き換えたいと思うかもしれません。最後に、メモリ ポインターが ovdr に渡されたら、データ プロバイダーに削除を処理させるCGDataProviderCreateWithData必要があります。NULLmData

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);です。そのため、データ プロバイダーは、割り当てられたメモリの処理が完了すると、メモリを解放します (これについて心配する必要はありません)。

于 2012-09-26T13:01:45.487 に答える
-2

ARC が有効な環境で任意のクラスのリソースを free() または解放する場合は、「ビルド フェーズ」でクラスに適切なフラグを設定する必要があります。これを行うには、XCode でプロジェクト ファイルを選択し、ターゲットを選択して、[ビルド フェーズ] セクションに移動し、クラスを見つけて、-fno-objc-arcそのクラスのフラグを設定します。または、おそらく別の理由として、別のスレッドでのみメインスレッドから呼び出す必要がある CoreGraphics 関数を呼び出しています。

于 2012-09-25T10:28:47.737 に答える