1

I'm trying to understand how things work in regards to concurrent programming and calling setNeedsDisplay. I basically have Three objects.

Main View - container with different UIView objects, the main one being a UIScrollView
Small Map View - a small UIView that draws a miniature version of one of the other UIView items on screem
Processor - a delegate of the Main View that calculates what's on screen and calls the Main View back with what's in view.  

So a simple use case of what's going on is the user touches the ScrollView and then the processor updates what's in view of the scrollView (like calculating coordinates, center point, etc) It does this using blocks and does it asynchronously. This then posts a notification to the MainView object.

When the MainView receives the notification, it just calls

[smallMap setNeedsDisplay];  // example 1

I put some logs around this call, and I do see it gets called right away. However, the drawRect: of this function does not get called right away. It gets called after 2 seconds or so.

I remember reading that setNeedsDisplay just marks the view for redraw to happen on the next event of the run loop.

But if I add this code instead:

// example 2
dispatch_async(dispatch_get_main_queue(), ^{
    [smallMap setNeedsDisplay];        
    });

My view gets redrawn right away.

I guess I'm confused as to why I have to ask for the main event loop to call setNeedsDisplay to immediately redraw something. Like in example 1, by me calling setNeedsDisplay, is that done in the background or something and that's why it doesn't get redrawn right away? I'm trying to understand the difference in what's going on behind the scenes so I know what to look for the in future. Like should I have all my calls that need to be immediately redrawn in something similar to the example 2 block? Or is it because I'm processing my data asynchronously that I need to then ask for the main queue? Thanks!

Crystal
  • 28,460
  • 62
  • 219
  • 393

2 Answers2

2

setNeedsDisplay is a UIKIT API call and has to be called from the main thread of the application, also known as the UI thread. That's why calling it in a background thread doesn't have any immediate effect and scheduling it on the main queue has immediate effects.

See this related question https://stackoverflow.com/a/6988115/172690 for a more detailed answer.

Community
  • 1
  • 1
Grzegorz Adam Hankiewicz
  • 7,349
  • 1
  • 36
  • 78
  • That is true but does not apply to this question. He is using dispatch_async but dispatches to the main queue, which should work just fine. – Jiri Jun 04 '12 at 21:18
  • 1
    Errr, really? What I'm reading is that his code (see example 1) doesn't work unless he dispatches it on the main thread (example 2). You state using dispatch_async with main queue is fine, which I agree. The question is why, and I guess I failed to answer that if I'm confusing others as well. Sorry. – Grzegorz Adam Hankiewicz Jun 05 '12 at 11:31
  • The way I understood the question was that example 1 was called on the main queue and example 2 on another queue. Then the UIKit call would in both cases run on the main queue which would be OK. I might have misunderstood, sorry. – Jiri Jun 05 '12 at 11:42
2

My guess is 1 of 2 things:

Your code that is running on a separate thread is calling your MainView methods from the separate thread instead of using performSelectorOnMainThread or a GCD call that invokes the code on the main thread. Thus your call to setNeedsDisplay is actually taking place on a background thread, which is a no-no, as the other poster said.

The second possibility is that your MainView code is running on the main thread, but it gets busy doing time-consuming processing, or waiting for a synchronous call to another thread to finish, and doesn't service the event loop.

You can rule out the first possibility by setting a breakpoint on your call to setNeedsDisplay and looking at the call trace in the debugger to see what thread it's running from.

Figuring out the second possibility will take a little more work. You might need to delve into instruments.

Duncan C
  • 128,072
  • 22
  • 173
  • 272