Odd behaviour with NSInvocation, performSelectorOnMainThread and NSClassFromString

  • Please bear with me, I'm only a Java programmer.

    I'm looking to capture the result of performSelectorOnMainThread, and
    in particular I'm trying to invoke NSClassFromString, in order to
    ensure that I build load QTMovie on the main thread.

    With a little help from our Cocoa guys, I've come up with the following

    @implementation SelectorHelper : NSObject
    + (id) performSelectorOnMainThread:(SEL) aSelector onTarget:
    (NSObject*) theTarget withObject:(id) arg {
    NSString* selectorName = aSelector != nil ? NSStringFromSelector
    (aSelector) : nil;
    NSString* className = NSStringFromClass([theTarget class]);
    NSLog(@"Calling performSelectorOnMainThread: %@ onTarget: %@
    withObject: %@", selectorName, theTarget, arg);
    NSMethodSignature* methodSignature  = [theTarget
    methodSignatureForSelector: aSelector];
    if (!methodSignature) {
      NSLog(@"No methodSignature found");
           return nil;
    } else {
      NSLog(@"methodSignature : %@", methodSignature);
    }
    NSInvocation* invocation = [NSInvocation
    invocationWithMethodSignature: methodSignature];
    [invocation setTarget: theTarget];
    [invocation setSelector: aSelector];
    if (arg) {
      [invocation setArgument:&arg atIndex:2];
    }
    [invocation performSelectorOnMainThread: @selector(invoke)
    withObject: nil waitUntilDone:YES];
    void* result = malloc([methodSignature methodReturnLength]); // TODO
    [invocation getReturnValue: result];
    NSLog(@"performSelectorOnMainThread returning %@", result);
    return result;
    }

    + (Class) createClass:(NSString*) className {
    Class result = NSClassFromString(className);
    NSLog(@"createClass returning %@ of type %@", result, [result class]);
    return result;
    }

    @end

    Invoking [SelectorHelper performSelectorOnMainThread: @selector
    (createClass) onTarget:SelectorHelper withObject:@"QTMovie"]
    gives the following log

    Calling performSelectorOnMainThread: createClass: onTarget:
    SelectorHelper withObject: QTMovie
    methodSignature : NSMethodSignature: types=#@:@ nargs=3
    sizeOfParams=12 returnValueLength=4;
    createClass returning QTMovie of type QTMovie
    performSelectorOnMainThread returning <QTMovie: 0x323930 time scale =
    0, duration = 0, rate = 0.000000, tracks = { }>

    As I read this (and looking in the debugger), createClass is
    returning a Class for QTMovie, but the result of the NSInvocation is
    an instance of QTMovie.

    Does this make sense to anyone? I fear I must be doing something
    stupid, but cannot see what.

    Thanks in anticipation

    Duncan McGregor
  • On Nov 16, 2007, at 2:26 PM, Duncan McGregor wrote:

    > void* result = malloc([methodSignature methodReturnLength]); // TODO
    > [invocation getReturnValue: result];
    > NSLog(@"performSelectorOnMainThread returning %@", result);

    -[NSInvocation getReturnValue:] takes a pointer to the buffer in
    which to store the return value.  The return value in your test is of
    type Class, so after the call to getReturnValue:, you should think of
    the variable with identifier "result" of having type pointer-to-Class.

    To get the actual return value, dereference the result:  Class
    returnValue = *(Class *)result.

    All ObjC objects also have a Class as the first word. That is why the
    log message indicates that you have a real QTMovie - the intial
    layout of your pointer-to-class buffer happens to match the layout
    for an ObjC object.  It is not, however, a real object.

    Hope this makes sense,
    -Peter
  • Thank you Peter, I think I'm understanding you.

    So as I want
    performSelectorOnMainThread:aSelector:onTarget:withObject: to return
    id, I should leave the code alone, effectively returning Class*, and
    do the cast in any calling code.

    On 16 Nov 2007, at 22:49, Peter Ammon wrote:

    >
    > On Nov 16, 2007, at 2:26 PM, Duncan McGregor wrote:
    >
    >> void* result = malloc([methodSignature methodReturnLength]); // TODO
    >> [invocation getReturnValue: result];
    >> NSLog(@"performSelectorOnMainThread returning %@", result);
    >
    > -[NSInvocation getReturnValue:] takes a pointer to the buffer in
    > which to store the return value.  The return value in your test is
    > of type Class, so after the call to getReturnValue:, you should
    > think of the variable with identifier "result" of having type
    > pointer-to-Class.
    >
    > To get the actual return value, dereference the result:  Class
    > returnValue = *(Class *)result.
    >
    > All ObjC objects also have a Class as the first word. That is why
    > the log message indicates that you have a real QTMovie - the intial
    > layout of your pointer-to-class buffer happens to match the layout
    > for an ObjC object.  It is not, however, a real object.
    >
    > Hope this makes sense,
    > -Peter
    >
previous month november 2007 next month
MTWTFSS
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30    
Go to today