简体   繁体   中英

iOS 5.0 respondsToSelector always returning false in NSOperation

I'm trouble porting a static library for our cloud database service to ARC.

I've got to the stage where it compiles and runs but it's never calling back the delegate.

I have 2 classes, a proxy class and an APIOperation.

The APIOperation is a subclass of NSOperation that uses NSURLConnection to fetch data from a web API.

The proxy has an NSOperationQueue, and basically is the delegate for all the APIOperation calls.

The usage model is as follows:

  • User class creates instance of proxy object.
  • Proxy instantiates APIOperation object and adds it to NSOperationQueue
  • APIOperation creates NSURLConnection
  • On connectionDidFinishLoading
    • Response is parsed then passed back to proxy class via NSInvocation.
    • Proxy class calls delegate (the user class) and passes back the API response.

The code is as follows:

proxy class:

@implementation theProxy

@synthesize callbackSelector,delegate,opQueue;

-(theProxy*)init{
    opQueue = [[NSOperationQueue alloc]init];
    return self;
}

- (void) apiOperation:(APIOperation*)operation didCompleteWithResult:(NSArray*)theResult{
    SEL selector = [operation callbackSelector];
    if ([delegate respondsToSelector:selector]) {
    NSInvocation* inv = [NSInvocation invocationWithMethodSignature:[[delegate class] instanceMethodSignatureForSelector:selector]];
    [inv setTarget:delegate];
    [inv setSelector:selector];
    theProxy* tmp = self;
    [inv setArgument:&tmp atIndex:2];
    [inv setArgument:&operation atIndex:3];
    [inv setArgument:&theResult atIndex:4];
    [inv invoke];        
}

}

- (void) apiOperation:(APIOperation*)operation didFailWithError:(NSString*)theError{
if ([delegate respondsToSelector:@selector(API:apiOperation:didFailWithError:)]) {
    [delegate API:self apiOperation:operation didFailWithError:theError];
}
}

-(void)cancelAllOperations{
[opQueue cancelAllOperations];
}

- (void)dealloc
{
[opQueue cancelAllOperations];
[opQueue release], opQueue = nil;
delegate = nil;
//[delegate release];  delegate should not be retained.

[super dealloc];
}

APIOperation class (vastly simplified):

@implementation APIOperation
@synthesize delegate,APIKey,secretKey,debugMode,callbackSelector,successCallbackMethodSignature,errorCallbackMethodSignature,delegateCallbackMethodSignature,tag,APIServer,requestProcessingTime,requestReceivedTime,responseCode,responseMessage,timestamp,requestRoundTripTime,requestStartMicroTime,useSSL;

-(void) main{
    receivedData = [NSMutableData data];
    connFinished = NO;
    // create the connection with the request
    // and start loading the data
    theConnection=[[NSURLConnection alloc] initWithRequest:[self buildRequest] delegate:self];
    if (theConnection) {

        do {
            [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
            } while (!connFinished);
    }
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection{

    id pList = [NSPropertyListSerialization propertyListFromData:receivedData mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&errorStr];

    theResponse = (NSDictionary*) pList;

    if ([delegate respondsToSelector:@selector(apiOperation: didCompleteWithResult:)]) {

        NSArray* theResultsArray = [theResponse objectForKey:@"payload"];

        NSInvocation *inv = [NSInvocation invocationWithMethodSignature:successCallbackMethodSignature];
        [inv setTarget:delegate];
        [inv setSelector:@selector(apiOperation: didCompleteWithResult:)];
        KSAPIOperation* tmp = self;
        [inv setArgument:&tmp atIndex:2];
        [inv setArgument:&theResultsArray atIndex:3];
        [inv performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:YES];

    }       
}
@end

Now as I say this works up until the line 'if([delegate respondsToSelector...' in connectionDidfinishLoading. At that point it always returns false. Now assuming this is to do with ARC, I've checked that the delegate is not null and the value is there, also the delegate property is declared in APIOperation.h as:

@property (unsafe_unretained) id<KSAPIOperationDelegate,NSObject> delegate;

If I remove the respondsToSelector check then the app crashes in main() with the following backtrace:

#0  0x0156b09b in objc_msgSend ()
#1  0xbfffde10 in ?? ()
#2  0x0132d437 in -[NSInvocation invoke] ()
#3  0x013c8e72 in -[NSObject performSelector:withObject:] ()
#4  0x009369ef in __NSThreadPerformPerform ()
#5  0x0139b97f in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ ()
#6  0x012feb73 in __CFRunLoopDoSources0 ()
#7  0x012fe454 in __CFRunLoopRun ()
#8  0x012fddb4 in CFRunLoopRunSpecific ()
#9  0x012fdccb in CFRunLoopRunInMode ()
#10 0x012b0879 in GSEventRunModal ()
#11 0x012b093e in GSEventRun ()
#12 0x0001ea9b in UIApplicationMain ()
#13 0x00002a58 in main (argc=1, argv=0xbfffed50) at /Users/MikeW/Desktop/ARC test proj/lib1.0Test/lib1/main.m:16
#14 0x000029b5 in start ()

Would appreciate any help you can offer.

Thanks

Mike

This would not have anything to do with ARC.

If it reports that it doesn't respond to selector, then it does not. Figure that out. Most likely you have a typo in the selector (they're case sensitive, for example). Or the delegate isn't in fact what you think it is.

BTW, I would remove the spaces from your selectors, eg @selector(apiOperation: didCompleteWithResult:) looks wrong even if the compiler likes it.

As David suggested it was a problem of the delegate being released.

The problem was resolved by simply making the property "(strong)" rather that unsafe_unretained or __weak.

@property (strong) id<KSAPIOperationDelegate,NSObject> delegate;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM