Can not load an archived NSMutableArray into a new NSMutableArray - iphone

The App will save the NSMutableArray into a archive with no problems but as soon as I try and load the NSMutableArray back into a new NSMutableArray # viewDidLoad the app will crash. I put a break point at the end of the code where "tempArray = [unarchiver decodeObjectForKey:kDataKey46];" and tempArray is being loaded with the archived array but when I go through the "for" loop # "[poolListData addObject:testTemp];" "poolListData" does not hold the data from "tempArray". Also if I did not use a break point and just let the app try to load, the app will crash...What do you guys think?
Thank you for your time!
Jeff
NSString *filePath = [self dataFilePath];
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath])
{
NSData *data = [[NSMutableData alloc] initWithContentsOfFile:[self dataFilePath]];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
field1.text = [unarchiver decodeObjectForKey:kDataKey1];
// snip another bunch of fields
field37.text = [unarchiver decodeObjectForKey:kDataKey37];
soundOn = [unarchiver decodeBoolForKey:kDataKey38];
soundVolumeSlider.value = [unarchiver decodeDoubleForKey:kDataKey39];
soundVolumeValue = [unarchiver decodeDoubleForKey:kDataKey39];
tempArray = [unarchiver decodeObjectForKey:kDataKey46];
[unarchiver finishDecoding];
for(int i = 0; i < [tempArray count]; i++)
{
NSData *testTemp = 0;
//NSString *temp = [tempArray objectAtIndex:i];
testTemp = [tempArray objectAtIndex:i];
[poolListData addObject:testTemp];
//[poolListData addObjectsFromArray:tempArray];
}
/*
firstNameTextField.text = field1.text;
lastNameTextField.text = field2.text;
adressTextField.text = field3.text;
emailTextField.text = field4.text;
*/
[tempArray release];
[unarchiver release];
[data release];
}

I believe this is at least partially a memory management error. The decodeObjectForKey call returns an autoreleased object, so that it’s a bug to release the tempArray at the end of the function. This would be the reason your app crashes. As for poolListData not holding the unarchived array’s contents, maybe you simply forgot to initialize it and you are trying to add items into a nil array?

If you call unarchive, and are not seeing any values, then the array held something that could not be archived. I believe UIImage would be an example of something you might have in the array to try and save. The key to your problem is, what types do you have in that tempArray when you archive it.

Related

Modifying content from new dictionary also modifies the parent dictionary data

As I have a requirement to add similar objects into the array, I have created new dictionary in such a way.
NSMutableDictionary* existingStepDict = [[[arrayForSteps objectAtIndex:0] mutableCopy] autorelease];
[arrayForSteps addObject:existingStepDict];
[existingStepDict release];
Now, what happens here is that later when I change something in any one of the dictionary, the other one also gets updated. I require both these dictionaries to behave independently.
For that I went through Deep-copy of dictionaries whose code is like this.
NSMutableDictionary* existingStepDict = [[[arrayForSteps objectAtIndex:0] mutableCopy] autorelease];
NSMutableDictionary* destination = [NSMutableDictionary dictionaryWithCapacity:0];
NSDictionary *deepCopy = [[NSDictionary alloc] initWithDictionary:existingStepDict copyItems: YES];
if (deepCopy) {
[destination addEntriesFromDictionary: deepCopy];
[deepCopy release];
}
//add Properties array to Steps Dictionary
[arrayForSteps addObject:destination];
But this too didn't reflect the difference. I know I am making some minor mistake here.
But could some one help me getting my result?
Thanks a lot!
There's an easy way to get a full deepcopy of an NSDictionary o NSArray using the NSCoding (serialization) protocol.
- (id) deepCopy:(id)mutableObject
{
NSData *buffer = [NSKeyedArchiver archivedDataWithRootObject:mutableObject];
return [NSKeyedUnarchiver unarchiveObjectWithData: buffer];
}
In this way you can duplicate any object plus all the obects it contains in a single step.
when I need a mutable deep copy of a NSDictionary I create a Category with this method:
- (NSMutableDictionary *)mutableDeepCopy
{
NSMutableDictionary *returnDict = [[NSMutableDictionary alloc] initWithCapacity:[self count]];
NSArray *keys = [self allKeys];
for (id key in keys) {
id oneValue = [self valueForKey:key];
id oneCopy = nil;
if ([oneValue respondsToSelector:#selector(mutableDeepCopy)]) {
oneCopy = [oneValue mutableDeepCopy];
} else if ([oneValue respondsToSelector:#selector(mutableCopy)]) {
oneCopy = [oneValue mutableCopy];
}
if (oneCopy == nil) {
oneCopy = [oneValue copy];
}
[returnDict setValue:oneCopy forKey:key];
}
return returnDict;
}
EDIT
and searching the web I found this, I haven't tested
NSMutableDictionary *mutableCopy = (NSMutableDictionary *)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFDictionaryRef)originalDictionary, kCFPropertyListMutableContainers);

