开发者

Sending unrecognized selector to object, why terminate application?

When studying objective-C one of the consequences of it's high degree of dynamism is that one can send any message to an object even when it will not respond to it at runtime.

It will then just ignore the message and raise an exception.

In an practical case I'm trying to send a message to a delegate object which is not yes implemented in the delegate.

Ofcourse I'll have to implement it to have my functionality but purely out of interest I was wondering why my application crashes when I don't.

2011-06-05 17:44:39.280 myTest[28158:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[TVC DoneLoadi开发者_如何学PythonngNoStoriesFound:]: unrecognized selector sent to instance 0x5c0ef90'
*** Call stack at first throw:
(
    0   CoreFoundation                      0x015cabe9 __exceptionPreprocess + 185
    1   libobjc.A.dylib                     0x0171f5c2 objc_exception_throw + 47
    2   CoreFoundation                      0x015cc6fb -[NSObject(NSObject) doesNotRecognizeSelector:] + 187
    3   CoreFoundation                      0x0153c366 ___forwarding___ + 966
    4   CoreFoundation                      0x0153bf22 _CF_forwarding_prep_0 + 50
    5   myTest                             0x0003db93 -[Loader loadTasksForStoriesForDisplayedWorkpace] + 1268
    6   myTest                             0x0003d388 -[Loader requestFinished:] + 1936
    7   myTest                             0x00017f2e -[ASIHTTPRequest reportFinished] + 100
    8   Foundation                          0x001f69a6 __NSThreadPerformPerform + 251
    9   CoreFoundation                      0x015ac01f __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 15
    10  CoreFoundation                      0x0150a28b __CFRunLoopDoSources0 + 571
    11  CoreFoundation                      0x01509786 __CFRunLoopRun + 470
    12  CoreFoundation                      0x01509240 CFRunLoopRunSpecific + 208
    13  CoreFoundation                      0x01509161 CFRunLoopRunInMode + 97
    14  GraphicsServices                    0x01b0f268 GSEventRunModal + 217
    15  GraphicsServices                    0x01b0f32d GSEventRun + 115
    16  UIKit                               0x0048642e UIApplicationMain + 1160
    17  myTest                             0x0000272b main + 85
    18  myTest                             0x000026cd start + 53
)
terminate called after throwing an instance of 'NSException'
Program received signal:  “SIGABRT”.
kill
quit


It crashed because the default behaviour of an object that receives an unrecognized selector is to throw an exception.

And if you don't have a @try / @catch block that catches the exception, the uncaught exception causes your application to terminate.

You can, however change this default behaviour by overriding the methods that handle unrecognized selector. Check out these NSObject methods:

  • forwardingTargetForSelector:
  • resolveInstanceMethodDynamically:
  • forwardInvocation:
  • doesNotRecognizeSelector:

There's also nice article by Mike Ash here, which gives a good overall summary.

The difference that dynamism buys you is that you throw an exception on an unrecognized selector, which allows you to decide how to respond to the error (instead of quitting immediately with a segmentation fault). You also get the option of overriding how objects respond to unrecognized selectors. However, you do want them to throw exceptions most of the time, because it's usually a sign that you've done something wrong.


Like that already mentioned, you are trying to send a message to an object that can't be responded. For delegates, it makes sense to see if they respond to the selector before messaging them. That check should go something like this,

if ( [delegate respondsToSelector:@selector(DoneLoadingNoStoriesFound:)] ) {
    [delegate DoneLoadingNoStoriesFound:YES];
}


Unless I'm missing something, the exception is thrown as you note but there's nothing in place to catch it so the application terminates.


Make a good habit likeif ( [delegate respondsToSelector:@selector(DoneLoadingNoStoriesFound:)] ) { [delegate DoneLoadingNoStoriesFound:YES]; } whenever you want to send message to delegate.


I think what you got wrong is that you can indeed send any message you want to an object even if it doesn't respond to it at compile time, not runtime. This pattern is possible because classes can be modified at runtime so they can respond to additional messages. When you go that way though you will need to take measures and handle cases when the object doesn't respond to the specific message, either with try-catch or something else.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