6

My code for registering uncaught exception handler using UncaughtExceptionHandler is as following, do you think there will be any potential issue?

@interface AppDelegate ()

void myHandler(NSException * exception);

@end

@implementation AppDelegate

void myHandler(NSException * exception)
{
  // ...
}

- (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    NSSetUncaughtExceptionHandler(&myHandler);
..

Is it possible to have a more concise way to write it?

I need to use the class extension to declare prototype in order to get rid of the warning of No previous prototype for function ..

Ryan
  • 10,041
  • 27
  • 91
  • 156

3 Answers3

11

Martin's answer is correct. However, I thought I'd elaborate just a bit, to explain why it is so.

Your function definition:

void myHandler(NSException * exception)
{
  // ...
}

defines a function that will be externally visible. In other (generalized, non-technical) words, a symbol will be created in the object file so that the linker can find it, which allows other files to call myHandler.

However, because it is supposed to be externally visible, other files are going to have to know what that function looks like. That's where the prototype comes into play. The warning is basically saying...

Hey, you have declared this function to be externally visible to other code, but I don't see a prototype that other code can use to know about the function.

So, you get a warning. It's a good warning to have on. It helps you remember to declare prototypes for functions that you want to export.

Now, as you discovered, you can declare a prototype, and the warning goes away. However, declaring the prototype just in the implementation file should be another warning to you. That personal warning should be:

Do you really want this function to have external visibility, or is it just called in this compilation unit? If the function is not going to have external visibility, then there is no need to export it in the symbol table, and there is no need for a prototype that other modules can include so they know about the function.

In that case, you can declare the function static as in Martin's response:

static void myHandler(NSException * exception)
{
  // ...
}

In this context, static tells the compiler something like:

Hey, compiler, create code for this function, and allow any code in this compilation unit to see the function, but do not give it external visibility. I don't want the function to be called by other modules.

In this case, even if other code declared the prototype, they would not see your function because it is "private" to the file in which it is defined.

Since it is being used only in the local file, there is no need for a prototype, so there is no need to warn you that you don't have one.

Now, just as a note... You don't need to put C functions in the @interface and @implementation sections of your code, as that does nothing. Those C functions are compiled with the exact same visibility and access whether they are inside the ObjC sections or not.

Finally, if you want, you can disable this warning in the Xcode build settings (but now that you understand the context of the warning, I suggest leaving it on).

Jody Hagins
  • 27,943
  • 6
  • 58
  • 87
  • 1
    Thanks for the detail explanation, but refer to my issue, `myHandler` is being registered in the `AppDelegate`, but uncaught exception can happen in any class, so I am not sure if using `static` is right for me... – Ryan Sep 01 '12 at 04:27
  • 2
    @Yoga: You can use `static` here without problem, because you pass the address of `myHandler` to `NSSetUncaughtExceptionHandler`. The visibility is only important for the linker. If any module has the address of `myHandler` it can call this function independent of the visibility. - Passing the address of a local static function to other functions is a common pattern, e.g. for callback functions etc. – Martin R Sep 01 '12 at 07:46
3

You do not get a warning about a missing prototype if you declare the function as static:

static void myHandler(NSException * exception)
{
  // ...
}
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
0

Yes, this is the correct way. I am just wondering why do you get the warning as I have the same thing without declaring it in an empty category and I don't get a warning... In addition you can also set signal handlers to catch SIGABRT, SIGILL and SIGBUS signals:

void signalHandler(int sig) {

}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    struct sigaction newSignalAction;
    memset(&newSignalAction, 0, sizeof(newSignalAction));
    newSignalAction.sa_handler = &signalHandler;
    sigaction(SIGABRT, &newSignalAction, NULL);
    sigaction(SIGILL, &newSignalAction, NULL);
    sigaction(SIGBUS, &newSignalAction, NULL);
    ...
}
graver
  • 15,183
  • 4
  • 46
  • 62