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;
}
J'ai eu le même problème, avec beaucoup des mêmes symptômes:
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.
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:
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:
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.)
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.
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.
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.
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
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?