I used the following code to write values to dictionary, but when add new one to the dictionary it is not updating, it just displays the plist with only recently added value and it is crashing too.
nameString=nameTxt.text;
NSFileManager *mngr=[NSFileManager defaultManager];
NSArray *docDir=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docPath=[docDir objectAtIndex:0];
NSString *filePath=[docPath stringByAppendingPathComponent:#"score.plist"];
NSString *bundlePath=[[NSBundle mainBundle] pathForResource:#"score" ofType:#"plist"];
if ([mngr fileExistsAtPath:filePath]) {
NSLog(#"File exists");
}
else {
NSLog(#"NO file exists");
[[NSFileManager defaultManager] copyItemAtPath:bundlePath toPath:filePath error:NULL];
}
dict=[[NSMutableDictionary alloc]init];
dict=[NSMutableDictionary dictionaryWithContentsOfFile:filePath];
NSLog(#"dict is %#",dict);
[dict setObject:nameString forKey:#"100"];
[dict writeToFile:filePath atomically:YES];
[dict release];
I get crash when I used the last line "[dict release]"
I have a score.plist file in my bundle.
The crash is due to this line,
dict=[[NSMutableDictionary alloc]init];
dict=[NSMutableDictionary dictionaryWithContentsOfFile:filePath];
first line you are allocting memory and then you are overwriting the dict param to link to static dictionary which is not owned by you. So the old one is leaked and when you are releasing it tries to release the static one.
Instead of that use,
dict=[NSMutableDictionary dictionaryWithContentsOfFile:filePath];
and do NOT use release statement. Since you dont own it, you dont have to release it.
Check this
This is a simple memory problem.Along with solving the problem you have to understand the problem.
The dict is a NSMutableDictionary that you declared globally. And so that you can alloc it for using this so that you won't lose the scope of the dictionary.
So in the beginning say 'ViewDidLoad:' , you can alloc and init this as
dict=[[NSMutableDictionary alloc]init];
or in the present condition you can use like
dict=[[NSMutableDictionary alloc]initWithContentsOfFile: filePath];
So that you can alloc the dictionary with the score.plist file and everything will work fine.
What happended in your case is you alloced the dict. But in the next line you replace the alloced object of dict with autoreleaed object by the statement
dict=[NSMutableDictionary dictionaryWithContentsOfFile:filePath];
As the class methods always returns autoreleased objects, when you try to release the object which is autoreleased, it crashes. :-)
Hope you got the idea.
Now the solution is you can change the line
dict=[[NSMutableDictionary alloc]init];
To
dict=[[NSMutableDictionary alloc]initWithContentsOfFile: filePath];
And remove the line
dict=[NSMutableDictionary dictionaryWithContentsOfFile:filePath];
Everything will work. Happy Coding. :-)
Related
I am studying iPhone development and facing a problem with a reading/writing plist file. I followed an example from a iPhone development book but keep getting an error message when running.
The error message says : 2012-04-26 00:21:09.759 FileHandling[5915:207] -[__NSCFDictionary addObject:]: unrecognized selector sent to instance 0x685ac40
Here is the example code (it seems fine to me...though):
NSString *plistFileName = [[self documentPath] stringByAppendingPathComponent: #"Apps.plist"];
NSLog(#"Where is the file? => %#", plistFileName);
if ([[NSFileManager defaultManager] fileExistsAtPath:plistFileName]) {
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:plistFileName];
for (NSString *category in dict) {
NSLog(#"%#", category);
NSLog(#"=========");
NSArray *titles = [dict valueForKey:category];
for (NSString *title in titles) {
NSLog(#"%#", title);
}
}
} else {
NSString *plistPath = [[NSBundle mainBundle] pathForResource:#"Apps" ofType: #"plist"];
NSLog(#"%#", plistPath);
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile: plistPath];
NSLog(#"Let's take a look : %#", dict);
NSMutableDictionary *copyOfDict = [dict mutableCopy];
NSLog(#"Let's look at the mutable dictationary : %#", copyOfDict);
NSArray *categoriesArray = [[copyOfDict allKeys] sortedArrayUsingSelector: #selector(compare:)];
for (NSString *cateogry in categoriesArray) {
NSArray *titles = [dict valueForKey: cateogry];
NSMutableArray *mutableTitles = [titles mutableCopy];
[mutableTitles addObject: #"New App Title"];
[copyOfDict setObject: mutableTitles forKey:cateogry];
}
NSString *fileName = [[self documentPath] stringByAppendingPathComponent: #"Apps.plist"];
[copyOfDict writeToFile: fileName atomically:YES];
}
According to the error message, the problem is occurring in the call to addObject: on an __NSCFDictionary. This means that, at runtime, a dictionary received a message to add an object.
However, in this code snippet, addObject: is apparently being sent to an NSMutableArray. This probably means that each object titles you're retrieving from dict in the last for-loop is not an array, but in fact another dictionary, that your code is simply referring to as an array.
Indeed, your code does seem well-formed, so check the well-formedness of your source plist; open it up in a plain text editor. Also, you use a ton of logging, so confirm this way: in the output, dictionaries (including the root entry) are denoted by {curly = braces}, where arrays are denoted by (round parentheses).
i am trying to parse a text file saved in doc dir below show is the code for it
NSArray *filePaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
NSString *docDirPath=[filePaths objectAtIndex:0];
NSString *filePath=[docDirPath stringByAppendingPathComponent:#"SKU.txt"];
NSError *error;
NSString *fileContents=[NSString stringWithContentsOfFile:filePath];
NSLog(#"fileContents---%#",fileContents);
if(!fileContents)
NSLog(#"error in reading file----%#",error);
NSArray *values=[fileContents componentsSeparatedByString:#"\n"];
NSLog(#"values-----%#",values);
NSMutableArray *parsedValues=[[NSMutableArray alloc]init];
for(int i=0;i<[values count];i++){
NSString *lineStr=[values objectAtIndex:i];
NSLog(#"linestr---%#",lineStr);
NSMutableDictionary *valuesDic=[[NSMutableDictionary alloc]init];
NSArray *seperatedValues=[[NSArray alloc]init];
seperatedValues=[lineStr componentsSeparatedByString:#","];
NSLog(#"seperatedvalues---%#",seperatedValues);
[valuesDic setObject:seperatedValues forKey:[seperatedValues objectAtIndex:0]];
NSLog(#"valuesDic---%#",valuesDic);
[parsedValues addObject:valuesDic];
[seperatedValues release];
[valuesDic release];
}
NSLog(#"parsedValues----%#",parsedValues);
NSMutableDictionary *result;
result=[parsedValues objectAtIndex:1];
NSLog(#"res----%#",[result objectForKey:#"WALM-FT"]);
The problem what i am facing is when i try to print lineStr ie the data of the text file it is printing as a single string so i could not able to get the contents in line by line way please help me solve this issue.
Instead use:
- (NSArray *)componentsSeparatedByCharactersInSet:(NSCharacterSet *)separator
it covers several different newline characters.
Example:
NSArray *values = [fileContents componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
for (NSString *lineStr in values) {
// Parsing code here
}
ALso seperatedValues is over released. First one is created with alloc init, then on the next line it is replaced by the method componentsSeparatedByString. So the first one od lost without being released, that is a leak. Later the seperatedValues created by componentsSeparatedByString is released but it is already auto released by componentsSeparatedByString to that is an over release;
Solve all the retain/release/autorelease problem with ARC (Automatic Reference Counting).
Here is a version that uses convenience methods and omits over release:
NSArray *values = [fileContents componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
for (NSString *lineStr in values) {
NSArray *seperatedValues = [lineStr componentsSeparatedByString:#","];
NSString *key = [seperatedValues objectAtIndex:0];
NSDictionary *valuesDic = [NSDictionary dictionaryWithObject:seperatedValues forKey:key];
[parsedValues addObject:valuesDic];
}
NSLog(#"parsedValues---%#",parsedValues);
Are you sure the line separator used in your text file is \n and not \r (or \r\n)?
The problem may come from this, explaining why you don't manage to split the files into different lines.
i've been reading for hours, searched apple's doc, stackoverflow, can't understand what i'm doing wrong....
when i use this data from a XML plist on my UITableViewController:
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *path = [[NSBundle mainBundle] pathForResource:#"arrayofstrings"
ofType:#"plist"];
NSData *myData = [NSData dataWithContentsOfFile:path];
NSString *error;
NSPropertyListFormat format;
myArray = [NSPropertyListSerialization propertyListFromData:myData
mutabilityOption:NSPropertyListImmutable
format:&format
errorDescription:&error];
}
my tableview shows the first visible rows just fine but crashes when trying to scroll.
it doesn't happen when instead of the XML data i use something like this:
- (void)viewDidLoad
{
[super viewDidLoad];
myArray = [[NSArray alloc] initWithObjects:#"thing1", #"thing2", #"thing3", #"thing4", #"thing5",#"thing6", #"thing7", #"thing8", #"thing9", #"thing10",
#"thing11",#"thing12", #"thing13", #"thing14", nil];
}
this way the tableview scrolls just fine. what's my problem?! Is the plist conversion to array supposed to be in any other way?
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *path = [[NSBundle mainBundle] pathForResource:#"arrayofstrings"
ofType:#"plist"];
NSData *myData = [NSData dataWithContentsOfFile:path];
NSString *error;
NSPropertyListFormat format;
myArray = [[NSPropertyListSerialization propertyListFromData:myData
mutabilityOption:NSPropertyListImmutable
format:&format
errorDescription:&error] retain];
}
The return value from propertyListFromData:mutabilityOption:format:errorDescription is autoreleased. Make sure you call retain so it doesn't get released out from under you at the end of the current run loop.
The second method works because creating the NSArray with alloc/init leaves the array with a retain count of 1.
The problem is that in the first case your call to [NSPropertyListSerialization propertyListFromData: returns an NSArray with no retain count on it (note the method doesn't have alloc, new, or copy in the name) - and then you don't retain this NSArray. Hence, the array is getting deallocated shortly after, and your code crashes trying to access garbage memory.
In the second case, you are making an NSArray using alloc - this returns an NSArray with a retain count of 1, which means it isn't deallocated (until a release gets called at some point).
To fix this, in your first case you want to assign the array as follows:
self.myArray = ...
The self. is the crucial part here (assuming you have declared the myArray property as retain).
There's plenty of resources and blog posts available concerning memory management.
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [path objectAtIndex:0];
NSString *databasePath = [documentsDirectory stringByAppendingPathComponent:#"DB"];
NSString *fileName = [newWordbookName stringByAppendingString:#".csv"];
NSString *fullPath = [databasePath stringByAppendingPathComponent:fileName];
[[NSFileManager defaultManager] createFileAtPath:fullPath contents:nil attributes:nil];
[databasePath release];
//[fileName release]; Error!
//[fullPath release]; Error!
//NSLog(#"#1 :databasePath: %d",[databasePath retainCount]);
//NSLog(#"#1 :fileName: %d",[fileName retainCount]);
//NSLog(#"#1 :fullPath: %d",[fullPath retainCount]);
I'm using this code and want to release NSString* ..
so, I declare fileName, fullPath, and databasePath of NSString.
database is released but fileName, fullpath doesn't release. I don't know why it happens.
I know that NSArray is Autoreleased. But is documentsDirectory autoreleased?
(newWordbookName is nsstring type)
I hope that I look through a document about iPhone memory management.
By convention the only two cases when a method returns a retained object are constructors i.e. alloc, new etc. and object-copying methods (containing copy in their name).
In all other cases the object is expected to be autoreleased, unless explicitly stated otherwise in the documentation.
This is the complete memory management documentation:
Cocoa Memory Management
You should not be calling release on any of the objects in the above code.
The reason the NSArray is autorelease'd is the same reason all the other objects are autorelease'd: the methods that assigned them their values called autorelease on them before they returned. In general, you can assume methods return autorelease'd objects if they do not have the word "create" or "new" in them. That is the general Cocoa convention. (Although 3rd party code may be goofy and do things differently, so caveat programmer).
You only really need to worry about objects you alloc or copy yourself; in other words, pair every alloc or copy with a release or autorelease.
I've been trying to save a plist of a NSDictionary to my app's Documents folder. I haven't tried this on the device yet but I'd like it to work on the simulator for testing purposes. The [self createDictionaryFromChoreList] method just creates a NSDictionary from some data in another class of mine. I've pretty much copied/pasted this code from the web documents and when I go to see if the file was saved or not, I find that it isn't. Here is the method block.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *plistName = [[NSString alloc] initWithFormat:#"%#chores.plist", self.firstName];
NSString *path = [documentsDirectory stringByAppendingPathComponent:plistName];
NSDictionary *choresDictionary = [[NSDictionary alloc] initWithDictionary:[self createDictionaryFromChoreList]];
[choresDictionary writeToFile:path atomically:YES];
Any help is greatly appreciated. Thanks in advance.
-S
You should also capture the BOOL returned by writeToFile:atomically:. That will tell you if the write succeeded or not.
Also, are you sure you are looking in the right documents folder? If you have more than one app in the simulator its easy to open the wrong app's documents folder in the Finder. I did that once and it cost me a couple of hours of frustration.
Edit01:
writeToFile:atomically: returning false explains why no file exist. The simplest explanation is that something in the dictionary is not a property list object.
From the NSDictionary docs:
This method recursively validates that
all the contained objects are property
list objects (instances of NSData,
NSDate, NSNumber, NSString, NSArray,
or NSDictionary) before writing out
the file, and returns NO if all the
objects are not property list objects,
since the resultant file would not be
a valid property list.
It just takes one non-plist object buried deep in a dictionary to prevent it from being converted to a plist.
Don't forget serialize the plist data:
Here is a snippet of code that I use for writing information to a plist
NSString *errorString;
NSData *data = [NSPropertyListSerialization dataFromPropertyList:plistDict
format:NSPropertyListXMLFormat_v1_0
errorDescription:&errorString];
[plistDict release];
if (!data) {
NSLog(#"error converting data: %#", errorString);
return NO;
}
if ([data writeToFile:[XEraseAppDelegate loadSessionPlist] atomically: YES]) {
return YES;
} else {
NSLog(#"couldn't write to new plist");
return NO;
}
This is something I whipped up really quickly and it correctly writes a plist directory of name and company to the documents directory. I have a feeling your dictionary creation method might have an issue. Try this out for yourself, then add your code and make sure it works.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *plistDirectory = [paths objectAtIndex:0];
NSString *plistPath = [plistDirectory stringByAppendingPathComponent:#"userCompany.plist"];
NSArray *userObjects = [[NSArray alloc] initWithObjects:#"Joe", #"Smith", #"Smith Co", nil];
NSArray *userKeys = [[NSArray alloc] initWithObjects:#"First Name", #"Last Name", #"Company", nil];
NSDictionary *userSettings = [[NSDictionary alloc] initWithObjects:userObjects forKeys:userKeys];
[userSettings writeToFile:plistPath atomically:YES];
Is it correct, that the name of file your writing to is:
SOEMTHINGchores.plist?
Created via:
NSString *plistName = [[NSString alloc] initWithFormat:#"%#chores.plist", self.firstName];
Also, what is the output of:
[choresDictionary print];
Some additional info would help to debug this.
Where exactly are you looking for the file?
I have the exact same code and it works fine for me.
Just that I have to dig deep to get the file. Something like:
/Users/myUserName/Library/Application Support/iPhone Simulator/User/Applications/0E62A607-8EEB-4970-B198-81CE4BDDB7AA/Documents/data.plist
And the HEX number in the path changes with every run. So I print the file path with every run.
Insert a break point at
NSDictionary *choresDictionary = [[NSDictionary alloc] initWithDictionary:[self createDictionaryFromChoreList]];
now when you step out drag your mouse over choresDictionary and check in the tooltip that its size is not 0x0 or you can simply do an NSLog of the choresDictionary
like NSLog(#"%#",choresDictionary); I think your dictionary has 0 key key value pairs thats why you are getting null into your documents folder.
Thanks,
Madhup
I was running into this issue as well. In my case it turned out that I was using NSNumbers for keys - which is not valid.