I determined that my application crashes on the following line:
if(sourceValues != nil && [sourceValues class] == [NSMutableArray class])
[sourceValues release];
"sourceValues" is declared as an NSMutableArray at the top of my class. The "if" loop is satisfied, and the [sourceValues release] call gets made, which crashes the program with no error code. So, since sourceValues != nil and since [sourceValues class] == [NSMutableArray class], I wanted to see exactly what sourceValues was. So, above the "if" loop I added the following:
NSLog(#"sourceValues is %#", sourceValues);
But my program will not print it. It just crashes with no error code on that line. So, if sourceValue exists and if it an NSMutableArray, why will it not be printed. What is the problem here?
I'm trying to have code that says "if sourceValues has been allocated, release it." How can I do this?
Why not use:
if(sourceValues != nil && [sourceValues isKindOfClass:[NSMutableArray class]])
[sourceValues release];
You may want to use other methods like:
isMemberOfClass:
If this...
NSLog(#"sourceValues is %#", sourceValues);
... is crashing your program, it is because sourceValues has already been released. Wherever you release sourceValues, set it to nil.
[sourceValues release], sourceValues = nil;
If your app is still crashing, it is because the array has been over-released somewhere else. That is, you didn't correctly balance retains and releases. First, try "build and analyze" and fix any problems the static analyzer identifies. Next, turn on zombie detection and see where you are first messaging the over-released object.
Note that [sourceValues class] == [NSMutableArray class] will not work and that pattern should never be used to check if an instance is of a particular class. You should always use isKindOfClass: or isMemberOfClass:.
However, since you can't tell the difference between a mutable or immutable array anyway, there is no point to checking in the first place.
Well, first off, there's no danger in releasing a nil object. So the issue of "if sourceValues has been allocated, release it." is probably superfluous. Unless you're doing some funky stuff, you should follow regular memory management rules and just call [sourceValues release]; in dealloc
Have you tried running everything with breakpoints on ("Build and Debug - Breakpoints On" in Xcode)? That usually gives you more debug information than the usual "Build and run".
It might be (I'm guessing based on some odd problems I've had in the past) that you've actually dealloced sourceValues already but the pointer is still pointing to its old memory. If that happens, a new object might be situated in that area which the program will attempt to treat like an NSMutableArray anyway.
Try checking the retainCount, something like:
NSLog(#"retain count: %d", [sourceValues retainCount]);
ONLY use for testing/debugging, don't rely on this in an if statement.
Also, you can try iterating over sourceValues and print its contents to NSLog, but I have no idea what's in there.
BEST thing to do is setup a class property as an MSMutableArray with RETAIN, and use synthesize. The dealloc will take care of the release.
If the above is implemented, you can alloc/init as follows:
self.sourceValues = [[[NSMutableArray alloc] init] autorelease];
and then add objects directly to self.sourceValues as needed. No need to worry about releasing, it will be done in dealloc.
Related
When running Analyzer, I receive the "Object sent -autorelease too many times" warning. I know I'm doing something tricky (and am open to alternatives to accomplishing my goal).
Essentially, I wanted to keep a stack of specific types of controllers handy for sending messages from a central location. So, considering:
From 'ActionBroker' shared object:
NSMutableSet *liveActions;
liveActions = [NSMutableSet alloc] init];
...
CLViewController *action = [[[actionClass alloc] init] autorelease];
if (!action) {
return nil;
}
[self.liveActions addObject: action];
// When adding an object, the retain count is increased. We want the action
// to dealloc, so that it might remove itself from the list...
[action release];
return action;
And the complementary dealloc code:
[[CLActionBroker sharedActionBroker] removeAction: self];
[super dealloc];
... and in removeAction:
[action retain];
[self.liveActions removeObject:action];
The above code works, I just get the nagging error. And the nagging sensation that I could probably solve the problem a different way.
One of the use cases for this code is to pass an 'handleOpenURL' request through a chain of open controllers and returning the first 'YES' response.
As I understand it, your intent is to have a set of controllers ("actions") in the set, which you could easily access from anywhere in your app. If one of these controllers is deallocated, it would automatically remove itself from the set.
There's a problem with this setup, though. As you've described it above, the controller is supposed to be removed from the liveActions set upon deallocation. But since an NSSet retains its members, your controller will never be deallocated so long as it is still in the liveActions set. -dealloc is only run when all retains have been balanced by a release.
This, then leads to your over-release, which leads to the warning. Since you've sent an extra release, -dealloc could be run while the controller is still in the liveActions set. But when you remove it from that set, it's going to send a release message to your object, which would take its retain count negative. That may or may not be safe, but in either case it's an ugly workaround.
What you really want, it seems, is a set that does not retain its members. This is normally a dangerous configuration, since it can lead to dangling pointers in the set. But if you're willing to manage the lifetime of the object and clear out those dangling pointers at the appropriate times, it turns out that it's quite doable. You just need to use a CFMutableSet instead of an NSMutableSet. And since the two are toll-free bridged, it doesn't even add that much complexity.
Setting up the CFMutableSet looks like this:
// The NULL for the third parameter tells the set to not do anything when
// an object is added to or removed from the set.
CFMutableSetRef cfLiveActions = CFSetCreateMutable(NULL, 0, NULL);
// Toll-free bridging makes this possible
NSMutableSet *liveActions = (NSMutableSet *)cfLiveActions;
After that, you can use it exactly as you would use any other NSMutableSet; this one will just be special in that it won't retain its members.
The problem:
CLViewController *action = [[[actionClass alloc] init] autorelease]; // +0 ownership
// ...
[self.liveActions addObject: action]; // liveActions takes ownership
// ...
[action release]; // -1 ownership
When you've alloced the object, you are responsible for it. But then when you autorelease it, you've fulfilled your obligation (and because you autoreleased instead of releasing, you can still use the object until the next turn of the runloop). You shouldn't release it again later.
Also:
[action retain];
[self.liveActions removeObject:action];
The retain is unnecessary.
(And here's another plug for beginning to switch to ARC, under which you won't have to worry about this!)
[Edit: misunderstood your question. Revised answer forthcoming.]
[Edit 2: never mind... even though I misread your intent, I believe my solution is still right.]
i am using NSArray named as allAvailableServices .
first i initialize it and after wrote [allAvailableServices release];
then i checked the condition if(allAvailableServices).
it came as true!may i know the reason.
then i wrote
allAvailableServices = nil; and [allAvailableServices release];
after the above code if(allAvailableServices) came false.is there any problem that i wrote [allAvailableServices release]; after allAvailableServices = nil;
whenever allAvailableServices = nil i need to allocate new array.so if i didn't call [allAvailableServices release]; i think there is a memory problem.am i correct?
can any one provide me a best way to deal with it?
Release just reclaims the memory, but the pointer value isn't changed; it still contains a value, but it points to garbage.
This is why many people nil their pointer after releasing it:
[allAvailableServices release], allAvailableServices = nil;
In Objective-C, it is safe to send a message to nil, but sending a message to an object that has been released may cause the app to crash (usually with EXC_BAD_ACCESS)
Note, that I've released the object before setting it to nil. If you do it the other way around, you've lost the pointer to which you send the release message, and so you've leaked the object.
If you use accessors then you can directly set the values to nil, it calls the release and sets them to nil. A release decrements the reference count by 1 and if it reaches 0 the object is freed.
The memory still has some value if you do not set to nil and hence the problem in your if condition.
Cleaner way is to use accessors and call self.yourvariable = nil; .
I have just been through a client project initially coded by us then abused by them! And we are getting a strange crash, which is consisten on each mac individually but inconsistent between the team or after a reload/restart of Xcode/OSX.
The problem is one I have come across before when I havent been cleaning up objects properly, but I have been through static analyser and am still struggling.
The crashes always seem to occur because objects we have created in the VC are getting overwritten by others therefore changing their type so valid method calls are being thrown as exceptions.
For example:
NSNumber *test1;
-(void)viewDidLoad {
test1 = [NSNumber numberWithInteger:7];
}
-(void)someOtherMethod {
NSLog(#"what was test? %#", [test stringValue]);
}
This is currently throwing an unrecognized selector exception during the NSLog as hovering over test1 tells me is is of type NSURLRequestInternal, if I restart Xcode it will probably be somewhere else!!
How can I debug/solve this!?!?
That should crash every time.
Let's look at the code.
test1 = [NSNumber numberWithInteger:7];
This creates a NSNumber and assigns it to the instance. However, the NSNumber is scheduled for deallocation as soon as the main event loop is run.
NSLog(#"what was test? %#", [test stringValue]);
This, presumably, is being run after the main event loop has had a swing. At this point, test (and I assume this should be test1) is pointing to a dangling pointer. Accessing it will crash.
There is no substitute to reading Apple's documentation on memory management: http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html
This will explain what you need to know. But basically, you should be using [[NSNumber alloc] initWithInteger:7] or retain it (possibly using property syntax) instead.
But unless you read Apple's documentation on the subject, you're going to be chasing memory bugs forever.
you forgot to retain test1 so it gets autoreleased, hence the crash
try using NSZombieEnabled for debugging this kind of stuff.
test1 = [NSNumber numberWithInteger:7];
should either be
test1 = [[NSNumber numberWithInteger:7] retain];
or (better)
test1 = [[NSNumber alloc] initWithInteger:7];
numberWithInteger: returns an autoreleased object, so by the time someOtherMethod is called, the object that test1 points to has been deallocated.
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.
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.