NSMutableArray's count method causes a bad access error? - iphone

I see a few similar questions, but no simple answers. I'm just playing around with NSMutableArray's to get a feel for them before I actually use them in my real project. For some reason, it's giving me an EXC_BAD_ACCESS error when I try to call count on the array, and I can't figure out why.
- (void) applicationDidFinishLaunching:(UIApplication*)application
{
// Create window and make key
_window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
[_window makeKeyAndVisible];
NSMutableArray* test = [[NSMutableArray alloc] initWithObjects:[NSString stringWithFormat:#"first!"], [NSString stringWithFormat:#"second!"], nil];
[test insertObject:[NSString stringWithFormat:#"inserted"] atIndex:0];
NSLog(#"%#", [test objectAtIndex:0]);
NSLog(#"%#", [test objectAtIndex:1]);
NSLog(#"%#", [test objectAtIndex:2]);
NSLog(#"%#", [test count]); //bad access here
}
All the inserting and accessing EXCEPT the count method work just fine. I don't see why this isn't working, and would greatly appreciate some help. Thanks!

The %# format specifier prints objects. The return value of -count is just an unsigned integer. You should use the format specifier %u for that type.

The problem is that [test count] returns an NSUInteger not a pointer (to an NSObject). Try this instead:
NSLog(#"%u", [test count]);
Note that using %d also works, but %u is preferred.

- (NSUInteger)count; returns an NSUInteger.
Use this instead:
NSLog(#"%u", [test count]); //bad access here

count is working just fine. It does however return a NSUInteger primitive and not a pointer to a NSObject subclass. The %# string formatter expects a point to an object and logs the NSString returned from that object's -description method. When you pass it a NSUInteger NSLog assumes it to be an object pointer and dutifully tries to send a -description message to memory address 3 which causes that EXEC_BAD_ACCESS.

Related

Declared and synthesized NSArray works in all methods but one

I have an array of NSMutableDictionaries which has been sorted.
This array has been declared and synthesized so that its reachable anywhere in the code. However, its not.
When I try to read it out in cellForRowAtIndexPath, I get 0x5852400 does not appear to point to a valid object in debugger by using the po command, and I get the EXC_BAD_ACCESS error when using NSLog.
Code:
- (void)request:(FBRequest *)request didLoad:(NSArray*)result
{
int counter;
friendsArray = [[NSMutableArray alloc] init];
for (NSDictionary *d in [result objectForKey:#"data"])
{
[friendsArray addObject:d];
counter++;
}
NSSortDescriptor *sortDescriptor;
sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:#"first_name" ascending:YES] autorelease];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray;
newfriendsArray = [[NSArray alloc] init];
newfriendsArray = [friendsArray sortedArrayUsingDescriptors:sortDescriptors];
NSLog(#"The new array which works and has been sorted: %#", newfriendsArray);
[[self tableView] reloadData];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
NSLog(#"Array still working here: %#", newfriendsArray);
return [newfriendsArray count];
}
Doing the same NSlog like the ones above in cellForRowAtIndexPath will cause the simulator to crash.
It's a memory management error. In this line:
newfriendsArray = [friendsArray sortedArrayUsingDescriptors:sortDescriptors];
sortedArrayUsingDescriptors: returns an autoreleased object that you need to retain if you want to use it for longer than the current method. The line above:
newfriendsArray = [[NSArray alloc] init];
has no effect other than introduce a memory leak to your code. It seems you have not quite understood when you need to create new objects and when other methods do this for you. In addition, you should really use properties to set your ivars (self.newFriendsArray = ...;) to avoid these simple memory management errors.

Memory Leak: Issues with releasing NSArray in iPhone app

I have a code which shows leaks in Instruments. It shows leaks where I initialize the arrayDetPerformance with the contents of arrayDetail
If I release my arrayDetail then my app crashes.
What could be wrong?
Here is the code:
NSDictionary *finalResult = [extractUsers JSONValue];
// NSLog(#" Stamp-16 : %#",[NSDate date]);
NSLog(#"Array2 : %d",[arrayDetail retainCount]); //RETAIN COUNT IS 0
arrayDetail = [[finalResult objectForKey:#"Detail"]
NSLog(#"Array2 : %d",[arrayDetail retainCount]); //RETAIN COUNT IS 2
// NSLog(#"Data is : %#",array1);
// NSLog(#" Stamp-17 : %#",[NSDate date]);
//NSLog(#"Final Value is : %#",[[allUsers objectAtIndex:0] valueForKey:#"password"]);
//[self setUserData:allUsers];
//[tblView reloadData];
[responseString release];
[request release];
}
//sleep(0.3);
//[inProgressIndicator stopAnimating];
[fileContents release];
//Release all the allocated data
[json release];
//label.text = #"Finish";
// NSLog(#" Stamp-19 : %#",[NSDate date]);
NSUserDefaults *def = [NSUserDefaults standardUserDefaults];
//NSLog(#"Array2 : %d",[array2 retainCount]);
arrayDetPerformance = [[NSMutableArray alloc] initWithArray:arrayDetail];
chartPoints= [arrayDetPerformance valueForKey:#"Points"];
NSLog(#"Chart Points: %#",chartPoints);
[def setObject:chartPoints forKey:#"yarray"];
[def setObject:#"YES" forKey:#"flagThumb"];
//array1 = [[NSMutableArray alloc] initWithObjects:#"New",#"Table",#"View",nil];
//[self.Dettable reloadData];
//sNSFileManager *fileManager = [NSFileManager defaultManager];
//[array2 release];
NSLog(#"ArrayDEtPerfomance : %d",[arrayDetPerformance retainCount]);
NSLog(#"array2 : %d",[arrayDetail retainCount]);
if([chartPoints count]>0)
{
PlotItem *plotItem = [[PlotGallery sharedPlotGallery] objectAtIndex:0];
[plotItem imageHive:Fund];
}
//[arrayDetail release];
}
Memory leak is shown on the line
arrayDetPerformance = [[NSMutableArray alloc] initWithArray:arrayDetail];
Also I am confused on why the retain count directly goes from 0 to 2 in the below code:
NSLog(#"Array2 : %d",[arrayDetail retainCount]); //RETAIN COUNT IS 0
arrayDetail = [[finalResult objectForKey:#"Detail"]
NSLog(#"Array2 : %d",[arrayDetail retainCount]); //RETAIN COUNT IS 2
What could be wrong?
It shows a leak because you allocate arrayDetPerformance and then not release it. Simple as that. At least that's what we can tell from the code you are showing us.
As for the rest, don't use retainCount to debug memory problems, ever! You have to understand the simple memory management rules and follow them, nothing else. Since you don't know what Apple's underlying code does, you cannot rely on the retain count of an object.
As to your question relating to this code:
NSLog(#"Array2 : %d",[arrayDetail retainCount]); //RETAIN COUNT IS 0
arrayDetail = [[finalResult objectForKey:#"Detail"]
NSLog(#"Array2 : %d",[arrayDetail retainCount]); //RETAIN COUNT IS 2
You are assigning a whole other object to arrayDetail so it's completely meaningless to compare any properties of arrayDetail before and after the assignment. The retain count could be all over the place and it would not tell you anything.
I get the impression that you don't really know what you are doing here. You should read the memory management rules again and again until you understand them thoroughly.
retainCount won't help you debug your problem (in fact it will almost never be relevant to debugging, so best forget it's even there).
Don't release arrayDetail, as you don't own it. The problem is with arrayDetPerformance. You're allocing an object on that line, and it's not being released anywhere. Now, you may be doing that elsewhere in your code, but if you aren't, send it a release when you've finished using it.
Edit
If you're deallocating arrayDetPerformance in your dealloc method, I'm assuming it's an instance variable? In this case, you can't assume that it doesn't already point at an object, so you should send it a release before assigning it to the new object.
Alternatively, if it is configured as a property, just use self.arrayDetPerformance = ... which will take care of the memory management for you.
Do not call retainCount
retainCount is useless. The absolute retain count of an object is an implementation detail. The rule is simple; if you cause something to be retained, you must cause it to be released when you are done with it. End of story.
The memory management documentation discusses this fully.
First, retainCount can never return zero. The only time you'll get a zero is if you happened to message nil. This:
NSLog(#"Array2 : %d",[arrayDetail retainCount]); //RETAIN COUNT IS 0
arrayDetail = [[finalResult objectForKey:#"Detail"]
NSLog(#"Array2 : %d",[arrayDetail retainCount]); //RETAIN COUNT IS 2
You are causing arrayDetail to point to a different object on that second line. Thus, no relation between the retain count before/after that line.
When leaks tells you a leak is on a particular line like this one...
arrayDetPerformance = [[NSMutableArray alloc] initWithArray:arrayDetail];
... it is telling you that said object allocated on that line was leaked. It is not telling you that particular line was the cause of the leak. The leak is likely because you either over-retained it somewhere else or forgot to leak it.
You've said in several comments that you are "deallocating [something] in your dealloc". Show your dealloc method's implementation.
[arrayDetPerformance release]; is not written in your code;
So, its show memory leak.

GKScore properties warning

I'm building a custom display for Game Center, which works, except I'm getting a warning for the following code -
NSMutableArray *playerIDsArray = [[NSMutableArray alloc] init];
[highScores removeAllObjects];
for (GKScore *thisScore in scores)
{
NSMutableDictionary *thisEntry = [[NSMutableDictionary alloc] init];
NSString *playerID = [thisScore playerID];
[thisEntry setObject:playerID forKey:#"playerID"];
[playerIDsArray addObject:playerID];
[thisEntry setObject:[NSNumber numberWithInt:(int)[thisScore value]] forKey:#"value"];
[highScores setObject:thisEntry forKey:[NSString stringWithFormat:#"%i",[thisScore rank]]]; // warning here
[thisEntry release];
}
The warning for [thisScore rank] says "Method '-rank' not found (return type defaults to 'id')". The code works fine, however. I must be missing something...
Any help appreciated!
in GKScore, rank is a NSInteger aka Object...
Where as calling %i is calling integer..
hence.. You must call [[thisScore rank]intValue] to get the integer Value of the NSInteger Object..
Finally figured it out - bit daft really, I just hadn't included this -
#import <GameKit/GKScore.h>
I'd just assumed it was covered by importing the other Game Center headers... Thanks anyway!

iPhone JSON object releasing itself?

I'm using the JSON Framework addon for iPhone's Objective-C to catch a JSON object that's an array of Dictionary-style objects via HTTP. Here's my connectionDidFinishLoading function:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[connection release];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
[loadingIndicator stopAnimating];
NSArray *responseArray = [responseString JSONValue]; // Grab the JSON array of dictionaries
NSLog(#"Response Array: %#", responseArray);
if ([responseArray respondsToSelector:#selector(count)]) {
NSLog(#"Returned %# items", [responseArray count]);
}
[responseArray release];
[responseString release];
}
The issue is that the code is throwing a EXC_BAD_ACCESS error on the second NSLog line. The EXC_BAD_ACCESS error I think indicates that the variable got released from memory, but the first NSLog command works just fine (and shows that the data is all there); it seems that only when calling the count message is causing the error, but the respondsToSelector call at least thinks that the responseArray should be able to respond to that message. When running with the debugger, it crashes on that second line, but the stack shows that the responseArray object is still defined, and has 12 objects in it (so the debugger at least is able to get an accurate count of the contents of that variable).
Is this a problem with the JSON framework's creation of that NSArray, or is there something wrong with my code?
count returns an NSUInteger, not an object. Per the Apple Documentation you should use %lu and cast to unsigned long in this case instead to display it:
NSLog(#"Returned %lu items", (unsigned long)[responseArray count]);
With %#, NSLog() tries to interpret the value as a pointer to an object and to send a description message to it.
It is highly unlikely that there is accidentally a valid object at that memory location, so the code is bound to fail badly or show unpredictable results.
In addition to what gf and Nikilai said, [responseArray release]; over releases
If those guys got their method naming right, NSArray *responseArray = [responseString JSONValue]; returns an autoreleased instance.
Simple typo: Your format string in the second NSLog contains an %# instead of %d.

Objective C - application crashes when creating an array of strings, am I crazy?

If i try to use this simple code:
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// Add the tab bar controller's current view as a subview of the window
[window addSubview:tabBarController.view];
NSString *stringMer = [NSString stringWithFormat:#"OK COOL"] ;
NSString *stringMer2 = [NSString stringWithFormat:#"OK COOL"];
NSArray *truc = [NSArray arrayWithObjects:stringMer,stringMer2];
}
My application crashes ("unable to read unknown load command 0x22" or just a regular crash)... What the applicationDidFinishLaunching is from my FooAppDelegate and i have no more code, is this normal ?
The list of arguments passed to the arrayWithObjects: method must be nil-terminated:
NSArray *truc = [NSArray arrayWithObjects:stringMer,stringMer2, nil];
Don't use +stringWithFormat: unless you actually have a format string that needs parsing.
NSResponder is right - don't get sloppy by defaulting to stringWithFormat.
Perspx also pointed out a fairly obvious (but easily forgotten) error with the missing nil.
I'd be a bit more explicit -
(in the .h)
NSArray *truc; (assuming it won't be a property)
(in the .m)
//actually, I'd define, "OK COOL" as a string and init with that, but...
NSString *stringMer = [[NSString alloc] initWithString:#"OK COOL"] ;
NSString *stringMer2 = [[NSString alloc] initWithString:#"OK COOL"];
truc = [[NSArray alloc] initWithObjects:stringMer,stringMer2, nil];
//appease the memory gods
[stringMer release];
[stringMer2 release];
(then, down in dealloc)
[truc release];
It would be good to get really familiar with Instruments - run for Leaks.