Pass C array to a objective C method - objective-c

I want to make an array of integers with as little code as possible and pass that array to an objective C method.
I tried the below. sequence starts out as an array and is passed to setLights: but when sequence is looked at in the method (via breakpoint) it is no longer an array.
*EDIT: I didnt want to use an NSArray because an NSArray of integers is so verbose:
Using NSArray:
NSArray *sequence = [[NSArray alloc] initWithObjects: [NSNumber numberWithInt:0], [NSNumber numberWithInt:1], [NSNumber numberWithInt:2], [NSNumber numberWithInt:3], [NSNumber numberWithInt:4],[NSNumber numberWithInt:5],nil];
Using C array:
int sequence[6] = {0,1,2,3,4,5};
What am I doing wrong?
- (IBAction)testLights:(id)sender {
int sequence[6] = {0,1,2,3,4,5};
//int *sequence[0][1][2][3][4][5]; //also tried this
[self setLights:sequence];
}
- (void)setLights:(int *)sequence {
UIImageView *light=[lgtArray objectAtIndex: sequence[0]];
light.alpha = 0;
[UIView animateWithDuration:0.3f
animations:^{
light.alpha = 1;
}completion:nil
];
}

Use this syntax to pass the array:
- (void)setLights:(int[] )sequence

You are running into a bizarre feature of C that has propagated through its variants: the [mostly] equivalence of pointers and arrays.
if you do
int *sequence ;
then you can do
sequence [4] ;
or
*(sequence + 4)
Arrays and points are mostly interchangeable. Arrays in C variants are merely data allocation. Your definition of
- (void)setLights:(int *)sequence
conveys no information array information. You can still access sequence as though it is an array. setLights simply has no intrinsic information as to how many elements sequence has allocated to it.
The problem here is that your usage of the array in setLights needs to match how you have allotted the data.
If you did
sequence [100] = 10 ;
it would be syntactically correct but likely to create an error.

Is verbose only if you want. Use literals instead:
NSArray *sequence = #[#0, #1, #2, #3, #4, #5];
And access to the value like this:
UIImageView *light = lgtArray[[sequence[0] intValue]]];

Related

-[__NSCFNumber addObject:]: unrecognized selector sent to instance [duplicate]

