Reputation: 2309
I have a Mac OS X server application that renders NSViews and returns them over an HTTP interface as images for use elsewhere. There's no visible UI, and the application creates detached NSViews without an NSWindow.
The application can receive many requests at once, but the layout and rendering process is synchronized around the main thread (using dispatch_sync in GCD) as Cocoa UI isn't thread safe, reducing the throughput to a single request at a time in that portion of the code.
Given that each request is entirely separate, with nothing shared between them, is there a way for a Cocoa application to effectively run multiple, entirely separate UI threads? Perhaps using multiple run loops?
I'd like to avoid having to run multiple processes, if possible.
Upvotes: 3
Views: 959
Reputation: 29946
It's hard to say with certainty that this will work for your specific needs (since your specific needs may have main-thread dependencies not called out in your question) but I don't see anything particularly controversial here. For instance, the following code works just fine without incident:
CGImageRef CreateImageFromView(NSView* view)
{
const CGSize contextSize = CGSizeMake(ceil(view.frame.size.width), ceil(view.frame.size.height));
const size_t width = contextSize.width;
const size_t height = contextSize.height;
const size_t bytesPerPixel = 32;
const size_t bitmapBytesPerRow = 64 * ((width * bytesPerPixel + 63) / 64 ); // Alignment
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, bitmapBytesPerRow, colorSpace, kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedLast);
CGColorSpaceRelease(colorSpace);
[view displayRectIgnoringOpacity: view.bounds inContext: [NSGraphicsContext graphicsContextWithGraphicsPort: context flipped: YES]];
CGImageRef image = CGBitmapContextCreateImage(context);
CGContextRelease(context);
return image;
}
- (IBAction)doStuff:(id)sender
{
static NSUInteger count = 0;
for (NSUInteger i =0; i < 100; ++i)
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSButton* button = [[[NSButton alloc] initWithFrame: NSMakeRect(0, 0, 200, 100)] autorelease];
button.title = [NSString stringWithFormat: @"Done Stuff %lu Times", (unsigned long)count++];
CGImageRef image = CreateImageFromView(button);
NSImage* nsImage = [[[NSImage alloc] initWithCGImage:image size: NSMakeSize(CGImageGetWidth(image), CGImageGetHeight(image))] autorelease];
CGImageRelease(image);
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = nsImage;
});
});
}
}
The key here is that everything be "private" to the background rendering task. It gets its own view, its own graphics context, etc. If you aren't sharing anything, this should be OK. Since you explicitly said, "Given that each request is entirely separate, with nothing shared between them", I suspect you've already satisfied this condition.
Try it out. Leave a comment if you run into trouble.
Upvotes: 1