NSArray runtime array - iphone

I have got I have got two methods both in different classes. One is class method and other is instance method. i am calling class method from instance method. When instance method finishes it gives runtime error "EXC_BAD_ACCESS".
#import "xmlObject.h"
#import "textmeAppDelegate.h"
#implementation Class1
- (void)method1 {
textmeAppDelegate *del = (textmeAppDelegate *)[[UIApplication sharedApplication] delegate];
NSArray *bgColor = [[NSArray alloc] initWithArray:[xmlObject fetchImmediateChildrenValues:[del.navigationbarStyle objectForKey:#"backgroundcolor"]]];
UIColor *color = [UIColor colorWithRed:[[bgColor objectAtIndex:3] floatValue] green:[[bgColor objectAtIndex:2] floatValue] blue:[[bgColor objectAtIndex:1] floatValue] alpha:[[bgColor objectAtIndex:0] floatValue]];
CGContextSetFillColor(context, CGColorGetComponents([color CGColor]));
CGContextFillRect(context, rect);
[bgColor release];
}
#end
#implementation xmlObject
+ (NSArray *) fetchImmediateChildrenValues:(NSMutableDictionary *) node {
NSMutableDictionary *tmp = [[node objectForKey:#"children"] retain];
NSArray *keys = [[NSArray alloc] initWithArray:[tmp allKeys]];
keys = [keys sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
NSMutableArray *pushArr = [[[NSMutableArray alloc] init] autorelease];
NSString *val = [[NSString alloc] init];
for(NSString *str in keys) {
val = (NSString *)[[tmp objectForKey:str] objectForKey:#"innertext"];
[pushArr addObject:val];
}
[val release];
[keys release];
return [NSArray arrayWithArray:pushArr];
}
#end
What is wrong with the code? Also app is crashing for this line of code
application is crashing if i include this line
NSArray *bgColor = [[NSArray alloc] initWithArray:[xmlObject fetchImmediateChildrenValues:[del.navigationbarStyle objectForKey:#"backgroundcolor"]]];
If I remove it application runs smoothly.

I have several comments on your code. One of them is the immediate cause of your crash, but you need to fix at least one other issue too. The short answer is that you over release val and keys.
NSArray *bgColor = [[NSArray alloc] initWithArray:[xmlObject fetchImmediateChildrenValues:[del.navigationbarStyle objectForKey:#"backgroundcolor"]]];
You don't need to create a new array here, you can simply write the following:
NSArray *bgColor = [xmlObject fetchImmediateChildrenValues:[del.navigationbarStyle objectForKey:#"backgroundcolor"]];
if you do, you don't need the [bgColor release] further down.
NSArray *keys = [[NSArray alloc] initWithArray:[tmp allKeys]];
keys = [keys sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
These two lines leak the first NSArray, you alloc it but you overwrite it straight away with the sorted version. In fact, you can simply write:
keys = [[tmp allKeys] sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
Note that you do not own keys so you can get rid of the [keys release] line further down.
NSString *val = [[NSString alloc] init];
for(NSString *str in keys) {
val = (NSString *)[[tmp objectForKey:str] objectForKey:#"innertext"];
[pushArr addObject:val];
}
[val release];
This is the source of your immediate problem. You first alloc a new string. Then you immediately overwrite it on each iteration of your loop. So the allocated NSString leaks. You do not own the val returned by [[tmp objectForKey:str] objectForKey:#"innertext"]; on each iteration, so the release ov val after the loop should not be there.
On a side note, objectForKey: returns an id - the cast to NSString* is redundant. Most people leave it out.
[keys release];
Going back to the bit above where I told you that you were leaking your alloc'd keys? Well the new version of keys you overwrote it with you don't own. Therefore you must not release keys here.
return [NSArray arrayWithArray:pushArr];
This is fine. My preference would be for:
return [[pushArray copy] autorelease];
but it is just a matter of style. You could also just return pushArray, but pushArray is mutable and the caller may rely on the return value being immutable.

Test your code with NSZombieEnabled set... It should give you enough informations to fix your problem.

Related

NSMutableArray release causes crash

I am using XCode for developing an iPhone app. I am new to this platform and need some help with a particular issue...
I have a method that processes some data and returns two integer values as NSNumber wrapped into a NSMutableArray.
Here is the method:
-(NSMutableArray *)processPoints:(int) x:(int) y
{
NSMutableArray *mutArray = [[NSMutableArray alloc] initWithCapacity:3];
int x9,y9;
// ...do some processing...
NSNumber* xNum = [NSNumber numberWithInt:x9];
NSNumber* yNum = [NSNumber numberWithInt:y9];
[mutArray addObject:xNum];
[mutArray addObject:yNum];
return [mutArray autorelease];
}
I call the above method from another method, where I copy the NSNumber stuff into local variables and then release the local copy of NSMutable array.
But the app crashes when releasing this NSMutable array (variable 'mutArray').
Here is the method:
-(void)doNinjaAction
{
NSMutableArray* mutArray = [self processPoints: x :y];
NSNumber* s1 = [[mutArray objectAtIndex:0] retain];
NSNumber* s2 = [[mutArray objectAtIndex:1] retain];
x = [s1 integerValue];
y = [s2 integerValue];
//...proceed with other stuff...
[mutArray autorelease]; //this is where the system crashes. same for 'release'
//instead of 'autorelease'
}
Can you please explain where I am going wrong with the process of memory release.
My understanding of the process is a bit shaky. Please help.
Because you're overreleasing the array. You alloc-init it in processPoints:, then you autorelease it - that's correct, this is how you dispose of its ownership.
After that, you don't need to and must not autorelease or release it once again. This is not malloc() from the standard library.
when you call the statement
NSMutableArray* mutArray = [self processPoints: x :y];
This itself acts as autorelease.
Hence releasing the array explicitly will cause the app to crash.
You are releasing mutArray more then once. Once in processPoints function and again in doNinjaAction.
To resolve the crash remove :
[mutArray autorelease];
-(NSMutableArray *)processPoints:(int) x:(int) y
{
NSMutableArray *mutArray = [[NSMutableArray alloc] initWithCapacity:3];
int x9,y9;
// ...do some processing...
NSNumber* xNum = [NSNumber numberWithInt:x9];
NSNumber* yNum = [NSNumber numberWithInt:y9];
[mutArray addObject:xNum];
[mutArray addObject:yNum];
[mutArray autorelase];
return mutArray;
}
try this one it'll resolve it.
-(NSMutableArray *)processPoints:(int) x:(int) y
{
NSMutableArray *mutArray =[[[NSMutableArray alloc] initWithCapacity:3]autorelease];
int x9,y9;
// ...do some processing...
NSNumber* xNum = [NSNumber numberWithInt:x9];
NSNumber* yNum = [NSNumber numberWithInt:y9];
[mutArray addObject:xNum];
[mutArray addObject:yNum];
return mutArray;
}
As #H2CO3 and #AppleDelegate suggested, it is right.
Still I would suggest to use ARC and convert your project to ARC enabled.
Go to Edit->Refactor->Convert to Objectiv-C ARC
Then you dont need to do any releases anywhere. It will take care itself of all the releases :)

Populating NSDictionary and NSArrays for Model data

I'm trying to create an NSDictionary full of arrays in the implementation file of my model but my code hasn't worked yet. I want to create arrays that are lists of types of dogs and cats and then add those arrays to a dictionary with keys called DOG and CAT. Here is my code:
#implementation wordDictionary
#synthesize catList = _catList;
#synthesize dogList = _dogList;
#synthesize standardDictionary =_standardDictionary;
- (void)setCatList:(NSMutableArray *)catList
{
self.catList = [NSMutableArray arrayWithObjects:#"lion", #"puma", #"snow leopard", nil];
}
- (void)setDogList:(NSMutableArray *)dogList
{
self.dogList = [NSMutableArray arrayWithObjects:#"pit bull", #"pug", #"chihuahua", nil];
}
-(void)setStandardDictionary:(NSMutableDictionary *)standardDictionary
{
[self.standardDictionary setObject: _catList forKey:#"CAT"];
[self.standardDictionary setObject: _dogList forKey:#"DOG"];
}
- (NSString*)selectKey
{
NSInteger keyCount = [[self.standardDictionary allKeys] count];
NSInteger randomKeyIndex = arc4random() % keyCount;
NSString *randomKey = [[self.standardDictionary allKeys] objectAtIndex:randomKeyIndex];
return randomKey;
}
#end
This code is the model. The model is hooked up to my view controller such that when a user taps a button, the NSString returned from randomKey is displayed in a label on the screen. So the text will read either CAT or DOG. Here's the code for that:
- (IBAction)changeGreeting:(UIButton*)sender {
NSString *chosenKey = [self.dictionary selectKey];
NSString *labelText = [[NSString alloc] initWithFormat:#"%#", chosenKey];
self.label.text = labelText;
}
Unfortunately when I tap the button on the simulator I get an error message saying: Thread 1:EXC_ARITHMETIC (code=EXC_1386_DIV, subcode=0x0) at NSInteger randomKeyIndex = arc4random() % keyCount; and it appears that I'm getting it because neither my NSArray nor my NSDictionary have any objects inside of them.
Does anyone have any idea why my NSArray and NSDictionary haven't been populated?
Thanks very much.
The simple answer is that there isn't any code here that calls the methods to set the arrays or dictionary.
But the real underlying issue is that there are a couple of bad 'patterns' going on here that you should fix:
In your setter methods (setCatList:, setDogList:, setStandardDictionary:) you're not setting the properties in question to the values that are passed in. For example, you should be setting catList to the passed in "catList" variable.
- (void)setCatList:(NSMutableArray *)catList
{
if (_catList != catList) {
[_catList release];
_catList = [catList retain];
}
}
Then you should have some kind of "setup" happening, usually in a method in the view controller like viewDidLoad:
[wordDictionary setCatList:[NSMutableArray arrayWithObjects:#"lion", #"puma", #"snow leopard", nil]];
// and more for the other two setters
Alternately, you can set these default values in the init for the wordDictionary class:
- (id)init {
self = [super init];
if (self) {
[self setCatList:[NSMutableArray arrayWithObjects:#"lion", #"puma", #"snow leopard", nil]];
}
return self;
}
The former is better in most cases, but you may have a good reason to pre-populate your model for all instances of the class.
Assuming you called setCatList:, setDogList: and setStandardDictionary: before. Probably that causing is this :
NSString *chosenKey = [self.dictionary selectKey];
change into this :
NSString *chosenKey = [self selectKey];
UPDATE
I'm trying to make your life easier. no need to create your object if you don't need the most.
- (NSMutableArray*)getCatList
{
return [NSMutableArray arrayWithObjects:#"lion", #"puma", #"snow leopard", nil];
}
- (NSMutableArray*)getDogList
{
return [NSMutableArray arrayWithObjects:#"pit bull", #"pug", #"chihuahua", nil];
}
-(NSMutableDictionary*)getStandardDictionary
{
NSMutableDictionary *standardDictionary = [NSMutableDictionary new];
[standardDictionary setObject:[self getCatList] forKey:#"CAT"];
[standardDictionary setObject:[self getDogList] forKey:#"DOG"];
return [standardDictionary autorelease];
}
- (NSString*)selectKey
{
NSMutableDictionary *standardDictionary = [self getStandardDictionary];
NSInteger keyCount = [[standardDictionary allKeys] count];
NSInteger randomKeyIndex = arc4random() % keyCount;
NSString *randomKey = [[standardDictionary allKeys] objectAtIndex:randomKeyIndex];
return randomKey;
}
- (IBAction)changeGreeting:(UIButton*)sender {
// NSString *chosenKey = [self selectKey];
//NSString *labelText = [[NSString alloc] initWithFormat:#"%#", chosenKey];
self.label.text = [self selectKey]; //no need to convert it to NSString again
}
Two things to consider:
I don't see you calling these:
setCatList:(NSMutableArray*)catList;
setDogList:(NSMutableArray*)dogList;
You use self.catList and self.dogList, but neither of those are synthesized, instead you have beatList and meList synthesized
Change the synthesizes to the catList and dogList, and make sure you call the set list methods, and then you should make some progress.

Trying to find leak of type NSMutableArray. Instruments shows leak in method.

I eliminated all the leaks from my current app. However Instruments constantly tells me that I have a leak in the method shown below.
The leak is of type NSMutableArray and has a size of either 16 or 32 bytes. Yes, I know that's not much but it adds up. Also see it as an academic question that I need to solve to make my code leakless.
+ (id) meterFromDict:(NSDictionary*)dict {
Meter* resMeter = [[Meter alloc] initWithType:[[dict objectForKey:#"MeterBase"] intValue]];
//NSLog(#"dict: %#",dict);
resMeter.volume = nil;
resMeter.sounds = nil;
resMeter.repeats = nil;
resMeter.volume = [[[NSMutableArray alloc] initWithArray:[dict objectForKey:#"volumeArray"]] autorelease];
resMeter.sounds = [[[NSMutableArray alloc] initWithArray:[dict objectForKey:#"soundsArray"]] autorelease];
resMeter.repeats = [[[NSMutableArray alloc] initWithArray:[dict objectForKey:#"repeatsArray"]] autorelease];
//NSLog(#"MeterFromDict called and resmeter.repeats count is : %i",[resMeter.repeats count]);
resMeter.bpm = [[dict objectForKey:#"BPM"] floatValue];
return [resMeter autorelease];
}
Without looking at your Instruments output directly I can't tell exactly, but you're writing some redundant code: Try this:
+ (id) meterFromDict:(NSDictionary*)dict {
Meter* resMeter = [[Meter alloc] initWithType:[[dict objectForKey:#"MeterBase"] intValue]];
//NSLog(#"dict: %#",dict);
resMeter.volume = [dict objectForKey:#"volumeArray"];
resMeter.sounds = [dict objectForKey:#"soundsArray"];
resMeter.repeats = [dict objectForKey:#"repeatsArray"];
//NSLog(#"MeterFromDict called and resmeter.repeats count is : %i",[resMeter.repeats count]);
resMeter.bpm = [[dict objectForKey:#"BPM"] floatValue];
return [resMeter autorelease];
}
There's no point in nilling your properties before assigning new values to them.
Also, No point creating new arrays for arrays that you already have. And if you have properly declared your volume, sounds and repeats properties with copy instead of retain.
Try that and see if it works better.

Table crashes when sorting the data multiple times

I have a tableview with a navigationBar with a segmentedControl on the top of the view. I have set up the segmentedControl with buttons that sort the table by either "FirstName" or "LastName". It works perfectly the first 2-4 of times you press the sorting buttons, but then the app crashes.
The debugger and console seem to be of no help finding the source of the bug. Does anyone see any glaring mistakes in my code?
Here is my code below, let me know if you have any questions. Thanks!
- (IBAction)sortingSegmentAction:(id)sender{
NSString *keyToSortBy = [NSString alloc];
if([sender selectedSegmentIndex] == 0)
{
self.sortingSegmentActionPressed = 0;
keyToSortBy = #"FirstName";
}
else if([sender selectedSegmentIndex] == 1)
{
self.sortingSegmentActionPressed = 1;
keyToSortBy = #"LastName";
}
//Create the sort descriptors
NSSortDescriptor *sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:keyToSortBy ascending:YES] autorelease];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
//Sort allSubItams by the values set in the sort descriptors
NSArray *sortedArray;
self.sortedArray = [allSubItems sortedArrayUsingDescriptors:sortDescriptors];
//Recreate the data structure by putting the newly sorted items into a dictionary sorted by inital letters.
NSDictionary *eachItemList; //A DICTIONARY FOR PUTTING ALL THE DATA FOR EACH ITEM IN IT'S OWN SECTION
NSMutableDictionary *tempSectionedDictionaryByFirstLetter = [[NSMutableDictionary alloc] init];
for (eachItemList in sortedArray) //eachElementList is a dictionary with a section for each item
{
NSDictionary *aDictionary = [[NSDictionary alloc] initWithDictionary:eachItemList];
NSString *firstLetterString;
firstLetterString = [[aDictionary valueForKey:keyToSortBy]substringToIndex:1];
NSMutableArray *existingArray;
if (existingArray = [tempSectionedDictionaryByFirstLetter valueForKey:firstLetterString])
{
[existingArray addObject:eachItemList];
} else {
NSMutableArray *tempArray = [NSMutableArray array];
[tempSectionedDictionaryByFirstLetter setObject:tempArray forKey:firstLetterString];
[tempArray addObject:eachItemList];
}
[aDictionary release];
[eachItemList release];
}
//Set the data source for the table (sectionedDictionaryByFirstLetter) to tempSectionedDictionaryByFirstLetter.
self.sectionedDictionaryByFirstLetter = tempSectionedDictionaryByFirstLetter;
NSMutableArray *keyArray = [[NSMutableArray alloc] init];
[keyArray addObjectsFromArray:[[self.sectionedDictionaryByFirstLetter allKeys] sortedArrayUsingSelector:#selector(compare:)]];
self.keys = keyArray;
[self.tableView reloadData];
[keyArray release];
[tempSectionedDictionaryByFirstLetter release];
}
Don't release eachItemList at the end of your loop. You did not explicitly allocate it in this context so you shouldn't release it.
The for (object in array) loop gives you a reference to the object in the array, not a copy. By sending a release message to this reference, you are decrementing the retain count of this object while it is still in the array. After a few times (depending on how many times the object has been retained, NSArray for example retains objects when they are added to the array) it's retain count will reach 0, and it will then become deallocated and you'll get crashes regarding unrecognised selectors or EXC_BAD_ACCESS and possibly other kinds of errors.

Leaking Memory on iPhone :(

I'm a beginner at C, Obj-C and the iPhone, and I'm trying getting to grips with a lot of terminology used. I hope one of ye can help with a problem I have been struggling with for a few days now.
My code below is a method which call up a nib containing a search field and a table. The table is populated from a search of the array created for 'theList' below. Using 'Instruments', I am getting a Leak at the line:
NSDictionary *theItem = [NSDictionary dictionaryWithObjectsAndKeys:clientName,#"Name",clientId,#"Id",nil]; , but I can't figure out why :(
I know it's probably a difficult question to answer, but if any one can be of any help!
- (void)editClient:(id)sender {
if (pickList == nil) {
pickList = [[PickFromListViewController alloc] initWithNibName:#"PickList" bundle:nil];
}
TimeLogAppDelegate *appDelegate = (TimeLogAppDelegate *)[[UIApplication sharedApplication] delegate];
NSMutableArray *theList = [[NSMutableArray alloc] init];
int i;
for (i=0;i < [appDelegate.clients count];i++) {
Client *thisClient = [appDelegate.clients objectAtIndex:i];
NSString *clientName = [[NSString alloc] initWithString: thisClient.clientsName];
NSNumber *clientId = [[NSNumber alloc] init];
clientId = [NSNumber numberWithInt:thisClient.clientsId];
NSDictionary *theItem = [NSDictionary dictionaryWithObjectsAndKeys:clientName,#"Name",clientId,#"Id",nil];
[theList addObject:theItem];
theItem = nil;
[clientName release];
[clientId release];
}
[pickList createSearchItems:theList :NSLocalizedString(#"Client",nil)];
[theList release];
appDelegate.returningID = [NSNumber numberWithInt: projectsClientsId];
[self.navigationController pushViewController:pickList animated:YES];
}
Thanks in advance!
This returns allocated NSNumber instance.
NSNumber *clientId = [[NSNumber alloc] init];
This line overwrites the above clientId with another instance of NSNumber, numberWithInt returns autoreleased object, since you haven't allocated memory for it you should not call release, it will be released automatically.
clientId = [NSNumber numberWithInt:thisClient.clientsId];
You are calling release on clientId so you get memory problem.
To fix it remove the first line above which is useless in this case and update the second one to:
NSNumber * clientId = [NSNumber numberWithInt:thisClient.clientsId];
Then remove the:
[clientId release]
Because the clientId will be released automatically.
EDIT: Re still have problems ...
I'm not sure how to you manipulate the clients in app delegate, otherwise the code should work ok, I created small example, omitting the parts that I can't see (app delegate and clients):
// command line utility - foundation tool project:
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSMutableArray * theList = [[NSMutableArray alloc] init];
int i = 0;
for (i = 0; i < 10; ++i)
{
NSString * clientName = [NSString stringWithString:#"client"]; //no need to release
NSNumber * clientId = [NSNumber numberWithInt:i];
NSDictionary * theItem = [NSDictionary dictionaryWithObjectsAndKeys:
clientName, #"name",
clientId, #"id",
nil];
[theList addObject:theItem];
}
for (id item in theList) for (id key in item) NSLog(#"%# - %#", key, [item objectForKey:key]);
[theList release];
[pool drain];
return 0;
}
You are creating clientID with [[NSNumber alloc] init], and then immediately overwriting it with an autoreleased NSNumber instance [NSNumber numberWithInt], and then you are releasing it later in your code, which you shouldn't do. Get rid of the [[NSNumber alloc] init] line and the [clientId release] line and that should fix it up a little.
Aside from the obvious leak of the NSNumber, there are a few other things I'd fix that may help. Most are fairly minor, but in my experience with Objective-C, less code == clearer code, something that is not equally true for languages like Bash or Perl. ;-)
- (void) editClient:(id)sender {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
if (pickList == nil) {
pickList = [[PickFromListViewController alloc] initWithNibName:#"PickList" bundle:nil];
}
TimeLogAppDelegate *appDelegate = (TimeLogAppDelegate*)[[UIApplication sharedApplication] delegate];
NSMutableArray *searchItems = [NSMutableArray array];
NSMutableDictionary *itemDict = [NSMutableDictionary dictionary];
for (Client *client in appDelegate.clients) {
[itemDict setObject:[client.clientsName copy] forKey:#"Name"];
[itemDict setObject:[NSNumber numberWithInt:client.clientsId] forKey:#"Id"];
[searchItems addObject:[[itemDict copy] autorelease]];
}
[pickList createSearchItems:searchItems :NSLocalizedString(#"Client",nil)];
[self.navigationController pushViewController:pickList animated:YES];
appDelegate.returningID = [NSNumber numberWithInt: projectsClientsId];
[pool drain];
}
There are a few mysterious points that make me suspicious:
The line just after the for loop tells pickList to do something with the NSMutableArray. That method should retain the new array, as well as release the old array if one exists. If you just overwrite the pointer, the old array will be leaked. (Also, this method is poorly named. Anonymous arguments (a colon with no preceding text) are legal in Objective-C, but considered extremely bad practice. Consider renaming the method to better express what it does.)
The next line seems to associate the pick list with a navigation controller. If it is custom code, make sure the -pushViewController:animated: method properly releases an existing pick list when a new one is specified.
Assigning to appDelegate.returningID is assumed to call the setter for a returningID property. Be sure that property retains or copies the NSNumber as necessary.
Memory leaks can be tricky to track down, even in Instruments, and you'll often find that it looks like Foundation classes (such as NSDictionary) are leaking like a sieve, but I have always been able to trace it back to an abnormality in my code. :-)