-[__NSArrayI replaceObjectAtIndex:withObject:]: unrecognized selector sent to instance - objective-c

I've got an Mutable NSArray storing object types (ids - NSNumber, NSString) that will store digits (1,2,3,4, etc), operations (+,-,/,*, etc.), and variables (x,y,z, etc.). I have the variables and related values stored in an NSDictionary, keys are NSStrings equal to x,y,z with NSNumber values 5,5,2 respectively. I want to replace my variable in my NSArray with the actual value stored in my NSDictionary. I keep getting the following error when I attempt to replace the object. Please help.
-[__NSArrayI replaceObjectAtIndex:withObject:]: unrecognized selector sent to instance
NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithDouble:5],#"x",
[NSNumber numberWithDouble:5],#"y",
[NSNumber numberWithDouble:2],#"z",
nil];
+ (double)runProgram:(id)program
usingVariableValues:(NSDictionary *)variableValues;
{
// introspection - ensure program is NSArray and variableValues is NSDictionary
if ([program isKindOfClass:[NSArray class]] && [variableValues isKindOfClass: [NSDictionary class]])
{
// array for program stack
NSMutableArray *stack = [program copy];
// Loop to replace variables with actual values in NSDictionary
for (NSUInteger i = 0; i < [stack count]; i++)
{
NSLog(#"object at index = %#", [stack objectAtIndex:i]);
if ([[stack objectAtIndex:i] isKindOfClass:[NSString class]] && [[stack objectAtIndex:i] isEqualToString: #"x"])
{
// replace variable with value in corresponding value in dictionary
NSNumber *numberKeyValue = [variableValues objectForKey:#"x"];
[stack replaceObjectAtIndex:i withObject:numberKeyValue];
}
}
return 0
}

NSArray's copy method always returns an immutable array, regardless of whether the original was mutable or immutable. If you want a mutable copy, you need to use mutableCopy. (It's a Cocoa convention. NSString, NSDictionary, and generally any class that has mutable and immutable variants will work the same way.)

Related

Setting a value for a key in a nested NSDictionary error (immutable)

So I've got this code:
self.items is an NSMutableArray
[self.items enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(NSDictionary *checkin, NSUInteger index, BOOL *stop) {
if ([checkin[#"_id"] isEqualToString:checkinID]) {
NSMutableDictionary *checkin = (NSMutableDictionary *)self.items[index];
NSMutableDictionary *user_data = (NSMutableDictionary *)checkin[#"user_data"];
user_data[#"likes_checkin"] = #1;
checkin[#"user_data"] = user_data;
self.items[index] = checkin;
}
}];
But receive the error:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFDictionary setObject:forKey:]: mutating method sent to immutable object'
I'm not sure how this error is occurring?
Is the approach I'm taking to do this the best way? It seems very verbose...
You seem to be casting immutable dictionaries to mutable dictionaries:
NSMutableDictionary *checkin = (NSMutableDictionary *)self.items[index];
That will not work. A cast will not actually alter the object and change it from an NSDictionary to NSMutableDictionary. This would:
NSMutableDictionary *checkin = [self.items[index] mutableCopy];
mutableCopy, when available, will make a new copy of the object that is mutable. Calling mutableCopy on an NSDictionary will result in a new NSMutableDictionary with the original dictionary's objects and keys.
You are modifying the array while you are enumerating it and you are attempting to modify an immutable dictionary, not a mutable one.
If you are looking to modify just one dictionary in the array then first find the index of the dictionary you want to change and then change the dictionary outside of the enumeration:
__block NSUInteger foundIndex = NSNotFound;
[self.items enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(NSDictionary *checkin, NSUInteger index, BOOL *stop) {
if ([checkin[#"_id"] isEqualToString:checkinID]) {
foundIndex = index;
*stop = YES;
}
}];
if (foundIndex != NSNotFound) {
NSMutableDictionary *checkin = [self.items[foundIndex] mutableCopy];
NSMutableDictionary *user_data = [checkin[#"user_data"] mutableCopy];
user_data[#"likes_checkin"] = #1;
checkin[#"user_data"] = user_data;
self.items[foundIndex] = checkin;
}
self.items is a NSMutableArray object but an item is immutable NSDictionary. You are adding an NSDictionary object inside self.items. Therefore add an NSMutableDictionary object to get immutability.
You can do so:
NSDictionary *yourImmutableDictionary = [NSDictionary doSomething];
NSMutableDictionary *yourMutableDictionary = [NSMutableDictionary dictionaryWithDictionary:yourImmutableDictionary];
or
NSMutableDictionary *yourMutableDictionary = [yourImmutableDictionary mutableCopy];

How to count items in plist

I got a question how i can count items in my plist file. I tried:
NSString *bundlePathofPlist = [[NSBundle mainBundle]pathForResource:#"Mything" ofType:#"plist"];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:bundlePathofPlist];
NSArray *dataFromPlist = [dict valueForKey:#"some"];
for(int i =0;i<[dataFromPlist count];i++)
{
//NSLog(#"%#",[dataFromPlist objectAtIndex:i]);
[self setTableData:[dataFromPlist count]];
}
NSLog(#"%#", tableData);
But on this line an error appears:
[self setTableData:[dataFromPlist count]];
Implicit conversion of 'NSUInteger' (aka 'unsigned int') to 'NSArray *' is disallowed with ARC
and warning:
Incompatible integer to pointer conversion sending 'NSUInteger' (aka 'unsigned int') to parameter of type 'NSArray *';
It looks like your setTableData takes an NSArray instance. You need to prepare an array upfront in a loop, and then set it once, like this:
NSMutableArray *data = [NSMutableArray array];
for(int i =0;i<[dataFromPlist count];i++)
{
//NSLog(#"%#",[dataFromPlist objectAtIndex:i]);
[data addObject:[NSNumber numberWithInt:[[dataFromPlist objectAtIndex:i] count]]];
}
[self setTableData:data];
This assumes that your setTableData method expects an array of NSNumber instances wrapping ints.
The problem is you're using [dataFromPlist count] inside your for loop, which makes no sense. You probably meant [dataFromPlist objectAtIndex:i].
Or, more idiomatically,
for (NSArray *elt in dataFromPlist) {
[self setTableData:elt];
}
Although I do have to wonder why you're calling -setTableData: over and over with different elements.

assigning NSNumber object to NSString object won't cause any error or warning

I've put NSNumber object into NSDictionary object, and then pop and assign it to a variable declared as NSString instance.
NSString *test;
NSDictionary *dict = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:1000] forKey:#"key"];
test = [dict valueForKey:#"key"];
NSLog(#"%#, type of %#", test, NSStringFromClass([test class]));
return 0;
After running the above code, I've found that the type of test, declared as (NSString *) is __NSCFNumber. Why did it happen and why compiler did give no warning or errors?
Do I have to NSString constructor, such as NSStringWithFormat..., in order to keep test as NSString's instance?
The reason this did not fail or warn you is because valueForKey: returns an object of type id which you can assign to any Objective-C type. You need to be aware of what type of value you are getting back from your collections to safely use them. In this case you know it contains an NSNumber so you should expect an NSNumber and if you need a string you will need to do the proper conversions.
NSNumber *test = [dict valueForKey:#"key"];
NSLog(#"%#, type of %#", test, NSStringFromClass([test class]));
//If you need a string
NSString *testStr = [test stringValue];

How does Object copying work in objective c

I'm a bit confused on how copying objects works in Objective C. Here's what i know: When you copy an object, you get a distinct object in memory that contains all the same elements from the object that you have just copied and increments the retain count for each element. Also, copying each element in the array object from the original to a new location meant just copying the reference from one element of the array to another. So, the old and the new are pointing to the same element.
look at the below code: why is it that when you remove an object it only affects one object and when you change the element, it affects both the original and the copy object? Shouldn't the remove affects both objects?
NSMutableArray *dataArray = [NSMutableArray arrayWithObjects:
[NSMutableString stringWithString:#"one"],
[NSMutableString stringWithString:#"two"],
[NSMutableString stringWithString:#"three"], nil];
NSMutableArray *dataArray2;
NSMutableString *mStr;
NSLog(#"1-dataArray: ");
for( NSString *elem in dataArray)
NSLog(#" %#", elem);
dataArray2 = [dataArray mutableCopy];
[dataArray2 removeObjectAtIndex:0];
NSLog(#"2-dataArray2: ");
for( NSString *elem in dataArray2)
NSLog(#" %#", elem);
mStr =[dataArray objectAtIndex:1];
[mStr appendString:#"ONE"];
NSLog(#"3-dataArray: ");
for( NSString *elem in dataArray)
NSLog(#" %#", elem);
NSLog(#"4-dataArray2: ");
for( NSString *elem in dataArray2)
NSLog(#" %#", elem);
[dataArray2 release];
mutableCopy performs a 'shallow copy' of the NSArray's contents. eg. it is copying the pointers (and presumably retaining them) from the origin array. It is not copying the data those pointers are pointing to.
If we were to do this explicitly it's essentially doing:
-(NSMutableArray*)mutableCopy
{
NSMutableArray *newArray = [[NSMutableArray alloc] init];
for (id elem in originalArray)
[newArray addObject: elem];
return newArray;
}
Though presumably it's doing it more efficiently by using its access to the internal data structures.

NSArray filled with bool

I am trying to create an NSArray of bool values. How many I do this please?
NSArray *array = [[NSArray alloc] init];
array[0] = YES;
this does not work for me.
Thanks
NSArrays are not c-arrays. You cant access the values of an NSArray with array[foo];
But you can use c type arrays inside objective-C without problems.
The Objective-C approach would be:
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:[NSNumber numberWithBool:YES]];
//or
[array addObject:#(NO)];
...
BOOL b = [[array objectAtIndex:0] boolValue];
....
[array release];
EDIT: New versions of clang, the now standard compiler for objective-c, understand Object subscripting. When you use a new version of clang you will be able to use array[0] = #YES
Seems like you've confused c array with objc NSArray. NSArray is more like a list in Java, into which you can add objects, but not values like NSInteger, BOOL, double etc. If you wish to store such values in an NSArray, you first need to create a mutable array:
NSMutableArray* array = [[NSMutableArray alloc] init];
And then add proper object to it (in this case we'll use NSNumber to store your BOOL value):
[array addObject:[NSNumber numberWithBool:yourBoolValue]];
And that's pretty much it! If you wish to access the bool value, just call:
BOOL yourBoolValue = [[array objectAtIndex:0] boolValue];
Cheers,
Pawel
Use [NSNumber numberWithBool: YES] to get an object you can put in the collection.