web-dev-qa-db-fra.com

développement iPhone

j'ai reçu ce message du débogueur:

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

donc j'ai fait un peu de traçage et obtenu:

(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

et je ne peux vraiment pas comprendre ce que je fais mal. voici le code de la fonction [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;
}

l'autre fonction personnalisée appelée auparavant est la suivante:

- (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

J'ai eu le même problème, avec beaucoup des mêmes symptômes:

  • le pointeur en cours de libération n'a pas été attribué d'erreur
  • mis à niveau vers Xcode 3.2
  • erreur se produisant dans le code pour les images

Si je modifie la cible de construction en 3.1, les erreurs dans le simulateur disparaissent. Si je lance le code sur le périphérique, les erreurs n'apparaissent pas. Peut-être un bug dans la version 3.0

Mon conseil est de tester avec 3.1 comme cible, et si vous le souhaitez, vous pouvez créer pour la version 3.0 sans vous soucier des erreurs car elles ne se produisent pas sur le périphérique. 

27
nevan king

Il semble que vous ayez un bogue de mémoire corrompu, et ce n’est probablement pas dans ce code. Les bogues de mémoire corrompus sont mes deuxièmes bogues les plus amusants, en partie parce qu'ils sont souvent non déterministes, et les symptômes (alias les accidents) se produisent généralement longtemps après le bogue rencontré.

Il existe deux principaux types de bugs de mémoire:

  1. Allouer plus que vous gratuitement.
  2. Libérer plus que vous n'allouez.

Dans ce cas, il semble que vous en libériez trop, ce qui est le cas le plus facile à voir (b/c, il peut planter plus tôt), mais le plus difficile à localiser.

Voici une stratégie que vous pouvez utiliser pour vous aider à trouver un désallocation supplémentaire:

  • Éteignez certaines de vos désallocations.
  • Voir si le crash se produit toujours.

Idéalement, vous pouvez trouver une seule commande de désallocation qui, une fois supprimée, permettra à votre code de fonctionner correctement. Il peut être utile d’essayer une approche de recherche binaire, si cela est pratique pour votre base de code. S'il s'agit d'une base de code volumineuse, j'espère que vous utilisez le contrôle de version et que vous pouvez essayer de vous concentrer sur les diffs récents.

Gardez à l'esprit que les désallocations peuvent être invoquées de différentes manières ici. Très probablement, vous appelez release et autorelease sur des objets. Il est également possible que vous appeliez explicitement dealloc (ce qui est généralement une erreur, cependant). Et bien sûr, vous pourriez même appeler explicitement directement free.

Une fois que vous vous êtes débarrassé des désallocations supplémentaires, il est judicieux de vérifier également les fuites de mémoire (c'est-à-dire les allocations supplémentaires). Vous pouvez le faire en utilisant des instruments et d’autres outils. Pour commencer, lisez/ Trouver les fuites de mémoire dans le guide de développement de l'iPhone.

Ajouté: C'est aussi une bonne idée de définir un pointeur sur nil juste après l'avoir publié et terminé. De cette façon, si vous appelez [objectPtr release]; plus tard, cela ne fera rien.

(PS Btw, mon type de bogue n ° 1 le plus amusant est la corruption de mémoire dans le code multithread. J'ai eu l'un de ceux-ci une fois dans une base de code de plusieurs millions de lignes.)

22
Tyler

Bien que ce ne soit probablement pas la cause de votre crash, vous perdez de la mémoire en ne libérant pas les objets context2, unmasked et mask Core Foundation à l'aide de CFRelease(), CFImageRelease() ou autre.

3
Brad Larson

J'ai eu le même problème avec le code suivant.

-(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;   }

J'ai pris la fonction de l'application open source Twitterfon.

Quand j’essayais de résoudre le problème, j’ai essayé de changer la dernière ligne en 

self.imageView.image = [converted retain]

Et cela a arrêté les messages d'erreur dans la console. Je vais vérifier cela dans Leaks bientôt pour voir ce qui se passe.

0
infiniteloop

J'ai eu du mal avec la même erreur dans mon code. Ce qui m'a rendu perplexe, c'est que mon application fonctionnait sous OS 3.0 sans problème jusqu'à ce que je modifie légèrement un code qui n'a rien à voir avec le contenu CGImage *. Mais une fois que cela a commencé à échouer, alors cela n'a jamais fonctionné sans s'écraser. Lorsque je suis passé à la version 3.1, tout fonctionnait à nouveau. J'ai réduit l'erreur à un appel CGImageRelease (). Le fait de supprimer cette ligne ou d'ajouter une retenue sur UIImage résultant a résolu le problème - bien que ce ne soit pas une solution, car l'application perdrait de la mémoire. 

J'ai essayé d'utiliser NSZombie avec des instruments. Cela n'a pas aidé - l'application s'est écrasée sans qu'aucun zombie ait été détecté. 

De plus, les exemples d’applications d’Apple (comme TheElements) ne plantent PAS, mais utilisent le même code EXACT que mon application. Donc, j'ai du mal à accepter que le problème réside dans les cadres. Pour l'instant, je passe à la version 3.1 et je continue.

0
Ephraim

Je voulais juste confirmer, encore une fois, que j'avais:

le pointeur en cours de libération n'a pas été attribué

erreur et il est parti si je change mon OS cible à 3.1 par opposition à 3,0

0
mracoker

C'est peut-être juste moi mais tu ne peux pas faire ce qui suit?

UIImage *whiteMasked = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

return whiteMasked;

whiteMasked est alloué sur la pile de la fonction et, une fois renvoyé, le pointeur n'est plus valide? Ou est-ce que je manque quelque chose? Si vous utilisez le UIImage * retourné, il n’est pas garanti d’y être toujours. (ce serait un hasard). N'avez-vous pas besoin d'allouer un UIImage * et de le relâcher automatiquement avant de revenir?

0
dkardell