web-dev-qa-db-ja.com

iPhone開発:解放されるポインターが割り当てられなかった

私はデバッガーからこのメッセージを受け取りました:

_Pixture(1257,0xa0610500) malloc: *** error for object 0x21a8000: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
_

だから私は少しトレースして得ました:

_(gdb) Shell malloc_history 1257 0x21a8000_

ALLOC 0x2196a00-0x21a89ff [size=73728]: thread_a0610500 |start | main | UIApplicationMain | GSEventRun | GSEventRunModal | CFRunLoopRunInMode | CFRunLoopRunSpecific | __CFRunLoopDoObservers | CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*) | CA::Transaction::commit() | CA::Context::commit_transaction(CA::Transaction*) | CALayerDisplayIfNeeded | -[CALayer _display] | CABackingStoreUpdate | backing_callback(CGContext*, void*) | -[CALayer drawInContext:] | -[UIView(CALayerDelegate) drawLayer:inContext:] | -[AvatarView drawRect:] | -[AvatarView overlayPNG:] | +[UIImageUtility createMaskOf:] | UIGraphicsGetImageFromCurrentImageContext | CGBitmapContextCreateImage | create_bitmap_data_provider | malloc | malloc_zone_malloc

そして、私は自分が間違っていることを本当に理解できません。 _[UIImageUtility createMaskOf:]_関数のコードは次のとおりです。

_+ (UIImage *)createMaskOf:(UIImage *)source {
    CGRect rect = CGRectMake(0, 0, source.size.width, source.size.height);
    UIGraphicsBeginImageContext(CGSizeMake(source.size.width, source.size.height));
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextTranslateCTM(context, 0, source.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);

    UIImage *original = [self createGrayCopy:source];

    CGContextRef context2 = CGBitmapContextCreate(NULL, source.size.width, source.size.height, 8, 4 * source.size.width, 
                                                   CGColorSpaceCreateDeviceRGB(), kCGImageAlphaNoneSkipLast);
    CGContextDrawImage(context2, CGRectMake(0, 0, source.size.width, source.size.height), original.CGImage);
    CGImageRef unmasked = CGBitmapContextCreateImage(context2);

    const float myMaskingColorsFrameColor[6] = { 1,256,1,256,1,256 };
    CGImageRef mask = CGImageCreateWithMaskingColors(unmasked, myMaskingColorsFrameColor);

    CGContextSetRGBFillColor (context, 256,256,256, 1);
    CGContextFillRect(context, rect);
    CGContextDrawImage(context, rect, mask);

    UIImage *whiteMasked = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return whiteMasked;
}
_

その前に呼び出された他のカスタム関数は次のとおりです。

_- (UIImage *)overlayPNG:(SinglePart *)sp {
    NSLog([sp description]);
    // Rect and context setup
    CGRect rect = CGRectMake(0, 0, sp.image.size.width, sp.image.size.height);
    NSLog(@"%f x %f", sp.image.size.width, sp.image.size.height);

    // Create an image of a color filled rectangle
    UIImage *baseColor = nil;
    if (sp.hasOwnColor) {
            baseColor = [UIImageUtility imageWithRect:rect ofColor:sp.color];
    } else {
        SinglePart *facePart = [editingAvatar.face.partList objectAtIndex:0];
        baseColor = [UIImageUtility imageWithRect:rect ofColor:facePart.color];
    }

    // Crete the mask of the layer
    UIImage *mask = [UIImageUtility createMaskOf:sp.image];
    mask = [UIImageUtility createGrayCopy:mask];

    // Create a new context for merging the overlay and a mask of the layer
    UIGraphicsBeginImageContext(CGSizeMake(sp.image.size.width, sp.image.size.height));
    CGContextRef context2 = UIGraphicsGetCurrentContext(); 

    // Adjust the coordinate system so that the Origin 
    // is in the lower left corner of the view and the 
    // y axis points up 
    CGContextTranslateCTM(context2, 0, sp.image.size.height); 
    CGContextScaleCTM(context2, 1.0, -1.0); 

    // Create masked overlay color layer
    CGImageRef MaskedImage = CGImageCreateWithMask (baseColor.CGImage, mask.CGImage);

    // Draw the base color layer
    CGContextDrawImage(context2, rect, MaskedImage);

    // Get the result of the masking
    UIImage* overlayMasked = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    UIGraphicsBeginImageContext(CGSizeMake(sp.image.size.width, sp.image.size.height));
    CGContextRef context = UIGraphicsGetCurrentContext();

    // Adjust the coordinate system so that the Origin 
    // is in the lower left corner of the view and the 
    // y axis points up 
    CGContextTranslateCTM(context, 0, sp.image.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);

    // Get the result of the blending of the masked overlay and the base image
    CGContextDrawImage(context, rect, overlayMasked.CGImage);

    // Set the blend mode for the next drawn image
    CGContextSetBlendMode(context, kCGBlendModeOverlay);

    // Component image drawn
    CGContextDrawImage(context, rect, sp.image.CGImage);

    UIImage* blendedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    CGImageRelease(MaskedImage);

    return blendedImage;
}
_
19
w4nderlust

私は同じ問題を抱えていましたが、同じ症状がたくさんありました:

  • 解放されているポインタに割り当てられていませんエラー
  • xcode3.2にアップグレード
  • 画像のコードでエラーが発生しています

