iPhone development: pointer being freed was not al

2019-01-10 11:50发布

问题:

i got this message from the debugger:

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

so i did a bit of tracing and got:

(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

and i really can't understand what i am doing wrong. here's the code of the [UIImageUtility createMaskOf:] function:

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

the other custom function called before that is the following:

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

回答1:

I've had the same problem, with a lot of the same symptoms:

  • pointer being freed was not allocated error
  • upgraded to Xcode 3.2
  • error happening in code for images

If I change the build target to 3.1, the errors in the simulator go away. If I run the code on the device, the errors don't appear. Possibly a bug in 3.0

My advice is to test with 3.1 as your target, and if you want, you can build for 3.0 for release and not worry about the errors as they don't happen on the device.



回答2:

It looks like you have a corrupted memory bug, and it is probably not in this code. Corrupted memory bugs are my second most-fun bugs, partially because they are often non-deterministic, and the symptoms (aka the crashes) usually happen long after the actual bug hit.

There are two main types of memory bugs:

  1. Allocating more than you free.
  2. Freeing more than you allocate.

In this case it looks like you're freeing too much, which is the easier case to see (b/c it can crash earlier) but the harder to track down.

Here is a strategy you can use to help find an extra deallocations:

  • Turn off some of your deallocations.
  • See if the crash still occurs.

Ideally, you can find one single deallocation command which, when removed, will allow your code to work correctly. It might be useful to try a binary search approach, if that is practical for your codebase. If it's a large codebase, hopefully you're using version control and you can try to focus on the recent diff's.

Keep in mind that deallocations can be invoked in several different ways here. Most likely, you're calling release and autorelease on objects. It's also possible you might be explicitly calling dealloc (which is usually a mistake, though). And of course you might even be explicitly calling free directly.

Once you've gotten rid of the extra deallocations, it's a good idea to also check for memory leaks (aka extra allocations). You can do this by using Instruments and other tools. A good place to start is by reading Finding Memory Leaks in the iPhone development guide.

Added: It's also a good idea to set a pointer to nil right after you've released it and are done using it. This way, if you call [objectPtr release]; later, it won't do anything.

(PS Btw, my #1 most-fun bug type is memory corruption in mutlithreaded code. I had one of those once in a multi-million line code base.)



回答3:

While probably not the cause of your crash, you are leaking memory by not releasing the context2, unmasked, and mask Core Foundation objects using CFRelease(), CFImageRelease(), or the like.



回答4:

I had the same issue with the following 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;   }

I took the function from the open source Twitterfon app.

When I was trying to fix the issue, I tried changing the last line to

self.imageView.image = [converted retain]

And that stopped the error messages in the console. I'm going to check this in Leaks soon to see whats happening.



回答5:

I just wanted to confirm, again, that I had was getting:

pointer being freed was not allocated

error and it went away if i change my target os to 3.1 as opposed to 3.0



回答6:

I've struggled with the same error in my code. What I was puzzled by is that my app worked in OS 3.0 without any problem until I made a minor modification to a code that has nothing to do with the CGImage* stuff. But once it started to fail, then it never worked without crashing. When I switched to 3.1, everything worked again. I narrowed down the error to a CGImageRelease() call. Either removing that line or adding a retain on the resulting UIImage resolved the problem--although this is a no solution since the app will be leaking memory.

I tried using NSZombie with instruments. This didn't help--the app crashed without any zombies being detected.

Furthermore, Apple's own example apps (like TheElements) does NOT crash but uses the same EXACT code as my app. So, I'm struggling to accept the problem lies in the Frameworks. For now, I am switching to 3.1 and moving on.



回答7:

It might be just me but you can't do the following ?

UIImage *whiteMasked = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

return whiteMasked;

whiteMasked is allocated on the stack of the function and after it returns the pointer is no longer valid? Or am I missing something? If you use the returned UIImage* it is not guaranteed to still be there. (it would be a hit and miss). Don't you need to alloc a UIImage* and then autorelease it before returning?