NSZombieEnabled breaking working code? - iphone

I have the following method in UIImageManipulation.m:
+(UIImage *)scaleImage:(UIImage *)source toSize:(CGSize)size
{
UIImage *scaledImage = nil;
if (source != nil)
{
UIGraphicsBeginImageContext(size);
[source drawInRect:CGRectMake(0, 0, size.width, size.height)];
scaledImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
return scaledImage;
}
I am calling it in a different view with:
imageFromFile = [UIImageManipulator scaleImage:imageFromFile toSize:imageView.frame.size];
(imageView is a UIImageView allocated earlier)
This is working great in my code. I resizes the image perfectly, and throws zero errors. I also don't have anything pop up under build -> analyze. But the second I turn on NSZombieEnabled to debug a different EXC_BAD_ACCESS issue, the code breaks. Every single time. I can turn NSZombieEnabled off, code runs great. I turn it on, and boom. Broken. I comment out the call, and it works again. Every single time, it gives me an error in the console: -[UIImage release]: message sent to deallocated instance 0x3b1d600. This error doesn't appear if `NSZombieEnabled is turned off.
Any ideas?
--EDIT--
Ok, This is killing me. I have stuck breakpoints everywhere I can, and I still cannot get a hold of this thing. Here is the full code when I call the scaleImage method:
-(void)setupImageButton
{
UIImage *imageFromFile;
if (object.imageAttribute == nil) {
imageFromFile = [UIImage imageNamed:#"no-image.png"];
} else {
imageFromFile = object.imageAttribute;
}
UIImage *scaledImage = [UIImageManipulator scaleImage:imageFromFile toSize:imageButton.frame.size];
UIImage *roundedImage = [UIImageManipulator makeRoundCornerImage:scaledImage :10 :10 withBorder:YES];
[imageButton setBackgroundImage:roundedImage forState:UIControlStateNormal];
}
The other UIImageManipulator method (makeRoundCornerImage) shouldn't be causing the error, but just in case I'm overlooking something, I threw the entire file up on github here.
It's something about this method though. Has to be. If I comment it out, it works great. If I leave it in, Error. But it doesn't throw errors with NSZombieEnabled turned off ever.

The purpose of NSZombieEnabled is to detect messages that get sent to objects after they've been deallocated. The console error you're seeing is NSZombieEnabled telling you that a release message is being sent to a deallocated instance of UIImage. Usually a bug like this is the result of too many calls to release, or not enough calls to retain.
In this case, your scaleImage:toSize: method returns an autoreleased UIImage. The error message you're getting from NSZombieEnabled suggests that you may be releasing this object after it gets returned. This would explain your bug. When your autorelease pool drains it would try to release an object that's already been deallocated.
You're passing imageFromFile to scaleImage:toSize:, and then reassigning that same variable to the return value. There's nothing wrong with this idiom per se, but does require some extra care to avoid memory bugs like this one. You're overwriting your reference to the original object, so you either have to make sure it's autoreleased before the assignment, or save a separate reference that you can manually release after the assignment. Otherwise your original object will leak.

The error was due to a release going on in the makeRoundedCornerImage method from UIImageManipulator. Still not sure why it wasn't getting picked up without NSZombieEnabled turned on, but that's what it was.
You can find the offending line in the Gist I posted in the original question: Line 74.

Related

Potential leak of object when calling subtreebordercolor

I am getting memory leak warnings for these two methods. The second one calls the first one and apparently its leaking memory. Any ideas?
static UIColor *subtreeBorderColor(void)
{
return [UIColor colorWithRed:0.0f green:0.5f blue:0.0f alpha:1.0f];
}
- (void) updateSubtreeBorder
{
CALayer *layer = [self layer];
if (layer) {
// If the enclosing TreeGraph has its "showsSubtreeFrames" debug feature enabled,
// configure the backing layer to draw its border programmatically. This is much more efficient
// than allocating a backing store for each SubtreeView's backing layer, only to stroke a simple
// rectangle into that backing store.
PSBaseTreeGraphView *treeGraph = [self enclosingTreeGraph];
if ([treeGraph showsSubtreeFrames]) {
[layer setBorderWidth:subtreeBorderWidth()];
[layer setBorderColor:[subtreeBorderColor() CGColor]];
} else {
[layer setBorderWidth:0.0];
}
}
}
//3: Potential leak of an object
//6: Calling 'subtreeBorderColor'
//1: Entered call from 'updateSubtreeBorder'
//13: Method returns an Objective-C object with a +0 retain count
//12: Reference count incremented. The object now has a +1 retain count
//6: Returning from 'subtreeBorderColor'
//13: Object leaked: allocated object is not referenced later in this execution path and has a retain count of +1
UPDATE 2: I just completely changed the code and deleted temporary files and cleaned the solution and this is what I see - it is finding errors where there isn't even code
Simple. You don't need the call to - retain in the function. That's exactly what the autorelease pattern is invented for. Since you don't create the UIColor object using alloc-init, you don't take ownership of it. No need to superfluously complicate memory management further. :)
Edit: (to prevent future downvotes) now that you edited your question and code so that it no longer erroneously returns a retained object, the previous statement is no longer valid. Yes, Xcode shows this notice about the memory leak where "there isn't even code", and that's strange. Yes, perhaps a compiler bug. Still, a temporary (and in my opinion, perfectly valid) workaround is to simply use a define instead of a function. Let's see what Xcode says if you write this instead:
#define SUBTREE_BORDER_COLOR [UIColor colorWithRed:0.0f green:0.5f blue:0.0f alpha:1.0f]
If you want to be using "retain" the way you are, you need to also "release" the "UIColor" object you returned from your "subtreeBorderColor" method.
Were this my code, I wouldn't do "retain" on that autoreleased object to begin with. Or better than that, simply do my code with ARC enabled.
both pervious answers are right
but, if for any reason you need to retain an object being returned by your method, please follow the rules and rename your method starting with "new" (or alloc or retain or copy), so everybody (and you too) will know that the returned object is retained, so the calling code knows it's responsable to release it later, when needed...

Do I have a memory management problem in here?

Something must be wrong with this code right here:
+ (UIImage*)captureView:(UIView *)theView {
UIGraphicsBeginImageContext(theView.frame.size);
[theView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
When I use that, Xcode throws me this error message:
malloc: * error for object
0x103f000: pointer being freed was not
allocated
* set a breakpoint in malloc_error_break to debug
I can't see any memory management errors there. Does anyone else?
I had the same warning. But, it does not occur in 3.1 or above.
Don't see. Set NSZombie enabled in the build to track it down.
It might be related to returning an autoreleased UIImage from a class method which will probably be inside of a temporary autorelease pool. The image might be being destroyed by the draining of that pool. To test, move the method to an instance method and see if the problem goes away.

EXC_BAD_ACCESS on iPhone when using "obj != nil

I've got a very simple line of code in Objective-C:
if ((selectedEntity != nil) && [selectedEntity isKindOfClass:[MobileEntity class]])
Occasionally and for no reason I can tell, the game crashes on this line of code with an EXC-BAD-ACCESS. It usually seems to be about the time when something gets removed from the playing field, so I'm guessing that what was the selectedEntity gets dealloc'd, then this results. Aside from being impossible to select exiting Entities (but who knows, maybe this isn't actually true in my code...), the fact that I am specifically checking to see if there is a selectedEntity before I access it means that I shouldn't be having any problems here. Objective-C is supposed to support Boolean short-citcuiting, but it appears to not be EDIT: looks like short-circuiting has nothing to do with the problem.
Also, I put a #try/#catch around this code block because I knew it was exploding every once in a while, but that appears to be ignored (I'm guessing EXC-BAD-ACCESS can't be caught).
So basically I'm wondering if anybody either knows a way I can catch this and throw it out (because I don't care about this error as long as it doesn't make the game crash) or can explain why it might be happening. I know Objective-C does weird things with the "nil" value, so I'm guessing it's pointing to some weird space that is neither an object pointer or nil.
EDIT: Just to clarify, I know the below code is wrong, it's what I was guessing was happening in my program. I was asking if that would cause a problem - which it indeed does. :-)
EDIT: Looks like there is a fringe case that allows you to select an Entity before it gets erased. So, it appears the progression of the code goes like this:
selectedEntity = obj;
NSAutoreleasePool *pool = ...;
[obj release];
if (selectedEntity != nil && etc...) {}
[pool release];
So I'm guessing that because the Autorelease pool has not yet been released, the object is not nil but its retain count is at 0 so it's not allowed to be accessed anyway... or something along those lines?
Also, my game is single-threaded, so this isn't a threading issue.
EDIT: I fixed the problem, in two ways. First, I didn't allow selection of the entity in that fringe case. Second, instead of just calling [entities removeObjectAtIndex:i] (the code to remove any entities that will be deleted), I changed it to:
//Deselect it if it has been selected.
if (entity == selectedEntity)
{
selectedEntity = nil;
}
[entities removeObjectAtIndex:i];
Just make sure you are assigning nil to the variable at the same time you release it, as jib suggested.
This has nothing to do with short circuiting. Objective-C eats messages to nil, so the check for selectedEntity != nil isn't necessary (since messages-to-nil will return NO for BOOL return types).
EXC_BAD_ACCESS is not a catchable exception. It is a catastrophic failure generally caused by trying to follow in invalid pointer.
More likely than not, whatever object selectedEntity points to has been released before the code is executed. Thus, it is neither nil nor a valid object.
Turn on NSZombies and try again.
If your app is threaded, are you synchronizing selectedEntity across threads properly (keeping in mind that, in general, diddling the UI from secondary threads is not supported)?
Your post was edited to indicate that the fix is:
//Deselect it if it has been selected.
if (entity == selectedEntity)
{
selectedEntity = nil;
}
[entities removeObjectAtIndex:i];
This fixes the issue because the NSMutableArray will -release objects upon removal. If the retain count falls to zero, the object is deallocated and selectedEntity would then point to a deallocated object.
if an object (selectedEntity) has been released and dealloc'd it is not == nil. It is a pointer to an arbitrary piece of memory, and deferencing it ( if(selectedEntity!=nil ) is a Programming Error (EXC_BAD_ACCESS).
Hence the common obj-c paradigm:-
[selectedEntity release];
selectedEntity = nil;
i just read this http://developer.apple.com/mac/library/qa/qa2004/qa1367.html which indicated that the error you are getting is a result of over-releasing the object. this means that altough selectedEntity is nill, you released it to many times and it just not yours to use anymore..
Put a breakpoint on OBJC_EXCEPTION_THROW and see where it is really being thrown. That line should never throw a EXC_BAD_ACCESS on its own.
Are you perhaps doing something within the IF block that could cause the exception?
selectedEntity = obj;
NSAutoreleasePool *pool = ...;
[obj release];
if (selectedEntity != nil && etc...) {}
[pool release];
You have a dangling pointer or zombie here. The selectedEntity is pointing at obj which gets releases right before you reference selectedEntity. This makes selectedEntity non-nil but an invalid object so any dereference of it will crash.
You wanted to autorelease the obj rather than release it.

Why am i getting a EXC_BAD_ACCES

Hey. I have been working on a Twitter application and have been stuck on a EXC_ BAD_ ACCESS error for quite some time. I know that EXC_ BAD_ ACCESS is a memory issue but i cannot pinpoint where the problem is. Here is my code sample:
- (void)viewDidLoad {
[super viewDidLoad];
NSString *path = #"/Volumes/Schools/BHS/Student/740827/Documents/Forrest McIntyre CS193P/Presence2";
NSArray *propList = [NSArray arrayWithContentsOfFile:[NSBundle pathForResource:#"TwitterUsers" ofType:#"plist" inDirectory:path]];
people = [[NSMutableArray alloc]init];
for (NSString *name in propList) {
Person *p = [[Person alloc] initWithUserName: name];
[people addObject: p];
[p release];
}
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
The exception is thrown on the last brace after the comment. I believe that it is truly thrown in the for loop somewhere but just shows up upon exiting.
Here is the implementation file for Person:
#implementation Person
#synthesize image;
#synthesize username;
#synthesize displayName;
#synthesize statusArray;
-(id)initWithUserName:(NSString *)userName {
if(self = [super init])
{
self.username = userName;
NSDictionary *info = [TwitterHelper fetchInfoForUsername:userName];
self.displayName = [info objectForKey:#"name"];
NSLog([NSString stringWithFormat:#"%#",[info objectForKey:#"profile_image_url"]]);
NSString *imageURL2 = [NSString stringWithFormat:#"%#",[info objectForKey:#"profile_image_url"]];
self.image = [UIImage imageWithData: [NSData dataWithContentsOfURL: [NSURL URLWithString: imageURL2]]];
[info release];
self.statusArray = [TwitterHelper fetchTimelineForUsername:userName];
}
return self;
}
#end
Thanks for any help
EDIT: Here is the header file for PersonListViewController (the class that contains the ViewDidLoad).
This is just to show you where people is coming from.
#interface PersonListViewController : UITableViewController {
NSMutableArray *people;
}
#end
since you never retain propList or path you shouldn't be releasing them.
You should, however, release people
For an overview of memory management, see the Memory Management Programming Guide
For quick fixes, try the static analyzer.
I think the problem is here:
[propList release];
Since you created propList using arrayWithContentsOfFile you don't need to release it - it will be automatically released. The autorelease is actually what's causing the error since it is trying to release something that you already released manually.
ETA: as cobbal mentioned, you also don't need to release path.
Debugging EXC_BAD_ACCESS is difficult to debug. This happens when a message is sent to an object that is already released. You need to find out what is causing this generic error by turning on NSZombiEnabled environment variable so the Objective-C environment will be able to 'track' a deallocated object. Using this, when you get the error you can determine where the error occurred by looking at the call stack. You won't know where it is released but at least it will get you close.
I don't have it setup here, but you may also be passing a pointer to the error which will cause the object to not persist as a zombie/dummy.
Bottom line, you need to make sure the variables you are meaning to release, that you retain them as necessary.
This Technical Q&A by Apple gives tips on Finding bugs with EXC_BAD_ACCESS.
For one, neither of these are necessary in your example:
[path release];
[propList release];
because:
path is a string literal (will always exist)
propList is autoreleased
For any EXC_BAD_ACCESS errors, you are usually trying to send a message to a released object. The BEST way to track these down is use NSZombieEnabled.
This works by never actually releasing an object, but by wrapping it up as a "zombie" and setting a flag inside it that says it normally would have been released. This way, if you try to access it again, it still know what it was before you made the error, and with this little bit of information, you can usually backtrack to see what the issue was.
It especially helps in background threads when the Debugger sometimes craps out on any useful information.
VERY IMPORTANT TO NOTE however, is that you need to 100% make sure this is only in your debug code and not your distribution code. Because nothing is ever released, your app will leak and leak and leak. To remind me to do this, I put this log in my appdelegate:
if(getenv("NSZombieEnabled") || getenv("NSAutoreleaseFreedObjectCheckEnabled"))
NSLog(#"NSZombieEnabled/NSAutoreleaseFreedObjectCheckEnabled enabled!");
If you need help finding the exact line, Do a Build-and-Debug (CMD-Y) instead of a Build-and-Run (CMD-R). When the app crashes, the debugger will show you exactly which line and in combination with NSZombieEnabled, you should be able to find out exactly why.
http://www.cocoadev.com/index.pl?NSZombieEnabled can be useful in tracking down EXC_BAD_ACCESS bugs. Instead of deallocating objects when they are released it puts them into a zombie state that raises an exception when they are subsequently accessed. Just be sure not to ever release code with this flag set, as it will leak memory like a sieve.
what is self.editButtonItem? I don't see it in your .h file
A couple of things.
In initWithUserName: you're getting info from a method that doesn't contain alloc/copy/create. Further, you don't explicitly retain it. Yet you release it. This is problematic assuming fetchInfoForUsername: autoreleases its result as expected according to the Cocoa Memory management rules.
Using property accessors in initializers is considered bad form since it can cause KVO notifications to be sent out for a half-baked instance.

objective-c memory management: caching view elements

I'm writing an application which is quite graphically heavy and therefore I'm trying to implement a caching mechanism within my view controller that creates a view once, and retains it for future use, similar to the following:
- (UIView *)logoView
{
if(_logoView == nil)
{
_logoView = [[UIImageView alloc] initWithImage: [UIImage imageNamed: #"logo_header.png"]];
_logoView.contentMode = UIViewContentModeCenter;
}
return _logoView;
}
This all works fantastically, however, I'm getting a bit confused over a certain issue; when I simulate a memory warning I call a method which purges the cache, like so:
- (void)purgeCachedContent
{
if(_logoView != nil)[_logoView release];
}
However, this seems to be causing my application issues, as it can get called several times (dealloc, didReceiveMemoryWarning and viewDidUnload).
How can I determine if an instance exists to send it a release message? Even when I try to NSLog a released view I receive a EXC_BAD_ACCESS error, so I'm having trouble figuring out the best way I'd do it. I even removed the if statement hoping to rely on being able to send nil objects messages, but this causes the same error...
I'm sure it's something simple missing from my objective-c knowledge... I just don't know what!
_logoView isn't set to nil automatically just by releasing it, so any future methods you try to call using that pointer will go to a memory location that used to contain a valid object, but now contains junk. You can fix this by setting _logoView = nil; after releasing it.
If you want to cache it why would you want to release it?
just use autorelease on init:
_logoView = [[[UIImageView alloc] initWithImage: [UIImage imageNamed: #"logo_header.png"]] autorelease];