I have the following code:
for (NSMutableDictionary *aDict in array)
{
// do stuff
[aDict setObject:myTitle forKey:#"title"];
}
My question is, if the array is filled with NSDictionary objects, will this for loop code as written automatically convert them into NSMutableDictionary objects?
Or do I need to do something more specific here to ensure that I don't get an unrecognized selector sent to instance error on setObject:forKey: in the loop?
Currently that will give you the error you mentioned. Whilst the loop is setup with mutable dictionaries, the underlying object is still immutable. You'd need to create a new dictionary out of it. Try this
NSMutableArray *newArray = [NSMutableArray array];
for (NSDictionary *aDict in array)
{
NSMutableDictionary *mutable = [aDict mutableCopy];
// do stuff
[mutable setObject:myTitle forKey:#"title"];
[newArray addObject:mutable];
}
No it will not automatically convert them. You have to do that yourself. You'll definitely get the unrecognized selector sent to instance exception.
Related
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.
I am struggling with a dictionary in which I want to create a new dictionary from a series of keys (P, SP, and RP).
I have attempted to create a new NSMutableDictionary that combines individual Dictionaries that have have all the values for the P, SP, and RP keys respectively, but have gotten the error "No class method for selector "addEntriesFromDictionary" "
Here is my code:
NSMutableDictionary *newDict = [NSMutableDictionary addEntriesFromDictionary:self.allSPPositions];
and
#interface className ()
- (void)addEntriesFromDictionary:(NSDictionary *)otherDictionary;
#end
#implementation className
- (void)viewDidLoad {
Any help or insight would be appreciated! Thanks!
The compiler is telling you exactly what's wrong. You're trying to add items from a dictionary to the NSMutableDictionary class. You need to send the ad items from dictionary message to an instance of NSMutableDictionary.
This line:
NSMutableDictionary *newDict =
[NSMutableDictionary addEntriesFromDictionary:self.allSPPositions];
Should read
NSMutableDictionary *newDict =
[someDictionary addEntriesFromDictionary: self.allSPPositions];
(Where someDictionary is the dictionary to which you want to add items.)
or even
NSUInteger count = [self.allSPPositions count];
NSMutableDictionary *newDict =
[NSMutableDictionary dictonaryWithCapacity: count];
[newDict addEntriesFromDictionary: self.allSPPositions];
(Since your code appears to be trying to create a new, mutable dictionary and add the contents of self.allSPPositions.)
If your goal is to get a mutable copy of self.allSPPositions, there is a cleaner way to do that:
NSMutableDictionary *mutablePositions = [self.allSPPositions mutableCopy];
Uhh, here's an example that will compile and use the method in question, you should probably NOT call that method in your Interface since it's part of Apple's framework and is already predefined, unless you have a special reason for doing so:
NSMutableDictionary *dict =[[NSMutableDictionary alloc] init];
[dict setValue:#"Seattle" forKey:#"name"];
[dict setObject:[NSNumber numberWithInt:3] forKey:#"age"];
[dict setObject:[NSDate date] forKey:#"date"];
dict[#"city"]=#"Seattle";
[dict addEntriesFromDictionary:[NSMutableDictionary dictionaryWithObjectsAndKeys: #"Washington",#"location", nil]];
This is merely meant to get you started, but it shows you how to do this so that it works for you in your circumstances
I have an array inside a NSMutableDictionary and i want to add objects to it. With my current approach I get an error saying that the array is immutable.
I think the problem lies when I´m saving the dictionary to NSUserDefaults. I´m retrieving the is it a NSDictionary but I am at the same time creating a new NSMutableDictionary with the contents.
However, the array seems to be immutable. How do I replace an array inside of a dictionary?
My dictionary looks like this:
NSMutableArray *array = [[NSMutableArray alloc] initWithObjects:[NSNumber numberWithInt:0], nil];
NSDictionary *dict = #{
#"firstKey": #{
#"theArray":array,
}
};
NSMutableDictionary *mutDict = [[NSMutableDictionary alloc] initWithDictionary:dict];
I am trying to add objects like this:
[[[mutDict objectForKey:#"firstKey"] objectForKey:#"theArray"] addObject:[NSNumber numberWithInt:5]];
I am able to add objects to the array inside mutDict before its saved to NSUserDefaults
The error message I get when I try to add to the array inside the dictionary after loading it from NSUserDefaults:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFArray insertObject:atIndex:]: mutating method sent to immutable object'
Here's what the documentation for dictionaryForKey: says on NSUserDefaults:
Special Considerations
The returned dictionary and its contents are immutable, even if the values you >originally set were mutable.
So when you retrieve your dictionary from NSUserDefaults the dictionary itself and all of the collections inside it are immutable. You can make the top level dictionary mutable (which I assume you are doing), but that won't propagate down into the now immutable NSArrays which are values in the dictionary.
The only way to get around this is to go through the dictionary that's returned and replace the immutable NSArrays with their mutable counterparts. It might look something like this.
- (NSMutableDictionary *)deepMutableCopyOfDictionary:(NSDictionary *)dictionary
{
NSMutableDictionary *mutableDictionary = [dictionary mutableCopy];
for (id key in [mutableDictionary allKeys]) {
id value = mutableDictionary[key];
if ([value isKindOfClass:[NSDictionary class]]) {
// If the value is a dictionary make it mutable and call recursively
mutableDictionary[key] = [self deepMutableCopyOfDictionary:dictionary[key]];
}
else if ([value isKindOfClass:[NSArray class]]) {
// If the value is an array, make it mutable
mutableDictionary[key] = [(NSArray *)value mutableCopy];
}
}
return mutableDictionary;
}
To be honest though it sounds like you're using NSUserDefaults for something a lot more complex then it is intended for. If you want to persist complex data structures then you should look into something like Core Data, or if that looks to be a bit overkill take a look at NSKeyedArchiver.
You can add object directly to the array:
NSMutableArray *array = [[NSMutableArray alloc] initWithObjects:[NSNumber numberWithInt:0], nil];
NSDictionary *dict = #{
#"firstKey": #{
#"theArray":array,
}
};
NSMutableDictionary *mutDict = [[NSMutableDictionary alloc] initWithDictionary:dict];
//Since Objective-C objects are always passed by reference (using pointers) you can add object to the array
[array addObject:[NSNumber numberWithInt:55]];
Swift example of adding object to array which is part of a dictionary.
let arr = [0] // note that initial array may be immutable
var dict = ["fK": ["a":arr]] // even if "arr" will be mutable, but "dict" immutable
dict["fK"]!["a"]!.append(3) // this will not work. "dict" must be mutable
println(dict) //[fK: [a: [0, 3]]]
Another approach
var arr = [0] // initial array must be mutable
var dict = ["fK": ["a":arr]] // in both cases dictionary must be mutable
arr.append(3)
let newArr = arr
dict["fK"]!["a"]! = newArr // because we change it's content
println(dict) //[fK: [a: [0, 3]]]
I have an array structure as follows:
NSMutableArray topArray{
NSMutableArray middleArray{
NSMutableArray lowerArray{
NSMutableDictionary dict{
}
}
}
}
The array structure is filled with some data that I retrieve from the web is JSON format.
I am trying to edit one of the objects in the NSMutableDictionary as follow:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSMutableArray *lowerArray = [[self middleArray] objectAtIndex:[indexPath section]];
NSMutableDictionary *dict = [lowerArray objectAtIndex:[indexPath row]];
[dict setObject:[NSNumber numberWithInt:1] forKey:#"key"];
[[self tableView] reloadData];
}
The data within each of the arrays is correct (I have checked with print statements), however, when I try to change the object in the dict I get the following error:
reason:
'-[__NSCFDictionary setObject:forKey:]: mutating method sent to
immutable object'
I need the object in the dictionary to be changed within the array structure.
Could this be an issue with the JSON data since when topArray is first initialised with the JSON data the middle and lower arrays are in the form of just NSArray's?
Sorry if this is confusing, I will try to clarify more if you have any questions.
Thanks in advance for your help.
If you're using NSJSONSerialization, you can pass NSJSONReadingMutableContainers to the options parameter of +JSONObjectWithData:options:error:, and all of the parsed dictionaries and arrays will be mutable.
NSMutableArray *topArray = [NSJSONSerialization JSONObjectWithData:webServiceData
options:NSJSONReadingMutableContainers
error:nil];
Just use NSMutableDictionary class instead of just NSDictionary for the moduleDict variable. It is easily done when parsing JSON objects. If no - create one like this:
NSMutableDictionary *newDict = [NSMutableDictionary dictionaryWithDictionary: moduleDict];
I doubt that you are really dealing with NSMutableDictionary on the lowest level. Do you have the output from these print statements for us?
Usually the structures generated by JSON deserialisation are of immutable type. And that is exactly what your error message is telling.
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];