125

Is there any way to check whether or not the current thread is the main thread in Objective-C?

I want to do something like this.

  - (void)someMethod
  {
    if (IS_THIS_MAIN_THREAD?) {
      NSLog(@"ok. this is main thread.");
    } else {
      NSLog(@"don't call this method from other thread!");
    }
  }
Daniele D.
  • 2,624
  • 3
  • 36
  • 42
fish potato
  • 5,319
  • 6
  • 27
  • 32

13 Answers13

173

Have a look at the NSThread API documentation.

There are methods like

- (BOOL)isMainThread

+ (BOOL)isMainThread

and + (NSThread *)mainThread

Louis CAD
  • 10,965
  • 2
  • 39
  • 58
rano
  • 5,616
  • 4
  • 40
  • 66
28

In Swift3

if Thread.isMainThread {
    print("Main Thread")
}
dimohamdy
  • 2,917
  • 30
  • 28
26

If you want a method to be executed on the main thread, you can:

- (void)someMethod
{
    dispatch_block_t block = ^{
        // Code for the method goes here
    };

    if ([NSThread isMainThread])
    {
        block();
    }
    else
    {
        dispatch_async(dispatch_get_main_queue(), block);
    }
}
boherna
  • 659
  • 8
  • 7
  • 5
    Answers to old questions can benefit from an explanation of how the new answer differs from existing answers. – Jason Aller Jan 15 '15 at 18:39
  • 1
    This is overkill, if some work *must* be done on the main thread, there's no use in checking whether you're on the main thread or not. Just do `NSOperationQueue.mainQueue().addOperationWithBlock { //your work here }` – Eric Apr 14 '15 at 14:04
  • 4
    @Eric I agree, but what if you want immediate execution of the method if already in the main thread? In your suggestion the method is always dispatched to be executed later via the main operation queue. – boherna Jan 26 '16 at 19:01
  • @boherna correct, that is something to watch out for. – Eric Jan 29 '16 at 23:11
  • @boherna Late comment, but the point you make in your comment would be stronger if you use `dispatch_sync()` instead of `dispatch_async()` in your example. – Caleb Aug 09 '18 at 15:42
  • @Caleb, my point is that if the method is already executed in the main thread there is no need to dispatch it using neither dispatch_sync or dispatch_async but simply executed straight away. – boherna Aug 10 '18 at 17:05
  • @boherna OK, but it's hard to think of why you'd want immediate execution if you're on the main thread but async execution if you're on some other thread. There are probably some cases where that makes sense, but none spring to mind. BTW, the pattern you show is not overkill; it's very common. It costs very little to check the thread, and avoiding the need to switch threads is a real benefit. – Caleb Aug 10 '18 at 17:42
15

If you want to know whether or not you're on the main thread, you can simply use the debugger. Set a breakpoint at the line you're interested in, and when your program reaches it, call this:

(lldb) thread info

This will display information about the thread you're on:

(lldb) thread info thread #1: tid = 0xe8ad0, 0x00000001083515a0 MyApp`MyApp.ViewController.sliderMoved (sender=0x00007fd221486340, self=0x00007fd22161c1a0)(ObjectiveC.UISlider) -> () + 112 at ViewController.swift:20, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1

If the value for queue is com.apple.main-thread, then you're on the main thread.

Eric
  • 16,003
  • 15
  • 87
  • 139
7

The following pattern will assure a method is executed on the main thread:

- (void)yourMethod {
    // make sure this runs on the main thread 
    if (![NSThread isMainThread]) {
        [self performSelectorOnMainThread:_cmd/*@selector(yourMethod)*/
                               withObject:nil
                            waitUntilDone:YES];
        return;
    }
    // put your code for yourMethod here
}
Albert Renshaw
  • 17,282
  • 18
  • 107
  • 195
T.J.
  • 3,942
  • 2
  • 32
  • 40
4
void ensureOnMainQueue(void (^block)(void)) {

    if ([[NSOperationQueue currentQueue] isEqual:[NSOperationQueue mainQueue]]) {

        block();

    } else {

        [[NSOperationQueue mainQueue] addOperationWithBlock:^{

            block();

        }];

    }

}

note that i check the operation queue, not the thread, as this is a more safer approach

Peter Lapisu
  • 19,915
  • 16
  • 123
  • 179
3

Two ways. From @rano's answer,

[[NSThread currentThread] isMainThread] ? NSLog(@"MAIN THREAD") : NSLog(@"NOT MAIN THREAD");

