问题
I had some code that created and added UIView
subviews via a queue created by [[NSOperationQueue alloc] init]
, and it led to strangely laggy behavior. Subviews were only added after abnormally long delays.
But then I switched to using [NSOperationQueue mainQueue]
for these parts, and responsiveness turned normal.
I would just like an explanation for the laggy behavior I saw using the first approach.
回答1:
From Apple doc
Threads and Your User Interface
If your application has a graphical user interface, it is recommended that you receive user-related events and initiate interface updatesfrom your application’s main thread. This approach helps avoid synchronization issues associated with handling user events and drawing window content. Some frameworks,such as Cocoa, generally require this behavior, but even for those that do not, keeping this behavior on the main thread hasthe advantage of simplifying the logic for managing your user interface. There are a few notable exceptions where it is advantageous to perform graphical operations from other threads. For example, the QuickTime API includes a number of operationsthat can be performed from secondary threads, including opening movie files, rendering movie files, compressing movie files, and importing and exporting images. Similarly, in Carbon and Cocoa you can use secondary threads to create and process images and perform other image-related calculations. Using secondary threads for these operations can greatly increase performance. If you are not sure about a particular graphical operation though, plan on doing it from your main thread
In addition, UI classes are not thread-safe according to the thread programming guide.
So, avoid to update the UI from a thread different from the main thread.
If you run an NSOperation
(within a queue) you could update your UI (for example after having download some data required for your app lifecycle) performing a method in the main thread like the following:
-(void)main {
// e.g the delegate could be the controller that has the view that you want to update
if (delegate) {
NSURL *url = [delegate urlForDownloadOperation:self];
if ( nil == url ) return;
self.downloadedImage = [[NSImage alloc] initWithContentsOfURL:url];
// e.g. rather than invoking processImage directly on the delegate, ensure that the method draw the image on the main thread
[delegate performSelectorOnMainThread:@selector(processImage:)
withObject:self waitUntilDone:YES];
}
}
Or you could send a notification to the component that need to update the UI like:
- (void)main {
NSURL *url = [delegate urlForDownloadOperation:self];
if ( nil == url ) return;
self.downloadedImage = [[NSImage alloc] initWithContentsOfURL:url];
// e.g. send a notificatio to inform some components that it is ready to update the UI with some content
[[NSNotificationCenter defaultCenter] postNotificationName:@"importData" object:self];
}
The component that needs to update the UI will register for that notification like
- (void)processImage:(NSNotification*)notification
{
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:@selector(processImage:) withObject:notification waitUntilDone:YES];
return;
}
// update the UI here, you are running on the main thread
}
来源:https://stackoverflow.com/questions/11421219/creating-ui-elements-on-nsoperationqueue-non-main-queues-causes-strange-behavior