0

I'm doing an asynchronous read from a USB printer. The read works correctly. My trouble is updating a NSTextField from within the callback.

-(IBAction)printTest:(id)sender
{
    // Setup... then:

    NSLog(@"starting async read: %@", _printerOutput);
    NSLog(@"_printerOutput pointer = %p", _printerOutput);

    result = (*interface)->ReadPipeAsyncTO(interface,
                                           1,
                                           readBuffer,
                                           numBytesRead,
                                           500,
                                           1000,
                                           USBDeviceReadCompletionCallback,
                                           &(_printerOutput)
                                           );

The callback is defined as:

void USBDeviceReadCompletionCallback(void *refCon, IOReturn result, void *messageArg)
{
    NSTextField *printerOutput = (__bridge NSTextField *) messageArg;
    NSLog(@"_printerOutput pointer = %p", printerOutput);
}

The pointer loses its value when inside of the callback.

starting async read: <NSTextField: 0x10221dc60>
_printerOutput pointer = 0x10221dc60
_printerOutput pointer = 0x0

I've looked in many places trying to mimic different ways to pass in the pointer. There can be only one correct way. :)

Another variation on the theme: (__bridge void *)(_printerOutput). This doesn't work, either.

I understand that the callback is of type IOAsyncCallback1.

Other URLs of note: http://www.google.com/search?client=safari&rls=en&q=another+usb+notification+example&ie=UTF-8&oe=UTF-8 and updating UI from a C function in a thread

Community
  • 1
  • 1

1 Answers1

0

I presume _printerOutput is an NSTextField*?

First, is there a particular reason why are you passing an NSTextField** into the callback? (Note the ampersand in the last argument you're passing to ReadPipeAsyncTO.)

Second, I'd avoid ARC with sensitive code, just as a precaution.

Third, from what I see, last argument of ReadPipeAsyncTO is called refcon. Is it a coincidence that callback's first argument is called refCon? Note you're trying to get a text field from messageArg, not refCon.


To extend on my third point…

  1. ReadPipeAsyncTO has an argument called refcon. This is the last argument.
  2. Please pass _printerOutput there. Not a pointer to _printerOutput (do not pass &(_printerOutput)) -- _printerOutput is already a pointer.
  3. Now finally. Look at the first argument of the callback. It's called refcon. In fact -- let's see what Apple docs say about this callback:

refcon

The refcon passed into the original I/O request

My conclusion is that your code should read:

void USBDeviceReadCompletionCallback(void *refCon, IOReturn result, void *messageArg)
{
    NSTextField *printerOutput = (__bridge NSTextField *) refCon; // <=== the change is here
    NSLog(@"_printerOutput pointer = %p", printerOutput);
}

Can you, please, try this out? I get a feeling that you didn't try this.

Small but possibly important digression: Were it some other object, and if you didn't use ARC, I'd suggest retaining the _printerOutput variable when passing it into ReadPipeAsyncTO, and releasing it in the callback.

But, since the text field should, presumably, have the lifetime of the application, there is probably no need to do so.

ARC probably loses track of the need for the object behind the pointer to exist once it's passed into C code, but it doesn't matter, since the pointer is still stored in the printerOutput property. Besides, once a pointer is in C code, nothing can just "follow it around" and "reset it".

Confusion when it comes to understanding and explaining the concepts is precisely why I said "avoid ARC with sensitive code". :-)

Ivan Vučica
  • 9,529
  • 9
  • 60
  • 111
  • Yes, _printerOutput is the NSTextField object defined as @property (assign) IBOutlet NSTextField *printerOutput; It's safe to say my code is based on guessing at this point. The first argument of IOAsyncCallback1 is the original refCon, which in this case is the variable interface. – Caylan Van Larson Apr 08 '13 at 22:58
  • IOAsyncCallback1 is documented at http://developer.apple.com/library/mac/#documentation/IOKit/Reference/IOKitLib_header_reference/Reference/reference.html – Caylan Van Larson Apr 08 '13 at 23:04
  • There is also this thread, but this code still causes the pointer to become valueless. http://stackoverflow.com/questions/12916491/pass-an-objective-c-object-to-a-function-as-a-void-pointer – Caylan Van Larson Apr 08 '13 at 23:21
  • @CaylanVanLarson I'm also trying to be clairvoyant here, because I never dealt with `IOKit` in detail (at least not on this level, and not with printers). But I get the feeling that you didn't try looking at the value of `refcon` argument that the callback received. See my edit. – Ivan Vučica Apr 08 '13 at 23:47
  • 1
    You were right, refCon in the callback is the argument _printerOutput. Very nice. Thank you, Ivan. – Caylan Van Larson Apr 09 '13 at 00:23