bandw
bandw

Reputation: 889

Empty NSMutableArray read from plist becomes immutable object

Write an empty NSMutableArray to disk, then read it back, it becomes an immutable object.
But, if the NSMutableArray is not empty, it won't. How to explain that?
here are the codes:

NSMutableArray *testItems1 = [NSMutableArray array];
NSMutableDictionary *testList1 = [NSMutableDictionary dictionaryWithObjectsAndKeys:testItems1, @"list_items", @"list1", @"list_name", nil];

NSMutableArray *testItems2 = [NSMutableArray arrayWithObjects:@"item11", @"item22", nil];
NSMutableDictionary *testList2 = [NSMutableDictionary dictionaryWithObjectsAndKeys:testItems2, @"list_items", @"list2", @"list_name", nil];

NSMutableArray *testLists = [NSMutableArray arrayWithObjects:testList1, testList2, nil];

[testLists writeToFile:@"/tmp/testLists" atomically:YES];

NSMutableArray *testReadLists = [NSMutableArray array];
[testReadLists setArray:[NSArray arrayWithContentsOfFile:@"/tmp/testLists"]];
NSMutableDictionary *testReadList = [testReadLists objectAtIndex:0];
NSMutableArray *testReadItems = [testReadList objectForKey:@"list_items"];
[testReadItems addObject:@"item3"]; // Crashes here: "*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFArray insertObject:atIndex:]: mutating method sent to immutable object'"

Upvotes: 0

Views: 231

Answers (3)

Caleb
Caleb

Reputation: 125017

Objects read straight from property lists are always immutable. You might create a mutable object from them, but the objects themselves are immutable. These lines are the problem:

NSMutableDictionary *testReadList = [testReadLists objectAtIndex:0];
NSMutableArray *testReadItems = [testReadList objectForKey:@"list_items"];

testReadList is the first object in the mutable array testReadLists, but that object itself is still immutable despite the fact that the declared type of testReadList is NSMutable*. Likewise, the object you get back from the objectForKey: call is an instance of NSArray even though you're assigning it to testReadItems, which is declared as NSMutableArray*. You can avoid the problem by simply making a mutable copy before you add new items:

NSMutableArray *testReadItems = [[testReadList objectForKey:@"list_items"] mutableCopy];

Upvotes: 0

Vivek
Vivek

Reputation: 564

Haven't tested this myself, but you probably want to first read the plist into an NSData, and then get the actual array by doing +[NSPropertyListSerialization propertyListWithData:options:format:error:], specifying NSPropertyListMutableContainers in the options argument (apple doc here)

Note this should give you a full hierarchy of mutable containers (an NSMutableArray containing NSMutableDictionaries, and so on). If all you want is an NSMutableArray at one particular level in the hierarchy, then the other posted solution/comments would probably be a more appropriate solution.

Upvotes: 1

rmaddy
rmaddy

Reputation: 318924

These two lines of code:

NSMutableArray *testReadLists = [NSMutableArray array];
[testReadLists setArray:[NSArray arrayWithContentsOfFile:@"/tmp/testLists"]];

give you a mutable array of immutable objects. You can add and remove objects from testReadLists but everything you get from this array (originally loaded from the plist) will be immutable.

Update - I was about to post info about the solution but the answer by Vivek describes what I was going to say.

Upvotes: 3

Related Questions