Advertise Mobile SDKs Books Events Forum News Social Networking Support Us
Follow @iphonedevsdk on Twitter

Interface 2, Advanced iOS
Mockup & Code Gen
($9.99)

Make your own iPhone apps
and run them live!
(free)

Pic Frame Dynamo: Photo Editing
($0.99)

Abiliator
($1.99)

Want your application or service advertised on iPhone Dev SDK?

Go Back   iPhone Dev SDK Forum > iPhone SDK Development Forums > iPhone SDK Development

Reply
 
LinkBack Thread Tools Display Modes
Old 03-20-2011, 05:20 PM   #1 (permalink)
Registered Member
 
Join Date: Feb 2011
Posts: 122
architectpianist is on a distinguished road
Question Array in NSUserDefaults

Hi,
I'm trying to save an array of objects (custom objects) to NSUserDefaults. With this code, though:
Code:
	NSMutableArray *filesCopy = [[[NSUserDefaults standardUserDefaults] objectForKey:@"projects"] mutableCopy];
	[filesCopy addObject:file]; //File is a custom object
	[[NSUserDefaults standardUserDefaults] setObject:filesCopy forKey:@"projects"];
it gives me this in the log:
Code:
*** -[NSUserDefaults setObject:forKey:]: Attempt to insert non-property value '(
    "<MyCustomObject: 0x4e274e0>"
)' of class '__NSCFArray'
Am I not allowed to put custom objects in arrays in NSUserDefaults? Is there another way I should do this?
Thanks!
architectpianist is offline   Reply With Quote
Old 03-20-2011, 05:26 PM   #2 (permalink)
Reading the Documentation
 
baja_yu's Avatar
 
Join Date: Sep 2010
Location: 45.255019,19.844908
Posts: 5,414
baja_yu has a spectacular aura about
Default

You can't save custom objects to NSUserDefaults.
baja_yu is offline   Reply With Quote
Old 03-20-2011, 05:40 PM   #3 (permalink)
Registered Member
 
Join Date: Dec 2008
Location: UK
Posts: 1,896
harrytheshark is on a distinguished road
Default

You can save them, they just need to be encoded to NSData first.

