NSInvocation pass C-arrays to Objective-C method

569 views Asked by At

I want to pass C-arrays to a method in Objective-C after a delay. Typically I could performSelector:withObject:afterDelay but I can't change the arrays in any way or convert them to NSMutableArrays, NSDictionaries or any other Cocoa object - they need to be C Arrays. In my research here on StackOverflow and Google I've found that one way to pass a C primitive is to wrap them in an NSInvocation. I've tried doing this with the code below and setting the arguments as pointers to the arrays being passed.

float gun1_vertex[24][8] = { {data,...data},{data,...data}, ... {data,...data} };
float gunDown1_vertex[24][8] = { {data,...data},{data,...data}, ... {data,...data} };

NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:@selector(testMethod:secondCArray:)]];
  [inv setSelector:@selector(testMethod:secondCArray:)];
  [inv setTarget:self];
  [inv setArgument:&gun1_vertex[0][0] atIndex:2];
  [inv setArgument:&gunDown1_vertex[0][0] atIndex:3];
  [inv performSelector:@selector(invoke) withObject:nil afterDelay:0.1f];

My test app keeps crashing when I attempt to print a few of the values from the passed arrays in the method below. I'm probably just missing something completely obvious. Can somebody please shed some light here?

- (void)testMethod:(float *)test secondCArray:(float *)test2 {

    for ( int a = 0 ; a < 10 ; a++ ) {

        NSLog(@"%f %f",test[a],test2[a]);

    }

}
3

There are 3 answers

4
Richard J. Ross III On BEST ANSWER

You could do something like this:

-(void) testMethod:(NSData *) arrayone secondArray:(NSData *) arraytwo
{
    float **gun1_vertex = (float **)[arrayone bytes];
    float **gunDown1_vertex = (float **)[arraytwo bytes];

    // ...
}

NSData *gun1data = [NSData dataWithBytes:(void *)gun1_vertex     length:sizeof(float) * 24 * 8];
NSData *gun1downData = [NSData dataWithBytes:(void *)gunDown1_vertex length:sizeof(float) * 24 * 8];

[inv setArgument:&gun1data atIndex:2];
[inv setArgument:&gun1downData atIndex:3];
1
ZhangChn On

To make the call after a delay, use dispatch_after block:

    double delayInSeconds = 0.1;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        [self testMethod:&gun1_vertex[0][0] secondCArray:&gunDown1_vertex[0][0]];
    });
4
CRD On

Your problem is not enough indirection. setArgument takes a pointer to the location of the value you wish to set as the argument, and the value in your case is also a pointer... Using the passed pointer and the type information setArgument can copy the correct number of bytes.

So you need something like:

float *gun1_vertex_pointer = &gun1_vertex[0][0];
[inv setArgument:&gun1_vertex_pointer atIndex:2];