# Debugging Tricks 1. In daily development we often encapsulate a feature module and expose a method for external use. But many times callers don't pass parameters according to the contract. We use assertions to catch this. However, in production if assertions are used the app will crash. Xcode provides a small feature to address this. `NS_BLOCK_ASSERTIONS`: prevents NSAssert from firing in Release builds; adding this single macro will filter out NSAssert. How: In Build Settings search for **Preprocessor Macros**, then add `NS_BLOCK_ASSERTIONS` under the Release configuration. ![](../assets/WX20180830-100631@2x.png) ### Breakpoints #### Categories Breakpoints include Normal Breakpoint, Exception Breakpoint, OpenGL ES Error Breakpoint, Symbolic Breakpoint, Test Failure Breakpoint, and Watchpoint. Use different breakpoint types according to the scenario. ### NSAssert and dispatch_once NSAssert is very common in development, especially when building SDKs and libraries. Assertions help catch problems during development and enforce expected behavior. NSAssert essentially raises an exception; when an exception occurs it triggers the C function `objc_exception_throw`. ![NSAssert assertion](../assets/2020-0311-NSAssert.png) Callback information example: ```Objective-c *** First throw call stack: ( 0 CoreFoundation 0x00007fff23c7127e __exceptionPreprocess + 350 1 libobjc.A.dylib 0x00007fff513fbb20 objc_exception_throw + 48 2 CoreFoundation 0x00007fff23c70ff8 +[NSException raise:format:arguments:] + 88 3 Foundation 0x00007fff256e9b51 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 191 4 TEst 0x0000000106edfeef -[AppDelegate application:didFinishLaunchingWithOptions:] + 287 5 UIKitCore 0x00007fff48089ad8 -[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 232 6 UIKitCore 0x00007fff4808b460 -[UIApplication _callInitializationDelegatesWithActions:forCanvas:payload:fromOriginatingProcess:] + 3980 7 UIKitCore 0x00007fff48090f05 -[UIApplication _runWithMainScene:transitionContext:completion:] + 1226 8 UIKitCore 0x00007fff477c57a6 -[_UISceneLifecycleMultiplexer completeApplicationLaunchWithFBSScene:transitionContext:] + 179 9 UIKitCore 0x00007fff4808d514 -[UIApplication _compellApplicationLaunchToCompleteUnconditionally] + 59 10 UIKitCore 0x00007fff4808d813 -[UIApplication _run] + 754 11 UIKitCore 0x00007fff48092d4d UIApplicationMain + 1621 12 TEst 0x0000000106ee0144 main + 116 13 libdyld.dylib 0x00007fff5227ec25 start + 1 ) libc++abi.dylib: terminating with uncaught exception of type NSException ``` You can clearly see when an assertion fails Xcode can precisely locate the line of code where NSAssert occurred—source is available. But in some scenarios exceptions cannot be traced to the real origin. For example, in large apps people often build Pods as static libraries to speed up builds; in those cases the exception cannot be precisely traced to the offending source line. If the assertion occurs inside a GCD block and the source context is missing, you also cannot pinpoint it. Example output: ```Objective-c *** First throw call stack: ( 0 CoreFoundation 0x00007fff23c7127e __exceptionPreprocess + 350 1 libobjc.A.dylib 0x00007fff513fbb20 objc_exception_throw + 48 2 CoreFoundation 0x00007fff23c70ff8 +[NSException raise:format:arguments:] + 88 3 Foundation 0x00007fff256e9b51 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 191 4 TEst 0x000000010f242e95 __57-[AppDelegate application:didFinishLaunchingWithOptions:]_block_invoke + 229 5 libdispatch.dylib 0x000000010f55fdd4 _dispatch_call_block_and_release + 12 6 libdispatch.dylib 0x000000010f560d48 _dispatch_client_callout + 8 7 libdispatch.dylib 0x000000010f56ede6 _dispatch_main_queue_callback_4CF + 1500 8 CoreFoundation 0x00007fff23bd4049 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9 9 CoreFoundation 0x00007fff23bceca9 __CFRunLoopRun + 2329 10 CoreFoundation 0x00007fff23bce066 CFRunLoopRunSpecific + 438 11 GraphicsServices 0x00007fff384c0bb0 GSEventRunModal + 65 12 UIKitCore 0x00007fff48092d4d UIApplicationMain + 1621 13 TEst 0x000000010f243134 main + 116 14 libdyld.dylib 0x00007fff5227ec25 start + 1 ) libc++abi.dylib: terminating with uncaught exception of type NSException ``` Add a Symbolic Breakpoint in Xcode for the symbol `objc_exception_throw`; then you can see the full stack in Xcode’s left-side navigator. ![objc_exception_throw](../assets/2020-03-11-objc_exception_throw.png) Check GCD’s `_dispatch_client_callout` to see if there’s anything unusual. You can find libdispatch source here: https://opensource.apple.com/tarballs/libdispatch/. ```Objective-c // object.m #undef _dispatch_client_callout void _dispatch_client_callout(void *ctxt, dispatch_function_t f) { @try { return f(ctxt); } @catch (...) { objc_terminate(); } } ``` You’ll find `_dispatch_client_callout` wraps the GCD block invocation in a try/catch that catches Objective-C exceptions and then calls `objc_terminate`, which breaks the call stack. There’s a scenario where a crash shows up inside `dispatch_once` because code inside `dispatch_once` threw an Objective-C exception. Big companies often see this early on; later they add code around assertions specifically to record an owner or provide better diagnostics.