NSAssert and dispatch_once

Believe everyone’s company code more or less has some assertions (for example NSAssert). A common assertion scenario: SDK developers to avoid SDK’s initialization methods and functional interfaces, will judge in functional interfaces if already initialized, otherwise trigger assertion. Of course also various other scenarios.

Exploring NSAssert

This article explores a phenomenon of Objective C’s assertion method NSAssert. This phenomenon is quite detailed, not easy to describe, let’s directly talk.

Assume has assertion below:

NSAssert(NO, @"should not call this");

When assertion code has source code, when triggered as below:

Complete callstack:

Since has source code, Xcode intelligently positions editor to NSAssert’s line. Also we know another information, NSAssert actually produces an Exception, Exception will trigger objc_exception_throw this C function.

Interaction with GCD

But if company promoted converting Pods to static libraries (to speed up compilation, generally teams with many people do this), NSAssert line has no source code, then likely Callstack will be as below:

Of course not only when no source code will be like above. If assertion in GCD’s some blocks, and context also has no source code, will also be like above. For example code below, will cause Xcode can’t breakpoint to code line.

Why like this? Look at detailed Callstack:

Carefully looked, here doesn’t have objc_exception_throw. Then we add symbol breakpoint to see.

No problem, this method is called. We look at objc_exception_throw’s implementation. https://opensource.apple.com/tarballs/objc4/ Download latest code. Find this method, as below.

After reading seems no ideas.

We look at GCD’s these two methods again,

Then find libdispatch’s code from here: https://opensource.apple.com/tarballs/libdispatch/

Now understand, _dispatch_client_callout catches OC Exception in GCD block, then directly objc_terminate. That is here, causes Callstack to break.

This problem temporarily ends here.

dispatch_once

For startup optimization, I wrote a launcher’s code, to avoid internal code executing multiple times, added a dispatch_once. Launcher executes various startup logic. However, for a period, always people said my code Crash.

Rough situation:

Left myRunner represents launcher. From figure above, indeed Crashed in my code.

But actual situation?

Because code in dispatch_once threw OC exception. Generally large companies early stage this situation often encountered, later stage generally will specifically develop some code for assertions to locate Owner, result due to dispatch_once all found me.

Simplest solution is, add an exception breakpoint. (That is symbol breakpoint objc_exception_throw)

Don’t underestimate this operation ha, I’ve seen many development classmates don’t know this operation (this might be small company iOS classmates’ first essential skill entering large companies).

Think about reason again, look at Callstack

Still exists _dispatch_client_callout . But slightly different is, dispatch_once’s this method is inline, written in header file

Xcode will try to find last line with matching code in Callstack, and position to this line, display to developer.

How to Solve

Reason clarified. Then how to bypass this problem? Currently seems not using GCD is fine.

For example: C++’s std::call_once.

Also for example use static variable’s built-in lock (this can write an article to explore)

More methods reference: https://stackoverflow.com/questions/8412630/how-to-execute-a-piece-of-code-only-once

If everyone likes, follow subscription account to encourage: