Object from NSUserDefaults releasing? - iphone

So I have a username saved in the UserDefaults. For some reason, I am experiencing some strange behavior.
I have a data controller that goes and fetches some data from the server based on the user name.
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSLog(#"NSUserDefaults dump: %#", [[NSUserDefaults standardUserDefaults] dictionaryRepresentation]);
userID = [prefs stringForKey:#"username"];
This works fine for the first few times, but after I do some random stuff and go back to try and reload the views, it crashes. It says:
-[CFString retain]: message sent to deallocated instance 0x4b18ff0
This is strange because it is stopping on the NSLog line. Has anyone seen this before or know why it may be happening??

How you defined your userID? If it's a property with a retain attribute you should call
self.userID = [prefs stringForKey:#"username"];
this way your string will be retained automatically. The string that is returned from stringForKey is autoreleased.

stringForKey returns an autorelease object which you aren't retaining, it's probably released as soon as that method finishes.
You need to retain that string either by doing so manually or using a property declaration with the retain setting.
And then you need to release it at some point (at least in dealloc)

Related

Save Third party Library Custom Object to NSUSerDefault

So,I am using the AFPhotoEditorController and there is property of it named as AFPhotoeditorSession that stores and tracks all user action.THis session class is custom object that inherits from NSObject,I have googled about how to save custom objects in NSUser Defaults and came to know that We can save that class if that class conforms to protocol NSCoding ,I don't know that whether I can change this class,Because it is only.h file that I have in my custom framework of AviarySDk.
NSData *myEncodedObject = [NSKeyedArchiver archivedDataWithRootObject:
[self.sessions objectAtIndex:0]]; // self.session an array of one object of AFPhotoeditorSession
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:myEncodedObject forKey:#"myEncodedObjectKey"];
[defaults synchronize];
NSData *data1 = [defaults objectForKey:#"myEncodedObjectKey"];
AFPhotoEditorSession *obj = (AFPhotoEditorSession *)[NSKeyedUnarchiver unarchiveObjectWithData: data1];
NSLog(#"%#",obj);
I am getting error like :
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<AFPhotoEditorSession 0x1c5fa0f0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key modified.'
And you can check the AFPhotoEditor Session class at PhotoEditorSession
You can make the class conform to NSCoding by declaring and defining a category which conforms to that protocol and implements the necessary methods.
Actually creating your own category means that you will have to have it compliant with that object as new ivars are added or changed, forever.
The right way to do this is to determine exactly what you would need to recreate an object of that class - the core values - then create keys and values for each and put them into a dictionary. Put the dictionaries into your user defaults. When you want to retrieve the values, pull out a dictionary, use those values to instantiate the objects.
The other option is to ask the author of AFPhotoeditor to do it, or do it yourself, and send him a pull request. But if you do it as a add on, you will have to update your category everytime the original framework changes.

Why UPDATE statement works with initWithFormat and NOT with stringWithFormat?

I was having an issue with my UPDATE statement as I was telling here: Update issue with sqliteManager
I found out that initWithFormat WORKS
NSString *sqlStr = [[NSString alloc] initWithFormat:#"UPDATE User SET Name = :Name WHERE Id = :Id"];
BUT not stringWithFormat:
NSString* sqlStr = [NSString stringWithFormat:#"UPDATE User SET Name = :Name WHERE Id = :Id"];
Why is this as such? I would like to understand the logic/reasoning behind..
I am guessing that it has to do with the memory management of the string, it might not have been sufficiently retained so it is getting cleaned up before for it is getting used. The difference between the two methods are defined here
I have just found something interesting from this thread: How to refresh TableView Cell data during an NSTimer loop
This, I believe, is the reasoning behind..
I quote what "petergb" said:
[NSString stringWithFormat:...] returns an autoreleased object. Autoreleased objects get released after control returns from the program's code to the apple-supplied run-loop code. They are more or less a convenience so we don't have to release all the little objects that we use once or twice here and there. (For example, imagine how tedious it would be if you had to release every string you created with the #"" syntax...)
We can tell stringWithFormat: returns an autoreleased object because, by convention, methods who's names don't start with alloc or copy always return auto-released objects. Methods like this are said to "vend" an object. We can use these objects in the immediate future, but we don't "own" it (i.e. we can't count on it being there after we return control to the system.) If we want to take ownership of a vended object, we have to call [object retain] on it, and then it will be there until we explicitly call [object release] or [object autorelease], and if we don't call release or autorelease on it before we lose our reference to it by changing the variable to something else, we will leak it.
Contrast with [[NSString alloc] initWithFormat:. This method "creates" an object. We own it. Again, it will be there until we explicitly call [object release].

NSUserDefaults: Question about difference between two approaches

Problem
I want to store a NSString in NSUserDefaults and retrieve it later. I have a question about two different retrieving methods. Now at the top of the file I have:
// String used to identify the update object in the user defaults storage.
static NSString * const kLastStoreUpdateKey = #"LastStoreUpdate";
Method 1
NSString *lastUpdate = [[NSUserDefaults standardUserDefaults] objectForKey:kLastStoreUpdateKey];
Method 2
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSString *myString = [prefs stringForKey:kLastStoreUpdateKey];
Are there are significant differences I should know about? Also, can someone please explain what exactly is objectForKey? Apple's API states: that it "Returns the object associated with the first occurrence of the specified default." What exactly do they mean by the "specified default?
Thank you!
Generally you should use method 1.
that is "objectForKey".
Because, you know that, whatever you have stored in NSUserDefault. So, at the time of retriving it, you can catch the object with proper class like NSString, Array or any other user defined.
genrally "stringForKey" is not used.
If you are storing ingteger, BOOL into NSUserDefault then you should use intForKey, BOOLforKey, etc..
Cheers.

NSUserDefaults overwriting new values with old

I have a bunch of userdefaults value that I use to load my UITableView in
-(void) prepareDisplay
{
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
table.dataSourceArray = [standardUserDefaults objectForKey:#"dataSource"];
}
-(void) viewDidLoad method
{
[self prepareDisplay];
}
and it works fine up to this point.
Then at a later point I change the userdefaults value and synchronize. Then I have to reload my table again. But before that I need to set the datasource array using the same
prepareDisplay () method.
Here I think that the instance of userdefaults that I created in my viewDidLoad earlier is overwriting or messing up with my userdefaults value and it calls up the old values again and not the newly set value.My newly set userdefaults value get overwritten with the old values. I have checked the plist file in the application sandbox after I do the userdefaults resetting and the values are reflected properly. But later, I don't how and when, they get overwritten with the old values. I am sure there is nothing I am doing explicitly to mess them up after resetting.
Can anybody help. Thanks
Actually you don't need an instance variable for NSUserDefaults because it is a singletone object. Avoid it. Additionally you may check the method - (BOOL)synchronize of the same object which Writes any modifications to the persistent domains to disk and updates all unmodified persistent domains to what is on disk.

inconsistant defaults (NSUserDefaults) behavior in iPhone simulator defaults

I've been experiencing very inconsistent results while developing an iPhone app and trying to save preferences via the standard NSUserDefaults mechanism. I am using code almost straight out of the iPhone Developer's Cookbook by Erica Sadun (fantastic book btw), it looks like this:
(void) updateDefaults
{
NSMutableArray *spells = [[NSMutableArray alloc] init];
NSMutableArray *locs = [[NSMutableArray alloc] init];
for (DragView *dv in [boardView subviews])
{
[spells addObject:[dv whichSpell]];
[locs addObject:NSStringFromCGRect([dv frame])];
}
[[NSUserDefaults standardUserDefaults] setObject:spells forKey:#"spells"];
[[NSUserDefaults standardUserDefaults] setObject:locs forKey:#"locs"];
[[NSUserDefaults standardUserDefaults] synchronize];
[spells release];
[locs release];
}
The values are saved, sometimes...and restored, sometimes. I can't get an exact bead on what does or does not make it work.
Does anyone else have any similar experiences? Any suggestions on what might make it work? Is the synchronize method the best way to force a disk write and make the values save, or is there something better (both for production, as well as simulator).
Thanks
Ryan
You should be using an NSKeyedArchiver for saving your arrays, such as:
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:spells] forKey:#"spells"];
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:locs] forKey:#"locs"];
[[NSUserDefaults standardUserDefaults] synchronize];
You should also make sure your spells class implements the NSCoding protocol (encodeWithCoder: and initWithCoder:), if it's a custom class. It looks like your locs are NSStrings, which will archive just fine.
You'll also need to do something like
NSData *dataRepresentingSavedSpells = [currentDefaults objectForKey:#"spells"];
if (dataRepresentingSavedSpells != nil)
{
NSArray *oldSpells = [NSKeyedUnarchiver unarchiveObjectWithData:dataRepresentingSavedSpells];
}
To load the old values from the defaults.
I use synchronize to write to disk on exit, and it's been very reliable in my experience.
On Mac OS X, and probably also on iPhone OS, the defaults database can only contain property list objects: NSString, NSNumber, NSArray, NSDictionary, and NSData.
It's not clear from your code snippet what type a spell is, but if it isn't one of the above, it won't get stored in the defaults database. Your options are:
Write a method that will convert your object to and from a plist representation. In other words, a method to create an NSDictionary from your object and another method to initialize the object using an NSDictionary. You could also choose to store it as an NSArray or NSString.
Make your object implement the NSCoding protocol and then used NSKeyedArchiver to convert the object to an NSData object, and NSKeyedUnarchiver to convert it back.