Empty NSMutableArray read from plist becomes immutable object - ios

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'"

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.

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.

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];

Related

Add Dictionary to NSMutableArray of Dictionaries

I have an NSMutableArray called myMutbaleArray that looks like this when I log it
2015-12-08 17:04:21.679 APP[3342:573379] (
{
id = 19;
"num_test" = 100000;
url = "http://fsad.com";
},
{
id = 20;
"num_test" = 100001;
url = "http://teeeet.com";
}
)
And I want to add an object that looks like this
{
id = 21;
"num" = 100002;
url = "http://example.com";
}
So I am trying this
[myMutbaleArray addObject:#{ #"id":#"23",#"num_test":#"100000", #"url":mainDict[#"website_url"],#"website_name":#"website"}];
But when I do this I get
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFArray insertObject:atIndex:]: mutating method sent to immutable object'
I initialize the array like this
#interface {
NSMutableArray *socailArray;
}
//inside connectionDidFinishLoading
socailArray = [dataDict valueForKey:#"socail"];
Why can I add another dictionary to the MutableArray?
Thanks
If you see this, your array is actually not a mutable array. Here is the hint:
-[__NSCFArray insertObject:atIndex:]
^^^^^^^^^^^
The object is of type __NSCFArray, which is an internal counterpart of NSArray.
Even if you declare your variable as NSMutableArray the pointer can point to an object of any type (event for example NSRegularExpression). Important is, how it is created.
This happens to most people if they serialise an array either using NSUserDefaults, NSJSONSerialization or what ever.
The key is to create a mutable copy when the array gets deserialised using
-[NSArray mutableCopy]
Note that this is not deep-copy. This means an array contained in the root array will not be mutable copied and needs to be replaced separately.
Deep copying can be achieved using this:
// array
NSArray *originalArray = #[#"a", #"b", #[#1, #2]];
NSMutableArray *mutableArray = (NSMutableArray *)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFArrayRef)originalArray, kCFPropertyListMutableContainers);
// dictionary
NSDictionary *originalDictionary = #{#"a": #"b", #"c": #[#{#"abc": #123}]};
NSMutableDictionary *mutableDictionary = (NSMutableDictionary *)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFDictionaryRef)originalDictionary, kCFPropertyListMutableContainers);
You should change init to:
//inside connectionDidFinishLoading
socailArray = [NSMutableArray arrayWithArray:[dataDict valueForKey:#"socail"]];
Because: [dataDict valueForKey:#"socail"] is a NSArray.
With socailArray = [dataDict valueForKey:#"socail"];, the type of [dataDict valueForKey:#"socail"] is NSArray, so it auto cast socailArray into NSArray, that's why you can not insert thing into this.
To avoid this, you must be hold socailArray as NSMutableArray using:
socailArray = [[dataDict valueForKey:#"socail"] mutableCopy];
Hope this could help.

mutating method sent to immutable object' in nsmutablearray

I want to remove objects from NSmutableArray can one tell me the Best way to remove from NSMutableArray
.h
#property(nonatomic,retain)NSMutableArray *arr_property;
.m
_arr_property=[[NSMutableArray alloc]init];
MTPop *lplv = [[MTPop alloc] initWithTitle:SelectProperty(APP_SHARE.language)
options:[_arr_property valueForKeyPath:#"property_list.property_type_name"]
handler:^(NSInteger anIndex) {
txt_Property.text=[[_arr_property valueForKeyPath:#"property_list.property_type_name"] objectAtIndex:anIndex];
NSLog(#"index number %ld",(long)anIndex);
remove object--->>>
NSLog(#"index number %#",[_arr_property valueForKey:#"property_list"]);
[[_arr_property valueForKeyPath:#"property_list.property_type_name"] removeObjectAtIndex:anIndex]; ////hear the app is crashing
app is crashing error iam getting is
2015-06-09 13:21:31.104 Estater[2170:62264] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFArray removeObjectAtIndex:]: mutating method sent to immutable object'
Think about your code:
_arr_property=[[NSMutableArray alloc]init];
You now have an empty NSMutableArray. It has no elements.
[... removeObjectAtIndex:0];
What did we just say? The array has no elements. It has no element 0 - to have an element 0 it would need to have one element at least, but it doesn't. There is nothing to remove.
[_arr_property valueForKeyPath:#"property_list.property_type_name"]
That part is the weirdest, but let's carry on. When called on an array, valueForKeyPath: results in an NSArray - not an NSMutableArray. So this gives you an empty NSArray. But you cannot say removeObjectAtIndex: to an NSArray, even if it empty - it is not mutable. That's the crash you are experiencing.
The real error is that you are calling removeObject on an element of your NSMutableArray:
[-->[_arr_property valueForKeyPath:#"property_list.property_type_name"]<-- removeObjectAtIndex:0];
The array looks empty, but if filled with something, to remove the first element you should do instead:
[_arr_property removeObjectAtIndex:0];
Firstly, you cannot work with NSMutableArray for key value coding it does not support. You must better use NSMutableDictionary for it.
As dictionaries store objects based on a key, whereas arrays store objects based on an index.
You can use NSMutableDictionary like this:
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setObject:something forKey:#"Some Key"];
// ... and later ...
id something = [dict objectForKey:#"Some Key"];
Secondly, valueForKeyPath: returns a value not array and valueForKey: returns array of value for the key and also that array is not mutable.
Edit:
Thirdly, after researching more on valueForKeyPath:, found its use in collection operation and syntax for using is. So, do it by changing
[_arr_property valueForKeyPath:#"property_list.property_type_name"]
To
[_arr_property valueForKeyPath:#"#property_list.property_type_name"]

Trying to take objects out of an array, reverse them, and put them back in

as the title says I'm having an issue with taking objects out of an array, flipping them, and putting them back in. Below is the code I currently have that ends in this error
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayI removeObjectsAtIndexes:]: unrecognized selector sent to instance
I was wondering if anyone knew how to fix this? Here's a little more info on how I have it set up:
The object "PEG" is an NSString that displays "-0.6", "4.36"
GlobalSortedArray is an array filled with dictionary containing the PEG object
//Declare variables
NSMutableArray *negArray = [[NSMutableArray alloc]init];
NSMutableIndexSet *index = [[NSMutableIndexSet alloc]init];
int negcount = 0;
NSDictionary *forLoopDict;
for (forLoopDict in globalSortedArray)
{
if ([[forLoopDict objectForKey:#"PEG"] hasPrefix:#"-"])
{
[index addIndex:negcount];
}
negcount++;
}
NSLog(#"%#", negArray);
// Removes objects from main array. This is what seems to be messing up.
[globalSortedArray removeObjectsAtIndexes:index];
// Reverses the array
NSArray* reversedArray = [[negArray reverseObjectEnumerator] allObjects];
// insters them back into the main array
[globalSortedArray insertObjects:negArray atIndexes:0];
You are trying to remove an item from globalSortedArray. It is an NSArray and not mutable.
globalSortedArray as NSmutableArray
NSArray -- > NSMutableArray

Updating the Value for a Key in an NSMutableOrderedSet object

I have the following statements
[[myListSet objectAtIndex:sender.tag] setValue:#"1" forKey:#"STATUS"];
where myListSet is defined as
NSMutableOrderedSet *myListSet;
myListSet is a list of dictionary entries, each with 6 key-value pairs, with one of the Keys being STATUS.
I thought I could update the values in one of the dictionaries using the above line. It worked in simulator, but not on my iPhone.
The error I am getting is
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFDictionary setObject:forKey:]: mutating method sent to immutable object'
I guess my thought on updating the entry is incorrect. Any ideas on how to update a value for one of the keys for one of the dictionary entries in this set?
NSMutableOrderedSet allows you to modify its direct children. The error you are seeing arises because your NSMutableOrderedSet contains NSDictionary objects and not NSMutableDictionary objects.
In order to modify the sub-dictionaries, you will need them to be NSMutableDictionary objects. You could do this by:
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithDictionary:[myListSet objectAtIndex:sender.tag]];
dict[#"STATUS"] = #"1";
[myListSet replaceObjectAtIndex:sender.tag withObject:dict];
You should be aware that anything that was holding a reference to the original immutable dictionary will still be holding that dictionary and not your modified dictionary.
setValue:forKey makes use of key-value coding which lets you set an object's properties. NSMutableOrderedSet doesn't come with a "STATUS" property out of the box.
You want to use setObject:forKey instead
I believe your data structure is a set of NSDictionary. Your set is mutable but the dictionary within it is immutable.
If you have access to that dictionary then change NSDictionary to NSMutableDictionary and this will work. If not then
NSDictionary *myDictionary = [myListSet objectAtIndex:sender.tag];
NSDictionary *myMutableDictionary = [myDictionary mutableCopy];
[myMutableDictionary setValue:#"1" forKey:#"STATUS"];
[myListSet setObject:myMutableDictionary atIndex:sender.tag]

How does this NSMutableArray alloc&init work?

I see a new kind of alloc&init NSMutableArray way in one project. It's like this A
NSMutableArray *array = [#[] mutableCopy]; and this works well, and i want to try whether its possible to use BNSMutableArray *array = [NSMutableArray mutableCopy]; it build succeeded, but got this error when used: +[NSMutableArray addObject:]: unrecognized selector sent to class 0x38bedc2c
Now i want to know how does A work? and why B is wrong? A is better than normal alloc&init?
Any help will be appreciated.
mutableCopy is an instance method declared in NSObject class. It is called on any instance to create a mutable copy of it.
In first case #[] will create an autoreleased NSArray instance on which calling mutableCopy will create NSMutableArray instance.
In second case calling mutableCopy on the class is incorrect because it is not meant to be called that way. This will get compiled but will cause exception at runtime.
Hope that helps!
In the first case, you're first initializing an empty NSArray instance; think of #[] as equivalent to [[NSArray alloc] init]. Therefore you're sending mutableCopy to a correct instance, so it works fine.
In the second case, you're sending the message to a class (as opposed to an instance of it), which doesn't make much sense, because the addObject message can only be sent to an instance, not the class itself.
#[] means an NSArray with no object. It returns an NSArray, and then its mutableCopy is copied to array.
+[NSMutableArray addObject:] is invalid as addObject is an instance method and you are trying to use it as class method.
Even NSMutableArray *array = [NSMutableArray mutableCopy]; is incorrect!!! As nothing is created, it is not been allocated and inited. If you log the array, it will only print the string NSMutableArray. Also you can't use array to addObject and other operations.
You should use NSMutableArray *array = [NSMutableArray array];
The first one is lazy typing.
You should avoid it.
It creates an empty NSArray from the array literal syntax and the creates a mutable copy.
That's saving a little typing by creating an unnecessary array.
You should just use
[NSMutableArray new]
Or
[[NSMutableArray alloc] init]
Or if possible because you know the initial capacity in advance
[[NSMutableArray alloc] initWithCapacity:someNSUIntegerValue]
Anything else above is laziness.
Only use mutableCopy when you are actually copying some content.

Resources