leak problem on NSUserDefaults and NSMutableArray - objective-c

I have a leak in the following code:
- (void)viewDidAppear:(BOOL)animated {
//If Home-Theme
if (themeIndex == 0) {
NSUserDefaults *pref = [NSUserDefaults standardUserDefaults];
NSMutableArray *thisArray = [[NSMutableArray alloc] init];
thisArray = [[pref objectForKey:#"Themes"] mutableCopy];
[thisArray release];
}
}
the leak is at NSMutableArray.
I have try'd some different workarounds but nothing is help.
Maybe there is someting wrong with the NSUserDefaults?
any ideas?
thank you
xnz

NSMutableArray *thisArray = [[NSMutableArray alloc] init];
That is leaking since you never release that instance, just assign a new one in the next line. Replace it with:
NSMutableArray *thisArray = [[pref objectForKey:#"Themes"] mutableCopy];

You are allocating a NSMutableArray and the changing the reference to another array.
You probably want something like this:
- (void)viewDidAppear:(BOOL)animated {
//If Home-Theme
if (themeIndex == 0) {
NSUserDefaults *pref = [NSUserDefaults standardUserDefaults];
NSMutableArray *thisArray = [[pref objectForKey:#"Themes"] mutableCopy]];
// do something with thisArray
[thisArray release];
}
}

you alloc thisArray and then overwrite the reference to it with a mutable copy from pref. Either do autorelease or just remove the unnecessary NSMutableArray allocation

Related

adding objects crash in ios

i am trying to add an object into a nsuserdefault, but i get this crash
"[__NSCFArray insertObject:atIndex:]: mutating method sent to immutable object"
its crashing on this line:
[currentFav addObject:incomingBabe];
I have no idea why its crashing, its working on my other project.
here is my code
-(IBAction)favorite {
NSUserDefaults *standardDefault = [NSUserDefaults standardUserDefaults];
NSMutableArray *currentFav = [[NSUserDefaults standardUserDefaults]objectForKey:#"fav"];
NSLog(#"strings stored = %#",currentFav);
NSMutableArray *newFav = [NSMutableArray arrayWithObject:[NSString stringWithFormat:#"bikini%02d.jpeg",self.currentNumber]];
if (currentFav == NULL){
currentFav = [[NSMutableArray alloc]init];
}
for(NSString *incomingBabe in newFav){
BOOL hasStringAlready = NO;
for(NSString *currentFavorite in currentFav){
if([currentFavorite isEqualToString:incomingBabe]){
hasStringAlready = YES;
NSLog(#"has string already");
break;
}
}
if (!hasStringAlready) {
[currentFav addObject:incomingBabe];
hasStringAlready = YES;
}
}
[standardDefault setObject:currentFav forKey:#"fav"];
[standardDefault synchronize];
}
Basically it says you are trying to use a method from NSMutableArray on NSArray.
This is because "Values returned from NSUserDefaults are immutable, even if you set a mutable object as the value."
NSMutableArray *currentFav = [[NSUserDefaults standardUserDefaults]objectForKey:#"fav"];
will return an array, not mutable array. You should make a mutable copy of it.
NSMutableArray *currentFav = [[[NSUserDefaults standardUserDefaults]objectForKey:#"fav"] mutableCopy];
You can get the reason of your problem from 1 floor. You can use his method to solve your problem, or like this:
NSMutableArray *currentFav = [NSMutableArray arrayWithArray:[[NSUserDefaults standardUserDefaults]objectForKey:#"fav"]];

NSMutableArray won't add NSDictionary

I updated to cocos2d from 0.99.4 to 0.99.5. While it was on the older version I had the high score list working and I was able to add the NSDictionary to NSMutableArray with no problems.
Now that I've updated it won't add the NSDictionary variable scoreDetails to my NSMutableArray scoreList. Here's my code:
StatsManager.h
#interface StatsManager : NSObject {
NSMutableArray *scoreList;
NSUserDefaults *saveHighScore;
NSMutableArray *printableScoreList;
//NSMutableArray *scoreListTestOne;
float highScoreHelloWorld;
}
StatsManager.m
-(void)setHighScore:(float)highScore nameStrings:(NSString*)nameString {
NSNumber *newHighScore = [NSNumber numberWithFloat:highScore];
NSLog(#"%# highScore", newHighScore);
NSDictionary *scoreDetails = [NSDictionary dictionaryWithObjectsAndKeys:nameString, #"name", newHighScore, #"score", nil];
NSLog(#"%#", scoreDetails);
//NSMutableArray *testTwo = [[NSMutableArray alloc] init];
[scoreList addObject:scoreDetails];
NSLog(#"scoreList %#", scoreList);
//[scoreListTestOne addObject:scoreDetails];
//NSLog(#"scoreListTestOne %#", scoreListTestOne);
//sort
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"score" ascending:NO];
[scoreList sortUsingDescriptors:[NSArray arrayWithObject:sort]];
printableScoreList = scoreList;
NSLog(#"printableScoreList %#", printableScoreList);
//NSLog(#"scoreListTestOne %#", scoreListTestOne);
}
The line in question is
[scoreList addObject:scoreDetails];
I created a local NSMutableArray variable in the setHighScore function and tried adding the scoreDetails to that and it worked. but why doesn't it work like I've coded it above anymore?
I alloc init my scoreList here:
#implementation StatsManager
static StatsManager *_sharedStatsManager = nil;
-(id)init {
scoreList = [[NSMutableArray alloc] init];
//playerNames = [[NSMutableArray alloc] init];
//playerScores = [[NSMutableArray alloc] init];
printableScoreList = [[NSMutableArray alloc] init];
//listOfScoresTest = [[NSMutableDictionary alloc] initWithCapacity:5];
/*if ([scoreList count] == 0) {
for (int i = 0; i < 5; i++) {
[scoreList addObject:[NSNumber numberWithFloat:0.00]];
}
}*/
return [super init];
}
I should also mention that I created a new projectB and transferred my files/images from my old projectA to the new one because the old one wouldn't compile anymore because of some duplicate error. But i "cleaned all targets" again and it worked but that also has the same problem as my new projectB
Do you initialize scoreList ivar in init or so forth?
- (id)init
{
/* snip */
scoreList = [[NSMutableArray alloc] init];
return self;
}
- (void)dealloc
{
[scoreList release];
[super dealloc];
}
Ok Georg I reconsidered your suggestion about overwriting it later.
and it had something to do with my NSUserdefaults. I commented them out and now it add's the objects to my NSMutableArray. I'm pretty new to NSUserdefaults so I don't know exactly how to use it atm lol
-(void)save
{
//make another array to save the scores.
saveHighScore = [NSUserDefaults standardUserDefaults];
//[saveHighScore setObject:scoreListNew forKey:#"DodgerAppBeta"];
[saveHighScore synchronize];
//[[NSUserDefaults standardUserDefaults] setObject:scoreListTestOne forKey:#"DodgerBeta"];
//[[NSUserDefaults standardUserDefaults] synchronize];
}
-(void)load
{
saveHighScore = [NSUserDefaults standardUserDefaults];
//scoreListNew = [[saveHighScore objectForKey:#"DodgerAppBeta"] mutableCopy];
printableScoreList = [[saveHighScore objectForKey:#"DodgerAppBeta"] mutableCopy];
//NSLog(#"scoreListTestOne %#", scoreListTestOne);
//[printableScoreList addObject:[[NSUserDefaults standardUserDefaults] objectForKey:#"Dodger"]];
NSLog(#"PSL %#", printableScoreList);
}

Objective C - UITableView after calling reloadData my object properties are null/nil

I have a ViewController defined as follows:
#interface SectionController : UITableViewController {
NSMutableArray *sections;
}
- (void) LoadSections;
When LoadSection is call it makes a call to NSURLConnection to load a url which in turn calls
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
[connection release];
[responseData release];
NSDictionary *results = [responseString JSONValue];
NSMutableArray *jSections = [results objectForKey:#"Items"];
sections = [NSMutableArray array];
for (NSArray* jSection in jSections)
{
Section* section = [Section alloc];
section.Id = [jSection objectForKey:#"Id"];
section.Description = [jSection objectForKey:#"Description"];
section.Image = [jSection objectForKey:#"Image"];
section.Parent = [jSection objectForKey:#"Parent"];
section.ProductCount = [jSection objectForKey:#"ProductCount"];
[sections addObject:section];
[section release];
}
[jSections release];
[results release];
[delegate sectionsLoaded];
[self.view reloadData];
}
The data parses correctly and I now have sections filled with many items.
Calling [self.view reloadData] forces a callback to the delegate method cellForRowAtIndexPath which should then present the data into the cell however its at this point that sections is now nil again.
Can someone please point out my mistake? I must admit I am a newbie to objective c and it probably a pointer issue. What is need to do is retain the value of sections after calling reloadData.
Many thanks.
Seeing the new code the problem is obvious:
sections = [NSMutableArray array];
should become
[sections release];
sections = [[NSMutableArray alloc] init];
note that the array does not become again "nil", is instead deallocated and you get an invalid reference, which might (should) generate a crash on dereferencing.
I suggest you to read some articles on reference counted memory management as it might be not obvious if you are new to Objective-C, and often leads to mistake (i.e: autorelease is not magic at all)
best way to avoid all memory leaks here is just simply use #property (nonatomic, retain) NSMutableArray *sections; by using property you can be sure that all men management works will be correctly managed by system. Just don't forget that property retains value when you doing setSections:, so that you need to pass autoreleased object here.
self.sections = [NSMutableArray array];
...
[self.sections addObject:section];
Also to avoid all problem try to make all objects which should live only in this method autorelease. Like this:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSString *responseString = [[[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding] autorelease];
NSDictionary *results = [responseString JSONValue];
NSMutableArray *jSections = [results objectForKey:#"Items"];
self.sections = [NSMutableArray array];
for (NSArray* jSection in jSections) {
Section* section = [[[Section alloc] init] autorelease];
section.Id = [jSection objectForKey:#"Id"];
section.Description = [jSection objectForKey:#"Description"];
section.Image = [jSection objectForKey:#"Image"];
section.Parent = [jSection objectForKey:#"Parent"];
section.ProductCount = [jSection objectForKey:#"ProductCount"];
[self.sections addObject:section];
}
[delegate sectionsLoaded];
[self.view reloadData];
}
And also most of object you trying to release already autoreleased:
all params passed into your method shouldn't be released manually, check I think JSONValue also should returns autoreleased object and anything you getting by enumerating or by call objectForKey:

How to archive an NSArray of custom objects to file in Objective-C

Can you show me the syntax or any sample programs to archive an NSArray of custom objects in Objective-C?
Check out NSUserDefaults.
For Archiving your array, you can use the following code:
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:myArray] forKey:#"mySavedArray"];
And then for loading the custom objects in the array you can use this code:
NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
NSData *savedArray = [currentDefaults objectForKey:#"mySavedArray"];
if (savedArray != nil)
{
NSArray *oldArray = [NSKeyedUnarchiver unarchiveObjectWithData:savedArray];
if (oldArray != nil) {
customObjectArray = [[NSMutableArray alloc] initWithArray:oldSavedArray];
} else {
customObjectArray = [[NSMutableArray alloc] init];
}
}
Make sure you check that the data returned from the user defaults is not nil, because that may crash your app.
The other thing you will need to do is to make your custom object to comply to the NSCoder protocol. You could do this using the -(void)encodeWithCoder:(NSCoder *)coder and -(id)initWithCoder:(NSCoder *)coder methods.
If you want to save to a file (rather than using NSUserDefaults) you can use -initWithContentsOfFile: to load, and -writeToFile:atomically: to save, using NSArrays.
Example:
- (NSArray *)loadMyArray
{
NSArray *arr = [NSArray arrayWithContentsOfFile:
[NSString stringWithFormat:#"%#/myArrayFile", NSHomeDirectory()]];
return arr;
}
// returns success flag
- (BOOL)saveMyArray:(NSArray *)myArray
{
BOOL success = [myArray writeToFile:
[NSString stringWithFormat:#"%#/myArrayFile", NSHomeDirectory()]];
return success;
}
There's a lot of examples on various ways to do this here: http://www.cocoacast.com/?q=node/167

Why does my array stay empty?

Hi i'm a objC noob. I have a problem filling an NSMutableArray with objects.
for(id p in tmpArray){
Person *person = [[Person alloc] init];
person.usrName = p;
[persons addObject:person]; // after this line the persons
// array is still empty
[person release];
}
Persons is a property NSMutableArray and the problem is that it's empty. Is it the release of the person object too early or have I instanciated it wrong?
You need to initialize your array in the -init method, like this:
NSMutableArray *array = [[NSMutableArray alloc] init];
self.persons = array; // will be automatically retained
// because you're using the property
[array release]; // we alloced - we release it
Don't forget to release it:
-(void)dealloc {
self.persons = nil; // previous value of property 'persons' will be released
[super dealloc];
}
Make sure you've alloced and initialised the array before you try to add things to it