I'm having trouble in writing mutable dictionary to a file. Here's the code that I'm writing. I'm reading the file like below: (first time when app is ran, it won't have any data)
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocume开发者_Python百科ntDirectory, NSUserDomainMask, YES);
NSString* documentsDirectory = [paths objectAtIndex:0];
self.favSaveFilePath = [[NSString alloc] initWithString:[documentsDirectory stringByAppendingPathComponent:@"Favorites.plist"]];
if([fm fileExistsAtPath:favSaveFilePath])
{
favoriteJokesDictionary = [[NSMutableDictionary alloc] initWithContentsOfFile:self.favSaveFilePath];
favoriteJokes = [[NSMutableArray alloc] initWithArray:[self.favoriteJokesDictionary objectForKey:@"FavoriteJokes"]];
return;
}
I'm adding new dictionary to array as below:
-(BOOL)addJokeToFavorite:(NSDictionary*)joke
{
BOOL success = NO;
[self.favoriteJokes addObject:joke];
success = [self.favoriteJokesDictionary writeToFile:self.favSaveFilePath atomically:YES];
return success;
}
I don't know why its not wring the dictionary to file. Can any one point me the mistake that I'm doing?
The Cocoa API is very specific about what kind of values can legally be in a dictionary when it is written out to file. One particular limitation that has been known to cause problems and which is not thoroughly discussed in the official API documentation is that all of the keys in your dictionary must be of type NSString (and your values must be one of NSData, NSDate, NSNumber, NSString, NSArray, or NSDictionary), even though the dictionary itself supports keys and values of any object type.
The reason for this restriction has to do with the fact that the dictionary gets stored as an XML document (or "property-list", in Apple-speak) by default. So I'd recommend verifying that your dictionary meets the requirements to be saved to file, and restructuring it if it doesn't.
Or, if restructuring your dictionary is not feasible, you might want to take a look at the NSKeyedArchiver class instead.
Looks like your favoriteJokesDictionary
doesn't exist at all when there's no save file to initialize it from. Hence, the attempt to create that save file from the non-existing dictionary in addJokeToFavorite:
doesn't do anything, either. You need to create an empty dictionary when there is no save file to begin with.
I was just typing this up when Ulrich posted the correct answer. I'll add the rest here for extra clarification.
As Ulrich pointed out, when you don't have a file, your code just skips the initialization of favoriteJokesDictionary
and favoriteJokes
. When you try to write out the object later, favoriteJokesDictionary
is nil and so the method doesn't do anything.
You can just create an empty NSMutableDictionary if the file doesn't exist yet, and since you're retaining part of the dictionary in a separate ivar as well you should create that at the same time. Something like this would work:
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentsDirectory = [paths objectAtIndex:0];
self.favSaveFilePath = [[NSString alloc] initWithString:[documentsDirectory stringByAppendingPathComponent:@"Favorites.plist"]];
if([fm fileExistsAtPath:favSaveFilePath])
{
favoriteJokesDictionary = [[NSMutableDictionary alloc] initWithContentsOfFile:self.favSaveFilePath];
favoriteJokes = [[NSMutableArray alloc] initWithArray:[self.favoriteJokesDictionary objectForKey:@"FavoriteJokes"]];
} else {
favoriteJokesDictionary = [[NSMutableDictionary alloc] init];
favoriteJokes = [[NSMutableArray alloc] init];
[favoriteJokesDictionary setObject:favoriteJokes forKey:@"JokesArrayKey"];
}
That said, I think you could probably simplify your data model there a bit (unless there much more going on that you're not showing). If you need a dictionary because you're storing more than just jokes, then what you have is fine. However, I wouldn't retain the jokes array, because if you later set a new object in the array for your JokesArrayKey, your favoriteJokes ivar is still going to be pointing to the old array.
I would just grab the object from the dictionary instead whenever you need it:
NSMutableArray *jokes = [favoriteJokesDictionary objectForKey:@"JokesArrayKey"];
Even better, if you are working with the jokes array quite a bit, just pull it out into its own file. You can write an NSArray out as a plist just like you can with an NSDictionary. If you're only using the NSDictionary as a shell to write out a .plist, you can skip it entirely.
One other thing: Assuming your favSaveFilePath
property is marked "retain", you have a small memory leak in the first section. You're creating a new retained string with [[NSString alloc] initWithString:...]
, and then retaining it again by assigning it to your property. You probably just want:
self.favSaveFilePath = [documentsDirectory stringByAppendingPathComponent:@"Favorites.plist"];
Referring to the answer aroth gave, non-ns objects in your dictionary may be more subtle than you realise.
EG: I had an NSArray
of NSDictionary
objects that I was turning into NSObject
subclassed items, and adding these to a NSMutableDictionary
.
Even though the subclass only contained NS based objects, strings etc; the NSMutableDictionary
itself wouldn't save.
The solution was to save the NSArray
of NSDictionary
items and turn them into my custom subclass after loading the file.
精彩评论