96

First of all I am writing code for iphone. I need to be able to call a method on the main thread without using performSelectorOnMainThread. The reason that I don't want to use performSelectorOnMainThread is that it causes problem when I am trying to create a mock for unit testing.

[self performSelectorOnMainThread:@Selector(doSomething) withObject:nil];

The problem is that my mock knows how to call doSomething but it doesn't know how to call performSelectorOnMainThread.

So Any solution?

picciano
  • 22,341
  • 9
  • 69
  • 82
aryaxt
  • 76,198
  • 92
  • 293
  • 442

6 Answers6

300

Objective-C

dispatch_async(dispatch_get_main_queue(), ^{
    [self doSomething];
});

Swift

DispatchQueue.main.async {
    self.doSomething()
}

Legacy Swift

dispatch_async(dispatch_get_main_queue()) {
    self.doSomething()
}
pkamb
  • 33,281
  • 23
  • 160
  • 191
aryaxt
  • 76,198
  • 92
  • 293
  • 442
2

There's a saying in software that adding a layer of indirection will fix almost anything.

Have the doSomething method be an indirection shell that only does a performSelectorOnMainThread to call the really_doSomething method to do the actual Something work. Or, if you don't want to change your doSomething method, have the mock test unit call a doSomething_redirect_shell method to do something similar.

hotpaw2
  • 70,107
  • 14
  • 90
  • 153
1

Here is a better way to do this in Swift:

runThisInMainThread { () -> Void in
    // Run your code
    self.doSomething()
}

func runThisInMainThread(block: dispatch_block_t) {
    dispatch_async(dispatch_get_main_queue(), block)
}

Its included as a standard function in my repo, check it out: https://github.com/goktugyil/EZSwiftExtensions

Esqarrouth
  • 38,543
  • 21
  • 161
  • 168
  • This is not better in any way, you created a function that does nothing but calling another one. by the way that swift syntax can be further simplified "() -> Void in" is not needed – aryaxt Dec 06 '15 at 04:51
  • Its more human readable/writable. Yes autocomplete adds in "() -> Void in". Is there anyway to disable this autocomplete behavior in void>void closures? – Esqarrouth Dec 06 '15 at 05:00
  • the method name you have could be misleading, it sounds like the block would be executed right away on the main thread, but that's not true. dispatch_async on main queue adds the block of code to the next runloop, this important behavior is hidden behind the method that is called "runThisInMainThread – aryaxt Dec 06 '15 at 05:03
  • and I haven't found a way to configure Xcode to follow the simplified syntax :( – aryaxt Dec 06 '15 at 05:04
  • http://pastebin.com/gReE6Wyr I tried it, you are correct. It not only adds the code to the next runloop, it also delays stuff after it. Why is this happening? – Esqarrouth Dec 06 '15 at 05:15
  • 1
    this is the expected behavior, dispatch_async adds the code to the end of the queue. If you want it to get called right away you should do dispatch_sync instead. If you do dispatch_sync on a queue for the thread that you are already in it causes a thread lock. in your example the order of prints are "a", "c", "b". a and c are getting executed in 1 runloop because they are in the same scope. b is added to the end of the queue so it gets called sometimes later when the other existing items in the queue are completed – aryaxt Dec 06 '15 at 05:25
  • so what is a legit way to run something in the background without locking a thread? – Esqarrouth Dec 06 '15 at 09:11
  • 1
    @Esqarrouth - are you SURE your `dispatch_async` blocked code after the call to it? The entire point of using `async` rather than `sync` is to NOT block what follows. (Of course, the `block` of code WILL block anything else *on the main thread*, since the point of the asked-for code is to execute on the main thread. If you want to run background code, then you would ask for a different queue, not `dispatch_get_main_queue`.) – ToolmakerSteve Aug 23 '16 at 18:37
1

And now in Swift 3:

DispatchQueue.main.async{
   self.doSomething()
}
shim
  • 9,289
  • 12
  • 69
  • 108
RomOne
  • 2,065
  • 17
  • 29
0

The modern Swift solution is to use actors for safe multithreading, namely the MainActor.

@MainActor func setImage(thumbnailName: String) {
    myImageView.image = UIImage(image: thumbnailName)
}

I've outlined more actor-based main thread solutions here.

Pranav Kasetti
  • 8,770
  • 2
  • 50
  • 71
-4
// Draw Line
    func drawPath(from polyStr: String){
        DispatchQueue.main.async {
            let path = GMSPath(fromEncodedPath: polyStr)
            let polyline = GMSPolyline(path: path)
            polyline.strokeWidth = 3.0
            polyline.strokeColor = #colorLiteral(red: 0.05098039216, green: 0.5764705882, blue: 0.2784313725, alpha: 1)
            polyline.map = self.mapVu // Google MapView
        }

    }