Changing NSNumber nested in NSMutableDictionary and NSMutableArray - ios

I'm not to be able to change a NSNumber within a NSMutableDictionary. The 'Before' NSLog prints but then crashes when I change the number.
As you can see the class is of type NSNumber and therefore I should be able to change the number with the class method 'numberWithIn'. However, I get the error "unrecognized selector sent to instance." Which is strange because if the method didn't exist, the if statement would not work? Thoughts? Have I just missed something strange?
Data is an NSMutableArray and within are NSMutableDictionaries from json data. Data has been initialised.
if([[Data[i] objectForKey:#"Pop"] isKindOfClass:[NSNumber class]]){
NSLog(#"Before: %#",[Data[i] objectForKey:#"Pop"]);
[[Data[i] objectForKey:#"Pop"] numberWithInt:5];
NSLog(#"After: %#",[Data[i] objectForKey:#"Pop"]);
}

if([[Data[i] objectForKey:#"Pop"] isKindOfClass:[NSNumber class]]){
NSLog(#"Before: %#",[Data[i] objectForKey:#"Pop"]);
[[Data[i] setObject:[NSNumber numberWithInt:5] forKey:#"Pop"];
NSLog(#"After: %#",[Data[i] objectForKey:#"Pop"]);
}
should work.
You didnt properly set a new NSNumber object.

[Data[i] objectForKey:#"Pop"] just access an element of your dictionary. If you want to change it you need a method that does so: [Data[i] setObject:[NSNumber numberWithInt:5] forKey:#"Pop] or more concisely [Data[i] setObject:#5 forKey:#"Pop"]. Did you know you can also access dictionary elements now like so: Data[i][#"Pop"]...

+(NSNumber *)numberWithInt:(int)value is a class method to create a new NSNumber. You cannot call it on an instance of NSNumber.

You should use one of those function :
[dataArray setObject:yourObject forKey:#"Pop"];
[dataArray replaceObjectAtIndex:theIndextoChange withObject:yourObject];

Related

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

Why can you sometimes cast a NSArray to NSMutableArray, and sometimes you can't?

Specifically:
self.words = (NSMutableArray*)[self.text componentsSeparatedByString:#" "];
works fine so long as there are separators. I see that the method returns the original string wrapped in an NSArray if there isn't, though. This single element NSArray stubbornly refuses to be cast as an NSMutableArray.
But, when I do:
self.words = [NSMutableArray arrayWithArray:self.words];
It works just fine.
Is there something I'm missing here? Is it bad practice to cast from NSArray to NSMutableArray?
You are confused.
This code:
self.words = (NSMutableArray*)[self.text componentsSeparatedByString:#" "];
...is wrong, and is setting you up for a crash later on. Casting just tells the compiler "trust me, I know what I'm doing." It does not change the type of the underlying object. The method componentsSeparatedByString returns an NSArray, not a mutable array. If you then try to mutate the resulting array, you will crash with an unrecognized selector message. With the cast, the compiler trusts you that your object will really be a mutable array at runtime, but it will not be.
This would crash:
self.words = (NSMutableArray*)[self.text componentsSeparatedByString:#" "];
[self.words addObject: #"new"];
However, this code:
self.words = [[self.text componentsSeparatedByString:#" "] mutableCopy];
[self.words addObject: #"new"];
...does the right thing. It doesn't cast a pointer, it is a method call to a method that takes an NSArray as input and returns a mutable array with the same contents. Thus the second line will work because the first line takes the immutable array it gets back from componentsSeparatedByString and uses it to create a mutable array.
It is bad practice to cast from NSArray to NSMutableArray. It may works if you are lucky because the array are constructed using NSMutableArray, but you can't rely on it.
If you want NSMutableArray from NSArray, use mutableCopy
self.words = [[self.text componentsSeparatedByString:#" "] mutableCopy];
Is it bad practice to cast from NSArray to NSMutableArray?
Yes. Bordering on nonsensical, really.
Typecasting does not change the object in any way at all, it just tells the compiler that it should regard the object as if it were an instance of the new type. At run time though, the object stays exactly the same. You may cast an NSArray to an NSMutableArray but it doesn't make the object transform into an NSMutableArray. It's still the same object of the same type.
Your misconception seems to be about the nature of casting. Casting doesn't change the object, it just tells the compiler to pretend that it's an object of that type. If the object really isn't an NSMutableArray, casting is not expected to make it become one.
NSString* string1 = #"this thing";
NSString* string2 = #"this";
NSMutableArray* array1;
NSMutableArray* array2;
array1 = (NSMutableArray*)[string1 componentsSeparatedByString:#" "];
array2 = (NSMutableArray*)[string2 componentsSeparatedByString:#" "];
[array1 addObject:string1]; //(A)
[array2 addObject:string1]; //(B)
This will break at (B) the last line with :
-[__NSArrayI addObject:]: unrecognized selector sent to instance 0x1702257a0
But will not break at (A)1
An object that is publicly declared as immutable may have been privately created as mutable. In this case, if you cast from the immutable public type to the private mutable type, everything will work fine. Where the object was not created as mutable, such a cast will not get you what you want.
In the case of componentsSerparatedByString, this suggests that the method implementation creates a mutable array only if it needs to - i.e. if it has to add more than one object to the array. If it only finds one object, you get an NSArray, if it finds more than one, you get an NSMutableArray. This is an implementation detail that is deliberately hidden from you as the user.
The interface tells you to expect an NSArray in all cases, and in all cases this will work.
You should not rely on such details to get you what you want. Stick to the public API, that's what it is for.
1 rather, as Bryan Chen points out, it may not break now but could well do so in the future
When you do that:
self.words = (NSMutableArray*)[self.text componentsSeparatedByString:#" "];
the array still remains immutable and if you send it a message from NSMutableArray class it will crash. The trick (NSMutableArray*) is only good to make the compiler happy.
In the second case:
self.words = [NSMutableArray arrayWithArray:self.words];
you do not CAST, you CREATE a new array object.
And of course you don't need to cast the arrays this way. NSMutableArray object IS already NSArray just like any object of a derived class is an object of a base class at the same time
Use second variant in all cases because it is right solution and more clearly for users who will support your code:
NSArray *array = [self.text componentsSeparatedByString:#" "];
self.words = [NSMutableArray arrayWithArray:array];
Never do this one:
self.words = [NSMutableArray arrayWithArray:self.words];
It will totally confuse all devs =)
In the first case, the componentsSeparatedByString: method is specifically returning an NSArray, which can't just be cast to the mutable type. If you wanted that to be mutable, you would have to do this:
self.words = [[self.text componentsSeparatedByString:#" "] mutableCopy];
The second one is calling the arrayWithArray: method on the NSMutableArray class, meaning it is making an instance of NSMutableArray. That's why it works. You can cast an NSMutableArray to an NSArray, but not the other way around, because an NSMutableArray is a subclass of NSArray.
Casting does nothing with an object. example:
NSString *mouse = (id)#[#"mouse"];
it will compile, but variable mouse is not NSString. it is NSArray. you can check it simply by writing
po mouse
in console.
The only way to create mutable copy of an object is to call 'mutableCopy' method on it:
NSArray *array = #[#"a"];
NSMutableArray *mutableCopy = [array mutableCopy];

NSMutableArray addObject getting set to nil after release of object

I have declared an NSMutableArray *arrAllRecordsM and am trying to add NSMutableDictionary *dicRecordM to it using addObject. The dicRecordM gets added to arrAllRecordsM but on doing [dicRecordM removeAllObjects] it sets to nil in arrAllRecordsM. Below is the code, please help me fix it.
self.arrAllRecordsM = [NSMutableArray array];
self.dicRecordM = [NSMutableDictionary dictionary];
// Some Method
[self.dicRecordM setObject:#"Test" forKey:#"ADDRESS"];
[self.arrAllRecordsM addObject:self.dicRecordM];
// Value: Test
NSLog(#"Value: %#", [[self.arrAllRecordsM objectAtIndex:0] valueForKey:#"ADDRESS"]);
[self.dicRecordM removeAllObjects];
// Value: null
NSLog(#"Value: %#", [[self.arrAllRecordsM objectAtIndex:0] valueForKey:#"ADDRESS"]);
Adding an object to an NSMutableArray just stores a pointer (or "strong reference")
to the object into the array. Therefore
[self.arrAllRecordsM objectAtIndex:0]
and
self.dicRecordM
are two pointers to the same object. If your remove all key/value pairs from self.dicRecordM then [self.arrAllRecordsM objectAtIndex:0] still points to the same
(now empty) dictionary. That is the reason why
[[self.arrAllRecordsM objectAtIndex:0] valueForKey:#"ADDRESS"]
returns nil.
If you want an independent copy of the dictionary in the array, use
[self.arrAllRecordsM addObject:[self.dicRecordM copy]];
copy can be used on many classes, such as NSDictionary, NSArray and NSString
and their mutable variants, to get a "functionally independent object". Formally, it is available for all classes conforming to the NSCopying protocol.
This is expected behavior.
You removed all the objects from the dictionary by calling removeAllObjects, then tried to retrieve an object from it and rightfully getting nil, since it doesn't exist in the dictionay anymore (you removed it).
What's maybe unclear to you is that NSArray doesn't copy the element you add to it, instead it just holds a strong reference.
So both dicRecordM and arrAllRecordsM are holding a reference to the same object, hence any modification to it (in this case removeAllObjects) will affect the same dictionary.
Incidentally, you shouldn't use valueForKey: for accessing the dictionary's entries. Use objectForKey: or the shorter subscripted syntax. For instance
self.arrAllRecordsM[0][#"ADDRESS"]
You can read this answer Difference between objectForKey and valueForKey? as a reference, but the main problem is that valueForKey: can behave very differently from objectForKey: in case the key contains special KVC characters, such as # or ..

How to check if a NSArray has the values of another array

I have my array unique that is my main array and my array kind. I need to check that only 1 value of kind is present in the array unique. Then if there is more than 1 value of the array kind in unique I need to unset all values but the first one used in the array.
The further i got to achieve this is with the following code but I can not store the indexpath of the found object to do a later comparison. xcode says "bad receiver type nsinteger"
could anyone help me to achieve this?
kind = #[#"#Routine",#"#Exercise",#"#Username"];
NSMutableArray *uniqueKind = [NSMutableArray array];
for (NSString* obj in kind) {
if ( [unique containsObject:obj] ) {
NSInteger i = [unique indexOfObject:obj];
[uniqueKind addObject: [i intValue]];
}
}
An NSInteger is like an int, so you can't send it a message ([i intValue]). Also, you can't add an NSInteger to an array without making it an NSNumber or some other object type. You can do it like this:
NSInteger i = [unique indexOfObject:obj];
[uniqueKind addObject: [NSNumber numberWithInteger:i]];
Also (without understanding what you're doing) you might want to use an NSSet instead of an array. And you can combine a couple of calls:
NSUInteger i = [unique indexOfObject:obj];
if ( i != NSNotFound ) {
[uniqueKind addObject:[NSNumber numberWithInteger:i]];
}
I'm not sure if it would solve your problem, but have you considered using sets (or mutable variation) instead of arrays? They ensure uniqueness and they allow you to check for intersection/containment. See the NSSet class reference.
You have to add objects to the NSMutableArray, not the actual intValue. Try converting teh integer to a NSNumber first.
[uniqueKind addObject: [NSNumber numberWithInt:i]];
instead.
(edited )

NsmutableArray setValue ObjectatIndex?

I could't setvalue of selected index with nsmutablearray.There is my code:
[[retArray objectAtIndex:1] setValue:str forKey:#"name"];
this line give error. What is the error?
correct code should be is
[retArray insertObject:str atIndex:1];
or
[retArray addObject:str];
or if you are using a dictionary
[retArray insertObject:[NSDictionary dictionaryWithObject:str forKey:#"name"] atIndex:1];
First of all you should explicitly tell us what exact error it gives you then what you are doing in your code is:
retrieving element at index 1
assuming that the item you got from the array is compliant to NSKeyValueCoding protocol (mostly a NSMutableDictionary)
setting a field of the element retrieved
Code is correct as it is, the only concern could be that the array is not mutable (hence NSArray instead that NSMutableArray)
you are using method of dictionary ...Refer NSMutableArray Reference

Resources