add data to NSMUtableArrray with keys by for loop

I'm new in iPhone, I want to add elements to NSMutableArray with each element's name
I created a MutableArray for keys , then other array for elements that I get them from object called Pages.
I wrote the following code
NSMutableArray *myArray;
NSMutableArray *arrayKey = [[NSMutableArray alloc] initWithObjects:#"b_pag_id", #"b_pag_bo_id", #"b_pag_num", #"b_pag_note", #"b_page_mark", #"b_page_stop", #"b_pag_user_id", nil];
for (int x=0; x<[pages count]; x++) {
Pages *myPages = (Pages *)[self.pages objectAtIndex:x];
NSString *b_pag_id2 = [NSString stringWithFormat:#"%d",myPages.b_pag_id];
NSString *b_pag_bo_id2 = [NSString stringWithFormat:#"%d",myPages.b_pag_bo_id];
NSString *b_pag_num2 = [NSString stringWithFormat:#"%d",myPages.b_pag_num];
NSString *b_pag_note2 = myPages.b_pag_note;
NSString *b_page_mark2 = [NSString stringWithFormat:#"%d",myPages.b_page_mark];
NSString *b_page_stop2 = [NSString stringWithFormat:#"%d",myPages.b_page_stop];
NSString *b_pag_user_id2 = [NSString stringWithFormat:#"%d",myPages.b_pag_user_id];
NSMutableArray *arrayValue = [[NSMutableArray alloc] initWithObjects:b_pag_id2, b_pag_bo_id2, b_pag_num2, b_pag_note2, b_page_mark2, b_page_stop2, b_pag_user_id2, nil];
NSDictionary *theReqDictionary = [NSDictionary dictionaryWithObjects:arrayValue forKeys:arrayKey];
myArray = [NSMutableArray arrayWithObjects:theReqDictionary,nil];
}
NSLog(#"array size: %d", [myArray count]);
I want to add every element to its key for example
element (b_pag_id2) its key (b_pag_id) ..etc
is this right ?? or how to do this ??
consider that NSLog(#"array size: %d", [myArray count]); gives me 1 and the size of my elements is 14
Before the loop you need to initialize the aray
NSMutableArray *myArray = [NSMutableArray array];
Inside the loop replace following:
myArray = [NSMutableArray arrayWithObjects:theReqDictionary,nil];
with
[myArray addObject:theReqDictionary];
The problem is that you are creating a new array with 1 dictionary in every loop iteration. Instead you need to initialize the array and add values one by one.
Each time through your loop you are creating a new array for myArray that has only one element. You should initialize an empty NSMutableArray before the loop and then simply add your new object to it instead of using arrayWithObjects: to create myArray..
Here i'm giving a short example, and i hope this will help you.
see this code :-
NSMutableArray *arrayValue = [[NSMutableArray alloc]initWithObjects:#"Value1",#"Value2",#"Value3", nil];
NSMutableArray *arrayKey = [[NSMutableArray alloc]initWithObjects:#"1",#"2",#"3", nil];
NSMutableDictionary *dic = [[NSMutableDictionary alloc]init];
for(int i=0;i<3;i++)
{
[dic setObject:[arrayValue objectAtIndex:i] forKey:[arrayKey objectAtIndex:i]];
}
//and you can see this by printing it using nslog-
NSLog(#"%#",[dic valueForKey:#"1"]);
Thank you!!!

Overwriting a plist file with content of a NSMutableArray (first item is always NULL in plist)

I am trying to use plist files to save a list of items from a text
file from a web site. When I first create the plist file and add
items to that, there is no problem. But when I try to remove an item
from plist, it is not removing the index, it only overwrites the
content of this index with NULL. And I tried an other way; I tried to
create a new array without the item I want to remove, and overwrite
plist file with the content of this new array. In this way, the item
I wanted to remove is removed, but surprisingly the first item gets
NULL! A more surprising situation is, I also write it to a new plist
file with same technique, and it is perferct! This is a very
primitive code, unfortunately it didn't worked for me. I searched
plenty of tutorials, but I couldn't overcome. How can I write the
content of a string array to a plist file without extra null objects
and without loosing datas?
========================================================================
I composed a sample code below :
- (IBAction)logFromPlist{
NSMutableArray *arr = [[NSMutableArray alloc] initWithContentsOfFile:[NSHomeDirectory() stringByAppendingPathComponent:#"Documents/data2.plist"]];
NSLog(#"LOG:");
NSLog(#"arrplist count : %d", [arr count]);
for(int a=0; a<[arr count]; a++){
NSLog(#"*** %#", [arr objectAtIndex:a]);
}
}
- (IBAction)logFromPlist2{
NSMutableArray *arr = [[NSMutableArray alloc] initWithContentsOfFile:[NSHomeDirectory() stringByAppendingPathComponent:#"Documents/data3.plist"]];
NSLog(#"LOG:");
NSLog(#"arrplist count : %d", [arr count]);
for(int a=0; a<[arr count]; a++){
NSLog(#"*** %#", [arr objectAtIndex:a]);
}
}
- (IBAction)addValue{
NSString *deger = [field5 text]; //New value text field in IB
NSMutableArray *arr = [[NSMutableArray alloc] initWithContentsOfFile:[NSHomeDirectory() stringByAppendingPathComponent:#"Documents/data2.plist"]];
if(arr == NULL){
arr = [[NSMutableArray alloc] init];
}
[arr addObject:deger];
[arr writeToFile:[NSHomeDirectory() stringByAppendingPathComponent:#"Documents/data2.plist"] atomically:NO];
}
- (IBAction)removeFromPlist{
NSMutableArray *arr2 = [[NSMutableArray alloc] initWithContentsOfFile:[NSHomeDirectory() stringByAppendingPathComponent:#"Documents/data2.plist"]];
if(arr2 != NULL){
NSMutableArray *arr = [[NSMutableArray alloc] init];
NSString *key = [field8 text];
for(int i = 0; i < [arr2 count]; i++){
NSString *cntStr = [[NSNumber numberWithInt:i] stringValue];
if(![cntStr isEqualToString:key]){
NSString *tempDeger = [arr2 objectAtIndex:i];
if(tempDeger != NULL){
[arr addObject:tempDeger];
}else{
NSLog(#"it is NULL");
}
}
}
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager removeItemAtPath:[NSHomeDirectory() stringByAppendingPathComponent:#"Documents/data2.plist"] error:nil]; //I tried this line by removing next line
[arr writeToFile:[NSHomeDirectory() stringByAppendingPathComponent:#"Documents/data2.plist"] atomically:NO]; //It is writing the array to plist but first item is always null
[arr writeToFile:[NSHomeDirectory() stringByAppendingPathComponent:#"Documents/data3.plist"] atomically:NO]; //same technique but everything is ok in this plist
[fileManager copyItemAtPath:[NSHomeDirectory() stringByAppendingPathComponent:#"Documents/data3.plist"] toPath:[NSHomeDirectory() stringByAppendingPathComponent:#"Documents/data2.plist"] error:nil]; // trying to copy correct plist file (data3.plist) to original plist file (plist2), but it does not fix the problem.
}
}
Project file : http://www.ozgunbursalioglu.com/files/plistWork.zip
At least your copyItemAtPath: will always fail since it won't overwrite files (data2.plist already exists).
Try to write your file by setting the automatically to YES
[arr writeToFile:PATH atomically:YES];
And also try to check the BOOL value returned to see if your oerration done successfully

NSMutable Array always adding the same pointer

The following code should add different objects to an NSMutableArray however it adds the same object each time:-
for(int i =0; i < [results count];i++)
{
Reservation *r = [[Reservation alloc] init];
NSDictionary *dict = [results objectAtIndex: i];
r.resId = [dict objectForKey:#"reservationrequest_id"];
r.driver = [dict objectForKey:#"driver_name"];
r.vehicle = [dict objectForKey:#"billing_registration"];
r.startDate = [dict objectForKey:#"hire_from_date"];
r.endDate = [dict objectForKey:#"hire_to_date"];
r.status = [dict objectForKey:#"status_type"];
[self.bookingsObjectArray addObject:r];
[r release];
r = nil;
}
I have exactly the same code that works fine in another part of my app it just uses a Groups class instead of Reservation.
When debugging the code I found that when it does [r release]; 'r' is greyed out but still keeps the same pointer. When it goes back to Reservation *r = [[Reservation alloc] init];
'r' has the same pointer as last time.
Any ideas what might be causing the problem? Thanks in advance.
Chris

Archiving mutable array - doesNotRecognizeSelector exception

I'm having an "doesNotRecognizeSelector" exception and I suspect that maybe my unarchiver return immutable array intstead of mutable.
Am I right ? how should I do the archiving and archiving properly ? (place of exception is show down)
Thanks!!!
NSMutableArray* arr;
- (void) write
{
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
NSMutableArray *copy = [[NSMutableArray arrayWithArray:self.Arr] copy];
[archiver encodeObject:copy forKey:#"Key"];
[archiver finishEncoding];
[data writeToFile:[Util DataPath] atomically:YES];
[archiver release];
[data release];
[copyOfPaidPacks release];
}
-(NSMutableArray*) read
{
NSString* DataPath = [Util FilePath];
NSData *data = [[NSMutableData alloc] initWithContentsOfFile:DataPath];
NSKeyedUnarchiver *unarchiver = nil;
if (data != nil)
{
unarchiver = [[NSKeyedUnarchiver alloc]initForReadingWithData:data];
if([self.Arr count] <= 0)
{
self.Arr = [unarchiver decodeObjectForKey:#"Key"];
}
}
[unarchiver finishDecoding];
[unarchiver release];
[data release];
return self.arr
}
-(void)B
{
[self write];
NSMutableArray* SecondArr = [self read];
[SecondArr sortUsingSelector:#selector(CompareDate:)]; - > `****THIS IS WHERE I GET THE EXCEPTION`
}
Edit
Adding the compare method:
- (NSComparisonResult)CompareDate:(T*)p
{
NSComparisonResult result = [self.changeDate compare:p.changeDate];
if(result == NSOrderedDescending)
result = NSOrderedAscending;
else if(result == NSOrderedAscending)
result = NSOrderedDescending;
if(result == NSOrderedSame)
{
result = [self CompareName:p];
}
return result;
}
NSKeyedUnarchiver does indeed return an immutable NSArray. If you really want to get an NSMutableArray, you'd need to call -mutableCopy on the return value from decodeObjectForKey:.
This code snippet makes me wonder if you really even need a mutable array though. It looks like you're just sorting the array you get from -read. Why not just call sortedArrayUsingSelector: on the immutable NSArray instead?
You already pass an immutable array to the archiver, so why would you expect the unarchiver to return a mutable one then?
If you want a copy to be mutable, then you have to use
NSMutableArray *copy = [[NSMutableArray arrayWithArray:self.Arr] mutableCopy];
as
NSMutableArray *copy = [[NSMutableArray arrayWithArray:self.Arr] copy];
will create an immutable copy (at least as far, as you should be concerned. In reality the framework will often use a mutable internal representation for immutable instances, but this is an implementation detail and you should not count on it, as these representations have changed in the past and the Cocoa docs explicitly tell you, to not check for the internal representation).
EDIT:
just tested with NSKeyedArchiver myself on iOS 5.0 simulator:
// this returns a mutable instance at runtime!
NSMutableArray* test0 = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:[NSMutableArray array]]];
// this returns an immutable instance at runtime!
NSArray* test1 = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:[NSArray array]]];
So your problem is really exclusively caused by using copy instead of mutableCopy, while archiving and unarchiving keeps the correct mutability-attributes of the instances!
I had an issue where archiving a 3-dimensional mutable array (ie, a mutable array of mutable arrays of mutable arrays) would unarchive as a mutable array of immutable arrays of immutable arrays.
The key to fixing this was to subclass NSKeyedUnarchiver and overwrite
- (Class)classForClassName:(NSString *)codedName
{
Class theClass = [super classForClassName: codeName];
if ([codeName isEqualToString: NSStringFromClass(NSArray.class))
{
theClass = NSMutableArray.class;
}
//... more here like NSDictionary
return theClass;
}