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.
Related
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. :-)
Good evening everyone, I was hoping you could help with an Objective-C question I have.
In my app, I have a mutable array that contains 16 objects; the objects being images.
I would like to save and load the array so that the images are retained when the user quits the program.
I am new to data persistence and I can see there are several ways of saving and loading data and I am familiar with the NSUserDefaults method of saving and loading data. I am aware though that you cannot save arrays with images in this way.
Could someone please explain, perhaps with an example of the best and simplest way of saving and loading an array with images? Any help would be great as I'm unsure the best way to go with this.
Thanks everyone in advance!
Consider using NSKeyedArchiver.
// Archive
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:theArray];
NSString *path = #"/Users/Anne/Desktop/archive.dat";
[data writeToFile:path options:NSDataWritingAtomic error:nil];
// Unarchive
NSString *path = #"/Users/Anne/Desktop/archive.dat";
NSMutableArray *theArray = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
This way you can be sure the unarchived array is identical to the original.
All classes conforming to the NSCoding protocol can be used by NSKeyedArchiver.
Note: You can use any extension.
Response to comment:
The following should work on iOS:
// The Array
NSMutableArray * array = [[NSMutableArray alloc] init];
// Determine Path
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [ [paths objectAtIndex:0] stringByAppendingPathComponent:#"archive.dat"];
// Archive Array
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:array];
[data writeToFile:path options:NSDataWritingAtomic error:nil];
// Unarchive Array
NSMutableArray *theArray = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
- (void)viewDidLoad
{
[super viewDidLoad];
if ([[NSFileManager defaultManager] fileExistsAtPath:pathString])
{
infoDict = [[NSMutableDictionary alloc] initWithContentsOfFile:pathString];
}
else
{
infoDict = [[NSMutableDictionary alloc]initWithObjects:[NSArray arrayWithObjects:#"BeginFrame",#"EndFrame", nil] forKeys:[NSArray arrayWithObjects:[NSNumber numberWithBool:YES],[NSNumber numberWithBool:YES], nil]];
if ([infoDict writeToFile:pathString atomically:YES])
{
NSLog(#"Created");
}
else
{
NSLog(#"Is not created");
NSLog(#"Path %#",pathString);
}
}
This is my code. I check if file is created, if not - I create a NSMutableDictionary and I write it to file at path, but writeToFile method returns NO. Where is problem? If I create this file with NSFileManager it works, but doesn't when I want to write a dictionary.
writeToFile:atomically only works if the dictionary you call it on is a valid property list object (see docs).
For a NSDictionary to be a valid property list object, among other things, its keys must be strings, but in your example the keys are NSNumber instances.
You can not control the content you are going to write sometimes. For example, you can't avoid a null value when you are going to write a JSON object that is gotten from a server.
NSData is compatible with these "invalid" values, so converting NSArray or NSDictionary to NSData is an ideal way in these cases.
write:
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:jsonObject];
[data writeToFile:path atomically:YES];
read:
NSData *data = [NSData dataWithContentsOfFile:path];
NSDictionary *jsonObject = [NSKeyedUnarchiver unarchiveObjectWithData:data];
I have a plist containing an array with three elements all of which are dictionaries. These dictionaries contain four items each (latitude, longitude, title, subtitle). I want to loop through each of the elements and get the latitude and longitude (both of which are attributes of the dictionary).
The following code is used.
- (void)loadAnnotations{
//retrieve path of plist file and populate relevant types with its information
NSString *plistPath = [[NSBundle mainBundle] pathForResource:#"Places1" ofType:#"plist"];
branchList = [[NSArray alloc] initWithContentsOfFile:plistPath];
NSLog(#"hi inside method",branchList.lastObject);
self.branchList =[NSMutableArray array];
//code ok until this
for (NSDictionary *key in branchList)
{ NSLog(#"hi in loop");
PlaceFinderAnnotation *placeAnnotations = [[PlaceFinderAnnotation alloc] init];
//loop through annotations array, creating parking annotations filled with the information found in the plist
CLLocationDegrees latitude = [[key valueForKey:#"latitude"]floatValue];
CLLocationDegrees longitude = [[key valueForKey:#"longitude"]floatValue];
placeAnnotations.coordinate = CLLocationCoordinate2DMake(latitude, longitude);
[self.branchList addObject:placeAnnotations];
[placeAnnotations release]; placeAnnotations= nil;
objectForKey:#"subtitle"]];
}
}
The problem is it doesnt go into the loop. meaning it doesnt print out the log command "hi inside the looppp".
Let's assume this code succeeded (we have to assume because you don't appear to be checking to make sure). Let's also assume (because you don't say) "branchList" is an instance variable in the current class:
NSString *plistPath = [[NSBundle mainBundle] pathForResource:#"Places1" ofType:#"plist"];
branchList = [[NSArray alloc] initWithContentsOfFile:plistPath];
This hopefully leaves you with an array. You could of course eliminate the "hopefully" by ... checking to make sure it leaves you with an array ( if (branchList)... ). Then, since "branchList" seems to be an instance variable, you immediately blow it away by replacing it with an empty array (using an accessor rather than setting it directly as you did above):
self.branchList =[NSMutableArray array];
...so then you try to iterate an empty loop (so the NSLog() statement is never executed).
self.branchList =[NSMutableArray array];
creates an empty array and that is what the for statement is asked to loop through.
Delete that statement.
Perhaps this is what you want:
NSString *plistPath = [[NSBundle mainBundle] pathForResource:#"Places1" ofType:#"plist"];
self.branchList = [NSArray arrayWithContentsOfFile:plistPath];
NSLog(#"hi inside method",branchList.lastObject);
for (NSDictionary *key in self.branchList) {
NSLog(#"hi inside loopppp");
if([branchlist count]){
for (NSDictionary *key in self.branchList) {
NSLog(#"Key item in branchlist %#",key);
}
}else{
NSLog(#"There is no items in branchlist");
}
I'm having an issue with the memory management in my application. I have an NSDictionary instance variable that I'm setting equal to another NSDictionary that gets made in a method. This all works fine and my application behaves like I want it to, but I'm having trouble applying the proper memory management.
If I release the local dictionary it eventually causes a crash as the method is called repeatedly, because the data saved in the instance variable is also trashed. Here's the code:
NSBundle *bundle = [NSBundle mainBundle];
NSString *plistPath = [bundle pathForResource:#"Names" ofType:#"plist"];
NSDictionary *dictionary = [[NSDictionary alloc] initWithContentsOfFile:plistPath];
self.dictAllValues = dictionary;
[dictionary release];
Create dictAllValues using
#property(retain) NSDictionary *dictAllValues;
Your method
-(void) myMethod
{
NSBundle *bundle = [NSBundle mainBundle];
NSString *plistPath = [bundle pathForResource:#"Names" ofType:#"plist"];
NSDictionary *dictionary = [[NSDictionary alloc] initWithContentsOfFile:plistPath];
self.dictAllValues = dictionary;
[dictionary release];
}
and release in dealloc method
-(void) dealloc
{
[dictAllValues release];
[super dealloc];
}
How do you declare dictAllValues? Typically, it would be:
#property(retain) NSDictionary *dictAllValues;
If so, then the release in your code is correct and your problem lies elsewhere. Post the backtrace of the crash, use Build and Analyze and fix any issues, and try turning on Zombie detection.
From the apple memory management guide.
As a corollary of the fundamental rule, if you need to store a received object as a property in an instance variable, you must retain or copy it.
So, in this case putting [dictionary release]; in dealloc method instead (or any other method you might use for clean up) should work fine.
I assume your dictAllValues property uses simple assignment, let me know if that's not the case.