I am tring to get value from an NSDictionary but getting an Exception that the keys is not defined.
NSDictionary *userNames=[[NSDictionary alloc] init];
NSString * testValue = #"";
testValue = [userNames valueForKey:#"#&"];//crashing here when key is #&
Terminating app due to uncaught exception 'NSUnknownKeyException',
reason: '[<__NSDictionaryM 0x2831eeec0> valueForUndefinedKey:]
Never use valueForKey to get a single value from a dictionary unless you know what KVC is and you really need KVC.
The dictionary is empty, the key does not exist and the leading # has a special meaning in KVC so valueForKey crashes in this case.
The proper API is objectForKey but just use modern key subscripting to get at least nil if the key doesn't exist
testValue = userNames[#"#&"];
Apple doc
If key does not start with “#”, invokes object(forKey:). If key does start with “#”, strips the “#” and invokes [super valueForKey:] with the rest of the key.
you may do
testValue = [userNames objectForKey:#"#&"]
or
testValue = [userNames valueForKey:#"&"]
Probably you are not declaring keys and values. NSDictionary requires you to init with Values and keys. (Whether fetch them from a file or declare them while initializing.
NSDictionary * userNames = [[NSDictionary alloc] initWithObjectsAndKeys:
#"value1", #"key1",
#"value2", #"key2",
nil];
NSString * testValue = [userNames valueForKey:#"key2"];
NSLog(#"%#", testValue);
Also notice that the value is written before and then key is defined. Refer to this document.
If you want to declare the dictionary first (like you did in your question) and then add the values and keys to the dictionary, You have to use NSMutableDictionary.
NSMutableDictionary *userNames1 = [[NSMutableDictionary alloc] init];
[userNames1 setValue:#"firstValue" forKey:#"key1"];
[userNames1 setValue:#"secondValue" forKey:#"key2"];
NSLog(#"%#", [userNames1 valueForKey:#"key2"]);
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 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 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]
Why is a NSString #"#" not Key Value Compliant?
Are there other strings that aren't compliant as well?
You can try that it is failing with this code for example:
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setObject:#"Some Object" forKey:#"#"];
NSString *theObject = [dict valueForKey:#"#"];
Setting it as a key is ok but not querying for that key..
Sure you can work around this error by appending some other string you later on remove another time like doing the following when you want to have the key #:
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setObject:#"Some Object" forKey:#"keyConst#"];
NSString *theObject = [dict valueForKey:#"keyConst#"];
The counterpart to setObject:forKey: is
objectForKey: (and not valueForKey:) to retrieve an item from a dictionary:
NSString *theObject = [dict objectForKey:#"#"];
Alternatively, use the "new" dictionary subscripting syntax:
dict[#"#"] = #"Some Object";
NSString *theObject = dict[#"#"];
valueForKey: uses Key-Value coding methods if the key starts with #.
From the documentation of -[NSDictionary valueForKey:]:
If key does not start with “#”, invokes objectForKey:. If key does
start with “#”, strips the “#” and invokes [super valueForKey:] with
the rest of the key.
For example,
NSString *x = [dict valueForKey:#"#description"];
does the same as
NSString *x = [dict description];
So in almost all cases, you should use objectForKey:, unless you explicitly
want to do some Key-Value coding magic.
In iOS 6 I can do this:
NSDictionary *d = #{anObject : "key"};
But apparently when the object is a UIImageView or UIWebView (or maybe others too), it crashes with:
'NSInvalidArgumentException', reason: '-[UIWebView copyWithZone:]: unrecognized selector sent
Changing the dictionary declaration to the old way works:
NSDictionary *d = [NSDictionary dictionaryWithObjectsAndKeys:anObject,#"key", nil];
Any idea why this happens?
The syntax is
NSDictionary *d = #{<key> : <value>, ...};
In your case:
NSDictionary *d = #{#"key" : anObject};
So your code tried to use anObject as the key. That failed for UIWebView because keys must
conform to the NSCopying protocol .
This is because the new syntax expects keys and values to be ordered in reverse, compared to dictionaryWithObjectsAndKeys:
NSDictionary *d = #{ #"key" : anObject};
Also, do not forget the # sign in front of NSString literal.
Key first, value second.
NSDictionary *d = #{#"key" : nObject};