ビルドターゲットを3.1に変更すると、シミュレーターのエラーはなくなります。デバイスでコードを実行しても、エラーは表示されません。おそらく3.0のバグ

私のアドバイスは、ターゲットとして3.1を使用してテストすることです。必要に応じて、リリース用に3.0用にビルドでき、デバイスでは発生しないため、エラーについて心配する必要はありません。

27
nevan king

破損したメモリのバグがあるようですが、おそらくこのコードには含まれていません。破損したメモリバグは、私の2番目に楽しいバグです。これは、部分的には非決定論的であることが多く、症状(クラッシュ)は通常、実際のバグヒットのずっと後に発生します

メモリバグには主に2つのタイプがあります。

  1. あなたより多くを自由に割り当てる。
  2. 割り当てた以上の解放。

この場合、解放しすぎているように見えます。これは見やすいケースですが(b/cは早くクラッシュする可能性があります)、追跡するのは困難です。

追加の割り当て解除を見つけるために使用できる戦略は次のとおりです。

  • 割り当て解除の一部をオフにします。
  • それでもクラッシュが発生するかどうかを確認します。

理想的には、1つの割り当て解除コマンドを見つけることができます。これを削除すると、コードが正しく機能します。コードベースで実用的な場合は、バイナリ検索アプローチを試すと便利な場合があります。大規模なコードベースの場合は、バージョン管理を使用していて、最近の差分に焦点を当てることができれば幸いです。

ここでは、割り当て解除をいくつかの異なる方法で呼び出すことができることに注意してください。ほとんどの場合、オブジェクトに対してreleaseautoreleaseを呼び出しています。明示的にdeallocを呼び出している可能性もあります(ただし、これは通常は間違いです)。そしてもちろん、明示的にfreeを直接呼び出すこともできます。

余分な割り当て解除を取り除いたら、メモリリーク(別名追加割り当て)もチェックすることをお勧めします。これは、Instrumentsやその他のツールを使用して行うことができます。まず、iPhone開発ガイドの メモリリークの検索 を読むことから始めてください。

追加:リリースして使用を終えた直後に、ポインタをnilに設定することもお勧めします。このように、後で[objectPtr release];を呼び出しても、何も実行されません。

(PSところで、私の一番楽しいバグタイプはマルチスレッドコードのメモリ破損です。私は数百万行のコードベースに一度それらの1つを持っていました。)

22
Tyler

おそらくクラッシュの原因ではありませんが、CFRelease()CFImageRelease()などを使用してcontext2unmasked、およびmask CoreFoundationオブジェクトを解放しないことでメモリリークが発生しています。

3
Brad Larson

次のコードでも同じ問題が発生しました。

-(void)adjustImageToImageView:(UIImage*)img{

float numPixels = 100;
float radius = 5;
UIGraphicsBeginImageContext(CGSizeMake(numPixels, numPixels));
CGContextRef c = UIGraphicsGetCurrentContext();

CGContextBeginPath(c);
CGContextMoveToPoint  (c, numPixels, numPixels/2);
CGContextAddArcToPoint(c, numPixels, numPixels, numPixels/2, numPixels,   radius);
CGContextAddArcToPoint(c, 0,         numPixels, 0,           numPixels/2, radius);
CGContextAddArcToPoint(c, 0,         0,         numPixels/2, 0,           radius);
CGContextAddArcToPoint(c, numPixels, 0,         numPixels,   numPixels/2, radius);
CGContextClosePath(c);

CGContextClip(c);

[img drawAtPoint:CGPointZero];
UIImage *converted = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.imageView.image = converted;   }

オープンソースのTwitterfonアプリから関数を取得しました。

問題を解決しようとしたときに、最後の行を次のように変更してみました

self.imageView.image = [converted retain]

そして、それはコンソールのエラーメッセージを止めました。すぐにリークでこれをチェックして、何が起こっているかを確認します。

0
infiniteloop

私は自分のコードで同じエラーに苦しんでいます。私が困惑したのは、CGImage *とは関係のないコードに小さな変更を加えるまで、私のアプリがOS3.0で問題なく動作したことです。しかし、一度失敗し始めると、クラッシュせずに機能することはありませんでした。 3.1に切り替えると、すべてが再び機能しました。エラーをCGImageRelease()呼び出しに絞り込みました。その行を削除するか、結果のUIImageに保持を追加することで問題が解決しましたが、アプリがメモリリークを起こすため、これは解決策ではありません。

NSZombieを楽器で使ってみました。これは役に立ちませんでした-ゾンビが検出されずにアプリがクラッシュしました。

さらに、Apple独自のサンプルアプリ(TheElementsなど)はクラッシュしませんが、私のアプリと同じ正確なコードを使用します。だから、私は問題がフレームワークにあることを受け入れるのに苦労しています。今のところ、3.1に切り替えて先に進みます。

0
Ephraim

もう一度、次のことを確認したかっただけです。

解放されるポインタは割り当てられませんでした

エラーが発生し、ターゲットOSを3.0ではなく3.1に変更すると消えました

0
mracoker

私だけかもしれませんが、次のことはできませんか?

UIImage *whiteMasked = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

return whiteMasked;

whiteMaskedは関数のスタックに割り当てられ、それが返された後、ポインターは無効になりますか?それとも私は何かが足りないのですか?返されたUIImage *を使用する場合、それがまだ存在することは保証されません。 (それはヒットとミスになります)。 UIImage *を割り当ててから、戻る前に自動リリースする必要はありませんか?

0
dkardell