This question already has answers here:
How can I debug 'unrecognized selector sent to instance' error
(9 answers)
Closed 8 years ago.
I created a multidimensional array as follows:
NSMutableArray *subArray = [NSMutableArray arrayWithObjects:[NSString string], [NSNumber numberWithInt:0], [NSMutableArray array], nil];
self.dataArray = [[NSMutableArray alloc] initWithCapacity:9];
for (int i = 0; i < 9; i++) {
[self.dataArray addObject:subArray];
}
then when I try to access and change values like this
NSNumber *num = self.dataArray[0][1];
int numInt = [num intValue];
NSNumber *newNum = [NSNumber numberWithInt:numInt + 1];
[self.dataArray[0][1] addObject:newNum];
// add item to dataArray
NSMutableArray *tmpArr= self.dataArray[0][2];
[tmpArr addObject:item];
[self.dataArray[0][2] addObject:tmpArr];
but I'm getting
-[__NSCFNumber addObject:]: unrecognized selector sent to instance
what exactly is the problem, I don't understand, thanks in advance!
In the first line you are treating the object in the array as a NSNumber (which it obviously is):
NSNumber *num = self.dataArray[0][1];
And here you treat the exact same object like an NSMutableArray:
[self.dataArray[0][1] addObject:newNum];
That won't work, because that object is an instance of NSNumber.
I don't know what you achieve so I can't help you with the correct code, but that's where your problem is. Maybe you just wanted to write:
[self.dataArray[0][2] addObject:newNum];
You should probably stop to use an "inner array" as data storage and switch to using a proper subclass. Currently your code is pretty much unreadable, using proper Objects to store your values would improve it a lot.
Btw, your multidimensional array is actually just one dimensional, because you add the exact same array multiple times.
You probably want to do this:
for (int i = 0; i < 9; i++) {
NSMutableArray *subArray = [NSMutableArray arrayWithObjects:[NSString string], [NSNumber numberWithInt:0], [NSMutableArray array], nil];
[self.dataArray addObject:subArray];
}
NSNumber *num = self.dataArray[0][1];
int numInt = [num intValue];
NSNumber *newNum = [NSNumber numberWithInt:numInt + 1];
[self.dataArray[0][1] addObject:newNum]; // 3. also, possibly here
// add item to dataArray
NSMutableArray *tmpArr= self.dataArray[0][2]; // 1. here
[tmpArr addObject:item]; // 2. and here
[self.dataArray[0][2] addObject:tmpArr];
You're getting this error because the first line I marked, tmpArr is actually of type NSNumber. NSNumber is a class cluster, which is why you're seeing __NSCFNumber throw the error. All that is, is just a private subclass of NSNumber.
So the error is being thrown because you're trying to call addObject on a type of object that doesn't support it. Personally I wouldn't store more than one type of object in an array, but I don't know exactly what you're doing. Assuming you don't change the way you're storing things, what you can do is this:
NSMutableArray *tmpArr= self.dataArray[0][2];
if ([tmpArr isKindOfClass:[NSMutableArray class])
{
[tmpArr addObject:item];
}
else
{
NSLog(#"Woops, trying to add an object to something that's not a mutable array");
}
You would have to do this everytime you try to store an object into an array that you're pulling out of self.dataArray. What this does is verify that tmpArr is what you think it is.
Alternatively, you could check if it responds to addObject
if ([tmpArr respondsToSelector:#selector(addObject:)])
{
[tmpArr addObject:item];
}
The second way doesn't care what class it is, only if the method addObject can be used.

Two arrays with specific object at same index

How do I check if two arrays have one specific object that is not related at one common index for both arrays in Objective-C?
if ([[Array1 objectAtIndex:SameIndex] containsObject:String1] && [[Array2 objectAtIndex:SameIndex] containsObject:String2]) {
}
When I think of it, I may have to use a loop instead of an if statement.
Any help would be greatly appreciated.
The isEqual method allows you to compare two objects. You could do something similar to:
NSArray *array1 = #[[NSNumber numberWithInteger:10],
[NSNumber numberWithInteger:20],
[NSNumber numberWithInteger:30]];
NSArray *array2 = #[[NSNumber numberWithInteger:60],
[NSNumber numberWithInteger:70],
[NSNumber numberWithInteger:80]];
NSNumber *object1 = [NSNumber numberWithInteger:20];
NSNumber *object2 = [NSNumber numberWithInteger:70];
NSUInteger sameIndex = 1;
if ([[array1 objectAtIndex:sameIndex] isEqual:object1] && [[array2 objectAtIndex:sameIndex] isEqual:object2]) {
// Do something
NSLog(#"Validation passed!");
}
That will compare the object stored at index sameIndex in array1 and array2 with object1 and object2 respectively.
EDIT: I changed my code snippet into a working example for you to better understand.
You can use a few method on NSArray to find the object, for example:
[Aarray1 indexOfObject:...]
[Aarray1 indexOfObjectIdenticalTo:...]
[Aarray1 indexOfObjectPassingTest:...]
After that just make sure that you have an index and if index exists you know that there is an object. You have to just slightly amended code from your question.

How does one populate an NSMutable array of NSMutableSets?

I am using this code in a loop to populate an NSMutable Array of NSMutableSets (of NSString objects). The index of the NSSet is based on the length of the word.
// if set of this length not initialized yet, initialize set.
wordIndex = [NSString stringWithFormat:#"%d", currentWordLength];
if ([myWordArray objectForKey:wordIndex] == nil)
[myWordArray setObject:[[NSMutableSet alloc] initWithObjects:currentWord, nil] forKey:wordIndex];
else
[[myWordArray objectForKey:wordIndex] addObject:currentWord];
The final intention is to split up an array of words into an array of sets of words grouped by their lengths.
However, I see that [myWordArray count] is 0 after this. Why?
You are confusing the methods of NSMutableDictionary and NSMutableArray: In Objective-C arrays do not have keys but have indexes. If you change the class for myWordArray to NSMutableDicitionary it should work.
Try this, it looks very much like your logic, but (1) it uses NSNumbers as keys, which makes a little more sense, (2) handles the missing set condition more simply, but just adding the set, and (3) breaks up the source lines somewhat for easier debugging...
NSArray *inputStrings = // however these are initialized goes here
NSMutableDictionary *result = [NSMutableDictionary dictionary];
for (NSString *currentString in inputStrings) {
NSInteger currentWordLength = currentString.length;
wordIndex = [NSNumber numberWithInt:currentWordLength];
NSMutableSet *wordSet = [result objectForKey:wordIndex];
if (!wordSet) {
wordSet = [NSMutableSet set];
[result setObject:wordSet forKey:wordIndex];
}
[wordSet addObject:currentWord];
}
If you still have an empty dictionary after running this, it might be simpler to watch what's happening by stepping through it.

Adding negative NSNumber value to a NSDictionary

As someone who has some programming experience it pains me to be asking this question. I just started playing around with objective-c a few days ago and I am trying to simply add NSNumber objects to an NSDictionary. The problem is, when I add an NSNumber object with a negative value it seems as if it is being added as a string not an NSNumber.
Here is how I am initializing the dictionary:
testDict = [[NSDictionary alloc] initWithObjectsAndKeys:[NSNumber numberWithDouble:-3],#"x",
[NSNumber numberWithDouble:7, #"a",
nil];
I guess I really have two questions, 1.) Is this not how you create an NSNumber object that has a negative value?
2.) When I print out the dictionary I get the following:
NSLog(#"dictionary = %#", self.testDict);
a = 7;
x = "-3";
Why the double quotes around the -3?
You're correct, and everything's fine. That's just the dictionary -description being misleading.
To verify, break on the NSLog() and try (warning: typed on iPhone):
p [testDict objectForKey:#"x"];
It should reveal it to be an NSNumber instance.
#Conrad Shultz is right, it's just an artifact of how the the description method for the NSDictionary prints the dictionary contents (which is what is happening when you pass the dictionary to NSLog)
Another way to verify that everything is really working as expected is to iterate through the dictionary members and print the descriptions of the indivdual objects. Then you can see your negative number description looks like a number rather than a string.
NSDictionary* testDict = [[NSDictionary alloc] initWithObjectsAndKeys:[NSNumber numberWithDouble:-3],#"x", [NSNumber numberWithDouble:7], #"a", nil];
NSArray *keys = [testDict allKeys];
for (NSString *key in keys) {
NSLog(#"%# => %#", key, [testDict objectForKey:key]);
}
Console output is:
2012-02-29 12:38:39.544 test10[1055:f803] x => -3
2012-02-29 12:38:39.546 test10[1055:f803] a => 7

Is there any way to pass an NSArray to a method that expects a variable number of arguments, such as +stringWithFormat:

Some context: I'm trying to clean up some of my FMDB code. My one table has a lot of columns and the method in FMDB I need to use is one that expects a variable number of arguments, similar to NSString's class method +stringWithFormat:.
An example:
[db executeUpdate:#"insert into test (a, b, c, d, e) values (?, ?, ?, ?, ?)" ,
#"hi'", // look! I put in a ', and I'm not escaping it!
[NSString stringWithFormat:#"number %d", i],
[NSNumber numberWithInt:i],
[NSDate date],
[NSNumber numberWithFloat:2.2f]];
When a table only has 5 columns it's not that bad but when a column has 20+ it starts to get hairy.
What I'd like to do is create a dictionary with all db abstraction information and build these queries dynamically. My question is... How in Objective-C do I fake out that method expecting a variable number of arguments and instead perhaps hand it an NSArray?
Related info:
How can I write a method that takes a variable number of arguments, like NSString's +stringWithFormat:?
(Edit: This worked back in the GCC days. It doesn't under Clang as of Xcode 4.6.)
Get the objects in the array into a C array, then treat that as a varargs list:
//The example input array
int i = 42;
NSArray *array = [NSArray arrayWithObjects:
[NSString stringWithFormat:#"number %d", i],
[NSNumber numberWithInt:i],
[NSDate date],
[NSNumber numberWithFloat:2.2f],
nil];
//The example destination (using NSString so anyone can test this)
NSString *string = nil;
//The intermediary C array
NSObject **arrayObjects = malloc(sizeof(NSObject *) * [array count]);
if (arrayObjects) {
//Fill out the C array.
[array getObjects:arrayObjects];
//Use the C array as a va_list.
string = [[[NSString alloc] initWithFormat:#"%# %# %# %#" arguments:(va_list)arrayObjects] autorelease];
free(arrayObjects);
}
NSLog(#"string: %#", string);
Output:
2009-03-26 20:10:07.128 NSArray-varargs[606:10b] string: number 42 42 2009-03-26 20:10:07 -0700 2.2
In your case, you'll use the -[FMDatabase executeUpdate:arguments:] method.
It might be easier to just make a category on FMDatabase that takes an array and does the updates. You should be able to copy most of executeUpdate to do it.
I think NSInvocation may do what you're looking to do.
Just be careful when calling setArgumentForIndex because args 0 and 1 are implicit ones that Obj-C fills in, where arg 2 is the first "real" arg that you're passing.
This may not be the example you're looking for. But in this case I'd put your string values into an array and then use [theArray componentsJoinedByString:#","] to turn them into your sql argument list.