If notification fired by child thread, then post it by NSNotificationQueue (of course invoked in child thread) but working handler in main thread, the handler won't be called. But if the notification fired in main thread, it works as expected.
Does Apple mean the notification sender and handler must be in the same thread? If Yes, NSNotificationQueue seems not be so helpful. Anyone who knows it please share and thanks in advance.
[[NSNotificationQueue defaultQueue] enqueueNotification:notification postingStyle:NSPostASAP coalesceMask:(NSNotificationCoalescingOnName) forModes:nil];
They will be in the same thread. If you need multithreading look atNSOperationQueue
Edited:
From NSNotificationQueue class reference: Every thread has a default notification queue, which is associated with the default notification center for the task. You can create your own notification queues and have multiple queues per center and thread.
And:
+ (id)defaultQueue
Returns the default notification queue for the current thread. This notification queue uses the default notification center.
Related
I have a notification which can be posted on a background thread. This notification eventually leads to calling setTitle:forSegmentAtIndex:, which is UISegmentedControl, part of the UIKit.
Should it be assumed that I need to wrap this setTitle:forSegmentAtIndex: call with a async call to main thread, or will some lower lying Cocoa code automatically dispatch anything like setTitle:forSegmentAtIndex: to the main thread?
Always dispatch code that modifies a UI control to the main queue. Always.
I am trying to do window management, but I need the code running on a separate thread.
The first thing I need to do is subscribe to app notifications like this:
NSNotificationCenter *nc = [[NSWorkspace sharedWorkspace] notificationCenter];
NSString *not = NSWorkspaceDidLaunchApplicationNotification;
[nc addObserver:self selector:#selector(appLaunched:) name:not object:nil];
But if I simply call addObserver on another thread, will the notifications be delivered there instead?
Apple has this reference, but it seems overcomplicated:
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Notifications/Articles/Threading.html
If the answer to the first question is no, then why couldn't I just forward the message like this?
NSThread *other;
- (void)appLaunched:(NSNotification*)not {
if([NSThread currentThread] != otherThread)
[self performSelector:#selector(appLaunched:) onThread:other withObject:not waitUntilDone:NO];
else
// do respond to notification
}
The second thing I need to do is add an AXObserver to a runloop on the other thread.
If I call CFRunLoopGetCurrent() from another thread, will a run loop automatically be created like calling [NSRunLoop currentRunLoop] or do a I have to create one?
Observers which are registered using -addObserver:selector:name:object: receive the notification on the thread where it's posted, not where they registered. There's also -addObserverForName:object:queue:usingBlock:, which causes the notification to be received on the specified queue, but that doesn't let you make it arrive on a specified background thread. (Only the main queue is tied to a thread.)
You can shunt a notification to another thread in the manner you suggest. However, the original receiving thread has to be idling to receive the notification in the first place. Or, rather, it has to be idling in order to allow NSWorkspace to detect the condition which causes it to post the notification.
All threads create a runloop for themselves as soon as it's requested. It's basically impossible to observe a thread not having a runloop, so you might as well just act as though the runloop is created when the thread is created.
All of that said, your original goal – "I am trying to do window management, but I need the code running on a separate thread" – is problematic. Many GUI manipulations are not legal from background threads. Also, why do you "need" to do it from a background thread? And if your main thread is not free, you're not going to receive the workspace notifications in the first place.
I have issue:
One thread raises event that is listened from main thread.
Main thread in eventHandler raises
message dialog like this:
MessageDialog md = new MessageDialog (parent_window, flags, msgtype, btntype, msg);
md.Run ();
md.Destroy();
However application crashes on md.Run(); (if i raise messageDialog using gtk.application.invoke() there is no crash but there is also no modality in dialog.)
GTK objects can only be accessed safely from the main thread. If you subscribe to an event from the main thread, that does not mean that the event will be raised from the main thread. Events are raised on the thread that raises them.
What you need to do is to use Application.Invoke to safely queue a delegate on the main thread's mainloop, and access the GUI objects from that delegate. You can do this in the event handler, or you could even use a delegate to dispatch the event onto the main thread, so that event handlers would not have to do so - it's just a question of how you want to define your internal API.
Note that although Application.Invoke runs the delegate asynchronously, this does not affect the modality of the dialog. The thing that affects the modality of the dialog is whether you include the DialogFlags.Modal flag in the flags parameters.
I have a window that displays some data in an NSTableView. This data is loaded in the background. The data-loading-thread is started in the windowDidLoad: method. If the window is closed before loading has finished, the background thread should be cancelled. I do this by signalling the thread in the windowWillClose: delegate method and waiting for the background thread to finish.
Now this all works perfectly. But I have one problem: How can I update the data in the table view? I have tried calling reloadData via performSelectorOnMainThread: but this leads to a race condition: The reloadData call is sometimes queued on the main thread after the window close command, and will execute after the window has closed, and everything goes up in flames.
What's the best way to control and communicate with a background thread?
Well, you know, this is exactly what makes the use of threading complex: you always face synchronization issues.
What I suggest is, instead of calling [tableView reloadData] from your thread, simply signal your controller (by calling a method controllerShouldReloadTable) and let your controller do the check if windowWillClose has been called or not. There might be a chance that your controller has been also released by the time controllerShouldReloadTable, and to fix this you will definitely need to retain the controller from the secondary thread.
On a side note, I would cancel the thread in viewDidUnload (for symmetry).
Most important: I would use asynchronous calls and a delegate class so that the whole multithreading issue is solved at its root.
EDIT: Sending asynchronously a request will not block the sending thread waiting for the response. Instead, asynchronous send (for NSURLConnection is called start) immediately returns (so, no blocking) and when the response is received, a delegate method will be called (i.e., connectionDidFinishLoading:) so that you can updated the model and the UI. Take a look at NSURLConnection docs, but as usual, I strongly suggest using [ASIHTTPRequest][2], which has many advantages.
When I start a 2nd background thread and pause the main thread, will my First Responder still be in action? For example I have an overwriting method called -flagsChanged and was wondering if it would still be active if the main thread is offline.
Thanks,
Kevin
Don’t pause the main thread since the main thread is responsible for handling events and your application UI will become irresponsive. If the main thread is paused, it won’t handle events, hence it won’t dispatch key events to the first responder.
If you think you need to pause the main thread, you probably need to redesign your program so that the behaviour that requires sleeping (if it does require sleeping) is offset to a secondary thread. If you need to update the user interface from a secondary thread, you should use -performSelectorOnMainThread:withObject:waitUntilDone:.