0

With Xcode 9, you can get analyzer warnings if the compiler is able to figure out that you're calling UIKit from a background thread.

Is there a way to get these for my own methods?

For example:

@interface MyObject

- (void)doThingOnMainThread NS_MAIN_THREAD_ONLY;
// where NS_MAIN_THREAD_ONLY is a thing I just made up, as far as I know

@end

Elsewhere:

- (void)otherMethod {
    MyObject *myObject = [MyObject sharedObject];
    dispatch_queue_t queue =
        dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{
        // I'd like a warning here, if possible!
        [myObject doThingOnMainThread];
    });
}
Steven Fisher
  • 44,462
  • 20
  • 138
  • 192
  • In Swift you’d often use `dispatchPrecondition(condition: .onQueue(.main))`. In Objective-C, you’re likely stuck doing `NSAssert` or `NSLog` based upon [`[NSThread isMainThread]`](https://developer.apple.com/documentation/foundation/nsthread/1408455-ismainthread). – Rob Oct 19 '17 at 00:47

1 Answers1

1

The closest I could get was:

#ifdef DEBUG
    #if TARGET_OS_SIMULATOR
        #define ENSURE_MAIN_THREAD if (![NSThread isMainThread]) { \
            printf("ERROR: UI USAGE ON BACKGROUND THREAD\n"); \
            __asm__ volatile("int3"); \
        }
    #else
        #define ENSURE_MAIN_THREAD if (![NSThread isMainThread]) { \
            printf("ERROR: UI USAGE ON BACKGROUND THREAD\n"); \
            __builtin_debugtrap(); \
        }
    #endif
#endif

Then I used it like:

- (void)testFunc {
    ENSURE_MAIN_THREAD

    printf("HERE");
}

- (void)testRun {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [self testFunc];
    });
}

When ran on a background thread, it creates a breakpoint or trap so you can see the stack in XCode and trace back to the source of the call.

It is run-time just like the Main-Thread-Checker by Apple (I was not able to get the static-analyzer to show a warning.. Only when I ran the code, Main-Thread-Checker would pause the app, and show error on main thread).

Only problem is that I couldn't find a way to "MARK" a function as main thread only.. but this is close..

Brandon
  • 22,723
  • 11
  • 93
  • 186
  • Yeah, I've got to the point where I start a lot of my methods with `NSAssert(NSThread.isMainThread, …);`. But a large number of them are nearly as simple as what I've posted and ought to be easy for clang to find. :) – Steven Fisher Oct 19 '17 at 17:50