4

I have a drawRect method that is rather slow (100-200ms). To save time, I need to cache the results. I am doing the actual caching like this:

// some code to check if caching would be desirable goes here.  If it is desirable, then
UIGraphicsBeginImageContext(viewSize);
CGContextRef c = UIGraphicsGetCurrentContext();
[view.layer renderInContext: c];
UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
[self.cachedImageArray addObject:image];
UIGraphicsEndImageContext();

The caching itself can take up to 40ms. This is still easily worth it. But the caching has to wait until everything is rendered, or it will come out wrong. Additionally, the caching is a low-priority task. Once everything is displayed, it is possible that other things will still be going on, and if so, the caching can wait. But since it uses UIKit, it has to be on the main thread.

Rather than putting in some arbitrary delay, is there a bulletproof way to wait like this?

Pang
  • 9,564
  • 146
  • 81
  • 122
William Jockusch
  • 26,513
  • 49
  • 182
  • 323
  • 2
    Why must the caching task be on the main thread? Couldn't you just send (or display) the results on the main thread (via `performSelectorOnMainThread` or GCD's magic `dispatch_async(dispatch_get_main_queue(), ^{ });` and spin off the caching on a separate thread near the end of your `drawRect`? – Michael Dautermann Jan 03 '12 at 22:01
  • 3
    Drawing to a graphics context is thread safe as of iOS 4 and doesn't have to be performed on the main thread. You could kick this whole thing to a background thread using `dispatch_async()`. – Mark Adams Jan 03 '12 at 22:32

1 Answers1

1

The caching itself doesn't have to be done on the main thread. You can get a copy/reference of the image context or bitmap data, and launch it using an NSThread only when the rendering is done. Example:

- (void) drawRect:(CGRect)rect {
    do_rendering_here();
    // when rendering completed:
    NSThread *t = [[NSThread alloc] initWithTarget:self selector:@selector(doCaching:) object:c];
    [t start];
    [t release];
}

- (void) doCaching:(CGContextRef)ctx {
    // do whatever kind of caching is needed
}
  • USing `NSThread is still allowed but Apple recommends against it for most tasks. They would much rather that you use Grand Central Dispatch and use @MichaelDautermann's solution from his comment on the question. Check out Apple's concurrency guide: http://developer.apple.com/library/ios/#documentation/General/Conceptual/ConcurrencyProgrammingGuide/Introduction/Introduction.html – Jack Lawrence Jan 03 '12 at 22:45
  • Sorry, I'm not avare of these new APIs, since I don't own any iOS5-capable devices and I have an old toolchain built from 3.1.3 headers... –  Jan 03 '12 at 22:47
  • 1
    @H2CO3 - GCD came in with iOS 4.0, so it's been supported for a while now. Also, there's no need to do a convoluted allocation of an NSThread instance to perform something on a background thread. Simply use NSObject's `-performSelectorInBackground:withObject:`. – Brad Larson Jan 05 '12 at 19:47
  • Thanks, I may have expressed myself wrong; I know about these, I just can't use them because I'm not used to them. –  Jan 05 '12 at 20:01