You need two methods in the custom objects.
Code:
// Do the same for each thing you want to save.
- (id)initWithCoder:(NSCoder *)aDecoder {
	
	if ((self = [super init])){
		[self setSomeProperty:[aDecoder decodeObjectForKey:@"somePropertyName"]];
	}
	
	return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder {
	[aCoder encodeObject:someProperty forKey:@"somePropertyName"];
}
You'll then need to loop through your array, encoding each object:
Code:
- (void)saveObjects {
	
	NSMutableArray * encodedObjects = [[NSMutableArray alloc] init];
	for (CustomObject * customObject in originalObjectsArray){
		[encodedObjects addObject:[NSKeyedArchiver archivedDataWithRootObject:customObject]];
	}
	
	[[NSUserDefaults standardUserDefaults] setObject:encodedObjects forKey:@"EncodedObjects"];
        [encodedObjects release];
}
To load them back up again:
Code:
- (void)loadObjects {
	
	NSMutableArray * customObjects = [[NSMutableArray alloc] init]
	NSArray * encodedObjects = [[NSUserDefaults standardUserDefaults] objectForKey:@"EncodedObjects"];
	
	for (NSData * encodedObject in encodedObjects){
		[customObjects addObject:[NSKeyedUnarchiver unarchiveObjectWithData:encodedObject]];
	}
	
	// customObjects now contains all the objects as they originally were.
        [customObjects release];
}
harrytheshark is offline   Reply With Quote
Old 03-20-2011, 06:45 PM   #4 (permalink)
Cocoa Junkie
 
Duncan C's Avatar
 
Join Date: Dec 2008
Location: Northern Virginia
Posts: 6,003
Duncan C has a spectacular aura about
Default

Quote:
Originally Posted by harrytheshark View Post
You can save them, they just need to be encoded to NSData first.

You need two methods in the custom objects.
Code:
// Do the same for each thing you want to save.
- (id)initWithCoder:(NSCoder *)aDecoder {
	
	if ((self = [super init])){
		[self setSomeProperty:[aDecoder decodeObjectForKey:@"somePropertyName"]];
	}
	
	return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder {
	[aCoder encodeObject:someProperty forKey:@"somePropertyName"];
}
You'll then need to loop through your array, encoding each object:
Code:
- (void)saveObjects {
	
	NSMutableArray * encodedObjects = [[NSMutableArray alloc] init];
	for (CustomObject * customObject in originalObjectsArray){
		[encodedObjects addObject:[NSKeyedArchiver archivedDataWithRootObject:customObject]];
	}
	
	[[NSUserDefaults standardUserDefaults] setObject:encodedObjects forKey:@"EncodedObjects"];
        [encodedObjects release];
}
To load them back up again:
Code:
- (void)loadObjects {
	
	NSMutableArray * customObjects = [[NSMutableArray alloc] init]
	NSArray * encodedObjects = [[NSUserDefaults standardUserDefaults] objectForKey:@"EncodedObjects"];
	
	for (NSData * encodedObject in encodedObjects){
		[customObjects addObject:[NSKeyedUnarchiver unarchiveObjectWithData:encodedObject]];
	}
	
	// customObjects now contains all the objects as they originally were.
        [customObjects release];
}
Actually, as long as the objects in the array support the NSCoding protocol, you can encode the entire array with one call:

Code:
NSData* arrayData = [NSKeyedArchiver archivedDataWithRootObject: theArray];
You could then save that data object into user defaults.


To reconstitute the array after reading the data object from user defaults, just use a line of code like this:

Code:
theArray = [NSKeyedUnarchiver unarchiveObjectWithData];
To make this work, you have to add the NSCoding methods encodeWithCoder: and initWithCoder: to your object. Those methods should encode/decode all the instance variables needed to save/reconstruct the object. It's not that hard. Take a look at the methods in NSKeyedArchiver (for encodeWithCoder) and NSKeyedUnarchiver (for initWithCoder).
__________________
Regards,

Duncan C
WareTo

Check out our apps in the Apple App store


Check out this password generator app that shows various techniques including using a data container singleton object to share data between objects in your project.

See this tutorial on using UIView animations and layer animations:

See this thread on generating random, non-repeating text

Check out a very cool Macintosh Kaleidoscopes app called ScopeWorks that we released to the Mac App store.
Duncan C is offline   Reply With Quote
Old 03-20-2011, 11:20 PM   #5 (permalink)
Registered Member
 
Join Date: Feb 2011
Posts: 122
architectpianist is on a distinguished road
Default

Any idea why this is giving me an EXC_BAD_ACCESS?
Code:
	NSMutableArray *filesCopy = [NSKeyedUnarchiver unarchiveObjectWithData:[[NSUserDefaults standardUserDefaults] objectForKey:@"projects"]];
	NSLog(@"When adding a file, %@", filesCopy);
	[filesCopy addObject:file];
	NSData *data = [NSKeyedArchiver archivedDataWithRootObject:filesCopy];
I want to basically add an object to the array in the NSUserDefaults, so looks like I have to load it into filesCopy, change it, and load it back up to NSUserDefaults, right? The NSLog shows that filesCopy contains exactly what I want it to contain. However, it's the last line that's giving the EXC_BAD_ACCESS.
architectpianist is offline   Reply With Quote
Old 03-21-2011, 05:20 AM   #6 (permalink)
Cocoa Junkie
 
Duncan C's Avatar
 
Join Date: Dec 2008
Location: Northern Virginia
Posts: 6,003
Duncan C has a spectacular aura about
Default

Quote:
Originally Posted by architectpianist View Post
Any idea why this is giving me an EXC_BAD_ACCESS?
Code:
	NSMutableArray *filesCopy = [NSKeyedUnarchiver unarchiveObjectWithData:[[NSUserDefaults standardUserDefaults] objectForKey:@"projects"]];
	NSLog(@"When adding a file, %@", filesCopy);
	[filesCopy addObject:file];
	NSData *data = [NSKeyedArchiver archivedDataWithRootObject:filesCopy];
I want to basically add an object to the array in the NSUserDefaults, so looks like I have to load it into filesCopy, change it, and load it back up to NSUserDefaults, right? The NSLog shows that filesCopy contains exactly what I want it to contain. However, it's the last line that's giving the EXC_BAD_ACCESS.
When you save an object into an archive and read it back, you get back an immutable version, even if the object you saved was mutable. The same is true for plists.

The second-to-last line is probably the line that's crashing, since you can't add an object to an NSArray, only an NSMutableArray. Add a line like this:


Code:
	NSMutableArray *filesCopy = [NSKeyedUnarchiver unarchiveObjectWithData:[[NSUserDefaults standardUserDefaults] objectForKey:@"projects"]];
	filesCopy = [filesCopy mutableCopy];
	NSLog(@"When adding a file, %@", filesCopy);
	[filesCopy addObject:file];
	NSData *data = [NSKeyedArchiver archivedDataWithRootObject:filesCopy];
That should fix the crash, unless there's a problem with the object "file" you're adding to the array.
__________________
Regards,

Duncan C
WareTo

Check out our apps in the Apple App store


Check out this password generator app that shows various techniques including using a data container singleton object to share data between objects in your project.

See this tutorial on using UIView animations and layer animations:

See this thread on generating random, non-repeating text

Check out a very cool Macintosh Kaleidoscopes app called ScopeWorks that we released to the Mac App store.
Duncan C is offline   Reply With Quote
Old 03-21-2011, 09:48 AM   #7 (permalink)
Registered Member
 
Join Date: Feb 2011
Posts: 122
architectpianist is on a distinguished road
Default

Well, it's definitely the last line that's causing a crash. Archiving "file" doesn't help either. Maybe I should individually archive each object in the array?
architectpianist is offline   Reply With Quote
Old 03-21-2011, 10:49 AM   #8 (permalink)
Cocoa Junkie
 
Duncan C's Avatar
 
Join Date: Dec 2008
Location: Northern Virginia
Posts: 6,003
Duncan C has a spectacular aura about
Default

Quote:
Originally Posted by architectpianist View Post
Well, it's definitely the last line that's causing a crash. Archiving "file" doesn't help either. Maybe I should individually archive each object in the array?
You can try that, but I doubt it will fix anything.

A bad access error usually means something has been over-released, and you're crashing when you try to reference it.

Try commenting out the line that adds the new object to the array and see if you can write it back to defaults without changing anything.

Take careful note of exactly what source line is causing the crash. Is it crashing in the NSCoding method for your class, by any chance?

You might want to post your NSCoding methods (initWithCoder and encodeWithCoder)
__________________
Regards,

Duncan C
WareTo

Check out our apps in the Apple App store


Check out this password generator app that shows various techniques including using a data container singleton object to share data between objects in your project.

See this tutorial on using UIView animations and layer animations:

See this thread on generating random, non-repeating text

Check out a very cool Macintosh Kaleidoscopes app called ScopeWorks that we released to the Mac App store.
Duncan C is offline   Reply With Quote
Old 03-21-2011, 11:57 AM   #9 (permalink)
Registered Member
 
Join Date: Feb 2011
Posts: 122
architectpianist is on a distinguished road
Default

It actually looks like it's encoding the objects fine, and yet when I place a breakpoint in the encodeWithCoder method and on the line immediately after [NSKeyedArchiver archivedDataWithRootObject], I press Continue for the first breakpoint, and then it gives me an EXC_BAD_ACCESS before the second breakpoint is even reached.
I'm archiving the NSMutableArray of projects.
Code:
		NSMutableArray *projectsCopy = [[NSKeyedUnarchiver unarchiveObjectWithData:[[NSUserDefaults standardUserDefaults] objectForKey:@"projects"]] mutableCopy];
		[projectsCopy replaceObjectAtIndex:[[NSUserDefaults standardUserDefaults] integerForKey:@"current project"] withObject:self.currentProject];
		NSData *data = [NSKeyedArchiver archivedDataWithRootObject:projectsCopy]; //Crashes between encodeWithCoder and the next line here
		[[NSUserDefaults standardUserDefaults] setObject:data forKey:@"projects"];
My NSCoder methods are really simple:
Code:
- (id) initWithCoder:(NSCoder *)aDecoder
{
	self = [super init];
	if (self) 
	{
		[self setTextBox:[aDecoder decodeObjectForKey:@"textBox"]];
                //etc. for each property
        }
        return self;
}

- (void) encodeWithCoder:(NSCoder *)aCoder
{
	[aCoder encodeObject:self.textBox forKey:@"textBox"];
        //etc. for each property
}
Am I missing anything?
architectpianist is offline   Reply With Quote
Old 03-21-2011, 12:20 PM   #10 (permalink)
Cocoa Junkie
 
Duncan C's Avatar
 
Join Date: Dec 2008
Location: Northern Virginia
Posts: 6,003
Duncan C has a spectacular aura about
Default

Quote:
Originally Posted by architectpianist View Post
It actually looks like it's encoding the objects fine, and yet when I place a breakpoint in the encodeWithCoder method and on the line immediately after [NSKeyedArchiver archivedDataWithRootObject], I press Continue for the first breakpoint, and then it gives me an EXC_BAD_ACCESS before the second breakpoint is even reached.
I'm archiving the NSMutableArray of projects.
Code:
		NSMutableArray *projectsCopy = [[NSKeyedUnarchiver unarchiveObjectWithData:[[NSUserDefaults standardUserDefaults] objectForKey:@"projects"]] mutableCopy];
		[projectsCopy replaceObjectAtIndex:[[NSUserDefaults standardUserDefaults] integerForKey:@"current project"] withObject:self.currentProject];
		NSData *data = [NSKeyedArchiver archivedDataWithRootObject:projectsCopy]; //Crashes between encodeWithCoder and the next line here
		[[NSUserDefaults standardUserDefaults] setObject:data forKey:@"projects"];
My NSCoder methods are really simple:
Code:
- (id) initWithCoder:(NSCoder *)aDecoder
{
	self = [super init];
	if (self) 
	{
		[self setTextBox:[aDecoder decodeObjectForKey:@"textBox"]];
                //etc. for each property
        }
        return self;
}

- (void) encodeWithCoder:(NSCoder *)aCoder
{
	[aCoder encodeObject:self.textBox forKey:@"textBox"];
        //etc. for each property
}
Am I missing anything?

Can you post your entire initWithCoder routine, along with the property declarations for the object's properties?

My bet is that your init routine is setting up some property as auto-released. It's fine until you visit the event loop, and then the property gets deallocated, and your property now points to a dead object.

Have you tried running static analysis on the program? That's worth a try.
__________________
Regards,

Duncan C
WareTo

Check out our apps in the Apple App store


Check out this password generator app that shows various techniques including using a data container singleton object to share data between objects in your project.

See this tutorial on using UIView animations and layer animations:

See this thread on generating random, non-repeating text

Check out a very cool Macintosh Kaleidoscopes app called ScopeWorks that we released to the Mac App store.
Duncan C is offline   Reply With Quote
Reply

Bookmarks

Tags
custom, nsarray, nsuserdefaults

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are On



» Advertisements
» Online Users: 345
11 members and 334 guests
condor304, Creativ, Domele, dreamdash3, laureix68, LEARN2MAKE, michelle, mistergreen2011, Sami Gh, shagor012, tinamm64
Most users ever online was 1,387, 04-10-2012 at 04:21 AM.
» Stats
Members: 175,662
Threads: 94,119
Posts: 402,896
Top Poster: BrianSlick (7,990)
Welcome to our newest member, shagor012
Powered by vBadvanced CMPS v3.1.0

All times are GMT -5. The time now is 01:31 AM.
Powered by vBulletin® Version 3.8.0
Copyright ©2000 - 2012, Jelsoft Enterprises Ltd.
Search Engine Friendly URLs by vBSEO 3.3.0