iOS 5.0 responsesToSelector всегда возвращает false в NSOperation - PullRequest
1 голос
/ 20 января 2012

У меня проблемы с переносом статической библиотеки для нашей облачной службы баз данных в ARC.

Я дошел до стадии, когда она компилируется и запускается, но никогда не вызывает делегата.

У меня есть 2 класса, прокси-класс и APIOperation.

APIOperation - это подкласс NSOperation, который использует NSURLConnection для извлечения данных из веб-API.

Прокси-сервер имеет NSOperationQueue ив основном является делегатом для всех вызовов APIOperation.

Модель использования выглядит следующим образом:

  • Класс пользователя создает экземпляр объекта прокси.
  • Прокси создает экземпляр объекта APIOperationи добавляет его в NSOperationQueue
  • APIOperation создает NSURLConnection
  • При подключенииDidFinishLoading
    • Ответ анализируется, а затем возвращается обратно в прокси-класс через NSInvocation.
    • Делегат класса вызывает вызовы(пользовательский класс) и передает ответ API.

Код выглядит следующим образом:

проксикласс:

@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 (значительно упрощенный):

@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

Теперь, как я уже сказал, это работает вплоть до строки 'if ([делегат RespondsToSelector ...' в connectionDidfinishLoading.В этот момент он всегда возвращает false.Теперь, предполагая, что это связано с ARC, я проверил, что делегат не является нулевым, и значение там есть, также свойство делегата объявлено в APIOperation.h как:

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

Если я удаляюRespondsToSelector проверяет, затем приложение вылетает в main () со следующей обратной трассировкой:

#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 ()

Буду признателен за любую помощь, которую вы можете предложить.

Спасибо

Майк

Ответы [ 2 ]

0 голосов
/ 09 марта 2012

Как предположил Дэвид, проблема была в том, что делегат был освобожден.

Проблема была решена простым присвоением свойству "(strong)" вместо unsafe_unretained или __weak.

@property (strong) id<KSAPIOperationDelegate,NSObject> delegate;
0 голосов
/ 20 января 2012

Это не имеет ничего общего с ARC.

Если он сообщает, что не отвечает на селектор, то это не так. Пойми это. Скорее всего, у вас есть опечатка в селекторе (например, они чувствительны к регистру). Или делегат на самом деле не то, что вы думаете.

Кстати, я бы удалил пробелы из ваших селекторов, например @selector(apiOperation: didCompleteWithResult:) выглядит неправильно, даже если компилятору это нравится.

...