Also,

[[NSThread mainThread] isEqual:[NSThread currentThread]] ? NSLog(@"MAIN THREAD") : NSLog(@"NOT MAIN THREAD");
Tom Howard
  • 4,672
  • 2
  • 43
  • 48
2

For Monotouch / Xamarin iOS you can perform the check in this way:

if (NSThread.Current.IsMainThread)
{
    DoSomething();
}
else
{
    BeginInvokeOnMainThread(() => DoSomething());
}
Daniele D.
  • 2,624
  • 3
  • 36
  • 42
2

Details

  • Swift 5.1, Xcode 11.3.1

Solution 1. Detect any queue

Get current DispatchQueue?

Solution 2. Detect only main queue

import Foundation

extension DispatchQueue {

    private struct QueueReference { weak var queue: DispatchQueue? }

    private static let key: DispatchSpecificKey<QueueReference> = {
        let key = DispatchSpecificKey<QueueReference>()
        let queue = DispatchQueue.main
        queue.setSpecific(key: key, value: QueueReference(queue: queue))
        return key
    }()

    static var isRunningOnMainQueue: Bool { getSpecific(key: key)?.queue == .main }
}

Usage

if DispatchQueue.isRunningOnMainQueue { ... }

Sample

func test(queue: DispatchQueue) {
    queue.async {
        print("--------------------------------------------------------")
        print("queue label: \(queue.label)")
        print("is running on main queue: \(DispatchQueue.isRunningOnMainQueue)")
    }
}

test(queue: DispatchQueue.main)
sleep(1)
test(queue: DispatchQueue.global(qos: .background))
sleep(1)
test(queue: DispatchQueue.global(qos: .unspecified))

Result (log)

--------------------------------------------------------
queue label: com.apple.root.background-qos
is running on main queue: false
--------------------------------------------------------
queue label: com.apple.root.default-qos
is running on main queue: false
--------------------------------------------------------
queue label: com.apple.main-thread
is running on main queue: true
Vasily Bodnarchuk
  • 24,482
  • 9
  • 132
  • 127
1

Swift Version


if (NSThread.isMainThread()) {
    print("Main Thread")
}
Michael
  • 9,639
  • 3
  • 64
  • 69
1

let isOnMainQueue = (dispatch_queue_get_label(dispatch_get_main_queue()) == dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL))

check this answer from https://stackoverflow.com/a/34685535/1530581

Community
  • 1
  • 1
JerryZhou
  • 4,566
  • 3
  • 37
  • 60
0
Here is a way to detect what the current queue is
extension DispatchQueue {
    //Label of the current dispatch queue.
    static var currentQueueLabel: String { String(cString: __dispatch_queue_get_label(nil)) }

    /// Whether the current queue is a `NSBackgroundActivityScheduler` task.
    static var isCurrentQueueNSBackgroundActivitySchedulerQueue: Bool { currentQueueLabel.hasPrefix("com.apple.xpc.activity.") }

    /// Whether the current queue is a `Main` task.
    static var isCurrentQueueMainQueue: Bool { currentQueueLabel.hasPrefix("com.apple.main-thread") }
}
Mark Cao
  • 156
  • 7
-2

UPDATE: seems that is not correct solution, according to queue.h header as mentioned @demosten

The first thought was brought to me, when I was needed this functionality was the line:

dispatch_get_main_queue() == dispatch_get_current_queue();

And had looked to the accepted solution:

[NSThread isMainThread];

mine solution 2.5 times faster.

PS And yes, I'd checked, it works for all threads

Cœur
  • 37,241
  • 25
  • 195
  • 267
HotJard
  • 4,598
  • 2
  • 36
  • 36
  • 3
    Makes sense - your method bypasses the overhead of the obj-c runtime messaging system. Although if you're using this technique, I'd say it has a bad code smell, perhaps that of premature optimization. – ArtOfWarfare Aug 21 '13 at 22:29
  • 4
    dispatch_get_current_queue() is deprecated from iOs 6.0 – Durai Amuthan.H Oct 21 '13 at 09:09
  • 33
    You can read this in description of Apple's queue.h header where dispatch_get_current_queue() is defined: `When dispatch_get_current_queue() is called on the main thread, it may or may not return the same value as dispatch_get_main_queue(). Comparing the two is not a valid way to test whether code is executing on the main thread.` – demosten Jan 16 '14 at 16:19