iOS 5.0 respondsToSelector 总是在 NSOperation 中返回 false

发布于 2024-12-28 11:33:33 字数 4629 浏览 4 评论 0原文

我在将云数据库服务的静态库移植到 ARC 时遇到问题。

我已经到了它编译和运行的阶段,但它永远不会回调委托。

我有 2 个类,一个代理类和一个 APIOperation。

APIOperation 是 NSOperation 的子类,它使用 NSURLConnection 从 Web API 获取数据。

代理有一个 NSOperationQueue,基本上是所有 APIOperation 调用的委托。

使用模型如下:

  • 用户类创建代理对象的实例。
  • Proxy实例化APIOperation对象并将其添加到NSOperationQueue
  • 创建NSURLConnection
  • APIOperation在connectionDidFinishLoading上
    • 解析响应,然后通过 NSInitation 将其传回代理类。
    • 代理类调用委托(用户类)并传回 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

现在正如我所说,直到connectionDidfinishLoading中的“if([delegate respondsToSelector...”行。此时它总是返回 false。现在假设这个与 ARC 有关,我检查了委托不为 null 并且值存在,委托属性也在 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 ()

感谢您提供的任何帮助,

谢谢迈克

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

℉服软 2025-01-04 11:33:33

这与ARC没有任何关系。

如果它报告它没有响应选择器,那么它就不会。弄清楚这一点。您很可能在选择器中出现拼写错误(例如,它们区分大小写)。或者委托实际上并不是您想象的那样。

顺便说一句,我会从选择器中删除空格,例如 @selector(apiOperation: didCompleteWithResult:) 看起来是错误的,即使编译器喜欢它。

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, e.g. @selector(apiOperation: didCompleteWithResult:) looks wrong even if the compiler likes it.

ˇ宁静的妩媚 2025-01-04 11:33:33

正如大卫所说,这是代表被释放的问题。

通过简单地将属性设置为“(strong)”而不是 unsafe_unretained 或 __weak 就解决了这个问题。

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

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;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文