2

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]);

    }

}
PhilBot
  • 748
  • 18
  • 85
  • 173
  • Can you change the method to accept an object? If so, you can stick the bytes in an `NSData` and then unpack them on the other side. – jscs Jan 31 '12 at 00:52
  • @JoshCaswell: Or just create an intermediate "thunk" method that does the unpacking, and turns around and calls the desired target method. – Ben Zotto Jan 31 '12 at 01:02
  • Nope, I can't modify the C-arrays. Does the syntax look OK for the NSInvocation? It seems like it should work - a pointer to the first element of the multi-dim array and then just printing out a few of the values after passing to the testMethod. I'm getting a EXC_BAD_ACCESS at the NSLog line. – PhilBot Jan 31 '12 at 01:02
  • @quixoto: Excellent idea. phil: We're not suggesting _modifying_ the arrays, just putting them in a wrapper. Re. EXC_BAD_ACCESS: are you sure the memory is still valid when the method gets called? The `NSInvocation` copies the pointer you give it, but it can't copy the data itself, since it doesn't know how much there is. – jscs Jan 31 '12 at 01:06
  • Oh perhaps wrapping it would be OK then. Could you please show me how you mean to pack/unpack it in an NSData? I am sure the data is still valid as it is imported from a header file and is a static array of vertices. – PhilBot Jan 31 '12 at 01:10
  • Wait, what? If these are static arrays, why does the method need to accept them as arguments? Just refer to them in the delayed method the same way you're referring to them in the calling method. – jscs Jan 31 '12 at 01:23
  • Maybe you can pack those pointers in `NSValue` with `+valueWithPointer:`, and then pack `NSValue`s in a single `NSDictionary`. – ZhangChn Jan 31 '12 at 01:25
  • I have a question. Why are you sending the address of the first element in the vertex array? Why not just send the array? – Richard J. Ross III Jan 31 '12 at 01:46
  • Thanks for the comments guys! I've solved my problem with your answers here and below. – PhilBot Jan 31 '12 at 02:42

3 Answers3

3

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];
Richard J. Ross III
  • 55,009
  • 24
  • 135
  • 201
  • Thank you this is what I was looking for as far as an example to wrap with NSData. – PhilBot Jan 31 '12 at 02:40
  • Constructing an `NSData` with `dataWithBytes:` will *copy* the original arrays, which is surely rather excessive given the original arrays are static? Also the types of the variables in `testMethod` should be `float *` (C arrays are rectangular, not vectors of vectors...). – CRD Jan 31 '12 at 03:43
  • @CRD the arrays are not static, they are allocated on the stack, and will be destroyed when the function exits, making them junk when the testMethod is executed. Thus NSData is used. – Richard J. Ross III Jan 31 '12 at 16:54
  • THIS IS WRONG! `setArgument` requires a *pointer* to the actual argument. That means, when the argument is a pointer to object (`NSData *`), you need to give it a pointer to pointer (`NSData **`). `[NSData dataWithBytes:...` is not what you need to pass; you need to pass a pointer to that. – user102008 Apr 20 '12 at 17:26
3

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];
CRD
  • 52,522
  • 5
  • 70
  • 86
  • Thanks this clears up my original issue with understanding the setArgument part of the invocation. – PhilBot Jan 31 '12 at 02:41
  • 1
    I don't understand. Why are you getting the address of the first element of the array? Just send the array itself, that just adds complexity. – Richard J. Ross III Jan 31 '12 at 16:56
  • @user102008 - correct! Egg on face, I made the same comment to another answer and made the same typo :-( Fixed. – CRD Apr 22 '12 at 22:48
  • @RichardJ.RossIII - sorry missed your comment. By now you know the answer as you've just fixed your own response. – CRD Apr 22 '12 at 22:49
1

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]];
    });
ZhangChn
  • 3,154
  • 21
  • 41