ich habe diese Nachricht vom Debugger erhalten:
Pixture(1257,0xa0610500) malloc: *** error for object 0x21a8000: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
also habe ich ein bisschen nachgezeichnet und bekam:
(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
und ich kann wirklich nicht verstehen, was ich falsch mache. Hier ist der Code der Funktion [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;
}
die andere zuvor aufgerufene benutzerdefinierte Funktion ist die folgende:
- (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;
}
Ich hatte das gleiche Problem mit vielen gleichen Symptomen:
Wenn ich das Build-Ziel auf 3.1 ändere, verschwinden die Fehler im Simulator. Wenn ich den Code auf dem Gerät ausführe, werden die Fehler nicht angezeigt. Möglicherweise ein Bug in 3.0
Mein Rat ist, mit 3.1 als Ziel zu testen, und wenn Sie möchten, können Sie für die Veröffentlichung 3.0 erstellen und sich keine Sorgen über die Fehler machen, da diese auf dem Gerät nicht auftreten.
Es sieht so aus, als ob Sie einen beschädigten Speicherfehler haben, der wahrscheinlich nicht in diesem Code enthalten ist. Beschädigte Speicherbugs sind meine zweitspaßigsten Bugs, zum Teil, weil sie oft nicht deterministisch sind und die Symptome (auch Abstürze genannt) normalerweise lange nach auftreten.
Es gibt zwei Haupttypen von Speicherfehlern:
In diesem Fall scheint es, als ob Sie zu viel Zeit haben. Dies ist der leichtere Fall (b/c kann früher abstürzen), aber der schwierigere, ihn zu finden.
Hier ist eine Strategie, die Sie verwenden können, um zusätzliche Aufhebungen zu finden:
Im Idealfall finden Sie einen einzigen Befehl zum Aufheben der Zuweisung, mit dem Ihr Code nach dem Entfernen ordnungsgemäß funktioniert. Es kann nützlich sein, einen binären Suchansatz zu verwenden, wenn dies für Ihre Codebasis sinnvoll ist. Wenn es sich um eine große Codebasis handelt, verwenden Sie hoffentlich die Versionskontrolle und können versuchen, sich auf die neuesten Unterschiede zu konzentrieren.
Beachten Sie, dass die Freigabe auf verschiedene Arten hier aufgerufen werden kann. Höchstwahrscheinlich rufen Sie release
und autorelease
für Objekte auf. Es ist auch möglich, dass Sie explizit dealloc
aufrufen (was jedoch normalerweise ein Fehler ist). Und natürlich können Sie auch explizit free
direkt aufrufen.
Sobald Sie die zusätzlichen Zuordnungen beseitigt haben, ist es eine gute Idee, auch nach Speicherlecks (auch als zusätzliche Zuordnungen bezeichnet) zu suchen. Sie können dazu Instrumente und andere Werkzeuge verwenden. Ein guter Anfang ist das Lesen von Auffinden von Speicherlecks im iPhone-Entwicklungshandbuch.
Hinzugefügt: Es ist auch eine gute Idee, einen Zeiger auf nil
zu setzen, unmittelbar nachdem Sie ihn freigegeben und damit fertig sind. Auf diese Weise führt ein späterer Aufruf von [objectPtr release];
zu nichts.
(PS Übrigens, mein am meisten Spaß machender Fehlertyp ist die Speicherbeschädigung in Multithread-Code. Ich hatte einen davon einmal in einer Multimillionen-Zeilencodebasis.)
Wahrscheinlich ist dies nicht die Ursache für Ihren Absturz, aber Sie verlieren Speicher, indem Sie die Core Foundation-Objekte context2
, unmasked
und mask
nicht mithilfe von CFRelease()
, CFImageRelease()
oder dergleichen freigeben.
Ich hatte das gleiche Problem mit dem folgenden Code.
-(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; }
Ich habe die Funktion von der Open Source Twitterfon App übernommen.
Als ich versuchte, das Problem zu beheben, habe ich versucht, die letzte Zeile in zu ändern
self.imageView.image = [converted retain]
Und das stoppte die Fehlermeldungen in der Konsole. Ich werde das in Kürze in Leaks nachprüfen, um zu sehen, was passiert.
Ich habe mit dem gleichen Fehler in meinem Code zu kämpfen. Was mich verwundert hat, ist, dass meine App unter OS 3.0 problemlos funktioniert hat, bis ich eine geringfügige Änderung an einem Code vorgenommen habe, der nichts mit CGImage * zu tun hat. Aber wenn es einmal versagt hat, hat es nie ohne Absturz funktioniert. Als ich auf 3.1 umgestiegen bin, hat alles wieder funktioniert. Ich habe den Fehler auf einen Aufruf von CGImageRelease () eingegrenzt. Entweder das Entfernen dieser Zeile oder das Hinzufügen einer Beibehaltung für das resultierende UIImage löste das Problem - obwohl dies keine Lösung ist, da die App Speicherlecks verursachen wird.
Ich habe versucht, NSZombie mit Instrumenten zu benutzen. Das hat nicht geholfen - die App ist abgestürzt, ohne dass Zombies erkannt wurden.
Darüber hinaus stürzen Apples eigene Beispiel-Apps (wie TheElements) NICHT ab, sondern verwenden denselben EXAKTEN Code wie meine App. Ich kämpfe also darum, das Problem zu akzeptieren, das in den Frameworks liegt. Im Moment wechsle ich zu 3.1 und gehe weiter.
Ich wollte nur noch einmal bestätigen, dass ich Folgendes bekommen hatte:
Zeiger freigegeben wurde nicht zugeordnet
fehler und es ging weg, wenn ich mein Ziel OS zu 3.1 im Gegensatz zu 3.0 ändere
Es könnte nur ich sein, aber Sie können das Folgende nicht tun?
UIImage *whiteMasked = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return whiteMasked;
whiteMasked wird auf dem Stapel der Funktion zugewiesen und nach der Rückkehr ist der Zeiger nicht mehr gültig? Oder fehlt mir etwas? Wenn Sie das zurückgegebene UIImage * verwenden, kann nicht garantiert werden, dass es noch vorhanden ist. (Es wäre ein Hit und Miss). Müssen Sie ein UIImage * nicht zuweisen und dann automatisch freigeben, bevor Sie zurückkehren?