I have a void method called 'startTask'.
On button click, I launch 'startTask' in the background as follows:
[self performSelectorInBackground:#selector(startTask) withObject:nil];
I want to cancel this process when a button is clicked, I cannot however figure out how to do this, can anyone please help?
Thanks in advance
I would use NSOperation and NSOperationQueue. These allow for canceling. Threads require much more work for them to get the signal.
Edit-- for an example
Create a simple class that subclasses NSOperation
In your implementation implement a -(void)main(){
} method. This method is called when your operation is executed.
In the file that you want to launch it from create a member variable of NSOperationQueue.
When you want to launch your task create the operation and add it to the queue. it will operate asynchronously and you can cancel it.
Here is a question for iOS but I believe it should be the same for Cocoa NSOperation on the iPhone
Related
I'm a windows developer and I'm having a hard time understanding the right way to run code in the
NSApplication's main thread.
Most of my code is running in a cvdisplaylink thread (it's an opengl app)
THe problem is that I can't call things like NSOpenPanel from it - it crashes the app and warns about only running stuff like this from the main thread.
It's fine, but the main thread is completely opaque as far as I understand, and I can only make it do things with events. The NSApp sendAction method sounded promising - because I could explicitly specify which method to call. But it didn't 'send' any thing, it just called this method directly from the same thread.
Am I understanding this right? Do I have to push some sort of a custom event (perhaps NSEventTypeApplicationDefined) to the main thread queue for this to work properly?
And if so, how to I respond to custom events like that?
Like this:
dispatch_async(dispatch_get_main_queue(), ^{
// do whatever
});
If what you want to do is to call a method of an Obj C object, the old school Cocoa way (which still works) is to use performSelectorOnMainThread:withObject:waitUntilDone:
E.g. to hide a window by calling its "orderOut:" method you would do this.
[theWindow performSelectorOnMainThread:#selector(orderOut:)
withObject:nil
waitUntilDone:NO];
I have 2 windows in my cocoa app. Main window opens a sub window. On click of OK on the sub window, I invoke a deligate on main form which will tell that OK button is clicked on the sub window.
Now, I need to run a long running process on the main window "in the background" so that the window will not become unresponsive. I also have progress bar which should show progress of this long running process.
Please let me know, what is the best way to achieve this.
You should start with Apple's Concurrency Programming Guide.
Specially the section about NSOperationQueue.
You can use Grand Central Dispatch for this. First you create a dispatch queue which will contain operations you want to perform on another thread. Each operation is represented as a Objective-C block (closure).
First you get a queue to put the task you want to run on another thread.
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
Then you place a block representing the work you want to do on this queue:
dispatch_async(queue, ^{
// this happens on separate thread
NSImage *image = produceImageFromSomeReallySlowOperation()
dispatch_async(dispatch_get_main_queue(), ^{
// this happens on main thread
[myView setImage:image];
});
});
The dispatch_get_main_queue() function returns the queue which is used for operations on the main thread (where the GUI is executed). This means that [myView setImage:image] will be executed on the main thread. You can place your update of the progress bar here. Just dispatch on the main queue to update the progress at every point in your algorithm where it makes sense to do so.
All of this can also be performed with NSOperation which provides a higher level Objective-C interface to the same functionality. But using GCD directly is sometimes easier. It depends on what you want to do.
I'm having serious problem whenever I pause the game, since most of my performSelector: has delay, so it will not execute immediately, but thing goes bad if I'm pausing the game then the performSelector is still calling... How should I overcome this?
I know there is one function under NSObject to cancel all the requests but that's not really what i'm looking for because I want the method to continue execute if the player resume the game.
According to cocos2d Best Practices you should not use performSelector:afterDelay directly but use cocos2d scheduler instead:
Try NOT to use Cocoa’s NSTimer. Instead use cocos2d’s own scheduler.
If you use cocos2d scheduler, you will have:
automatic pause/resume.
when the CCLayer (CCScene, CCSprite, CCNode) enters the stage the timer will be automatically activated, and when it leaves the stage it will be automatically deactivated.
Your target/selector will be called with a delta time
Here's how you schedule your method (assuming self is a CCNode):
[self schedule: #selector(tick2:) interval:0.5];
Don't forget to unschedule it in -tick2: if you want to call it once.
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.
I'm fairly new to Objective-C and I can't figure out how to wait in a non-blocking manner. I have an object that is being populated asynchronously and I need to wait on it before I can proceed in another method. Right now I am using the sleep function, but this blocks the whole app and myObject never gets loaded.
while (!myObject)
{
sleep(1);
}
return myObject;
EDIT: This code snippet is from a method that may be called before myObject has been loaded. In this case I actually do want to block in this method, but my code blocks everything including myObject from being loaded.
This little peach worked for me (in-order to delay for 20 seconds)....
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 20.0, false);
If you can, give the class a myObjectLoaded: method to be called when the object in question is loaded. Otherwise, the the most idiomatic equivalent of what you wrote above is to create a timer that keeps checking for myObject and does something once it finds it.
If you really needed to do it in the middle of a method for some reason, you'd have to create a loop that keeps running the runloop. It's the lack of a runloop that causes your app to block.
NSNotification should solve the problem.
http://developer.apple.com/documentation/Cocoa/Conceptual/Notifications/Introduction/introNotifications.html
Instead of waiting on on object, have this object register for notifications coming from your other object (say Publisher) which populates the data asynchronously. One this Publisher object finishes, have it post an NSNotification, which will then be automatically picked up by your waiting object. This would eliminate waiting too.
Sounds like you're looking for the observer pattern. Apple calls it "notification".
Assuming you have some background NSThread performing this population operation, you might like The NSObject method performSelector:onThread:withObject:waitUntilDone
That's because you are stopping the main thread waiting for your object to be loaded. Don't do that, because the main thread is the thread that drives the UI, and waits for user input. If you block the main thread you block the application user interface.
If you want the main thread to do something when the object is loaded, then create a method myObjectLoaded: and call from your loading threads:
[myObjectController performSelectorOnMainThread:#selector(myObjectLoaded:)
withObject:myObject
waitUntilDone:NO];
where myObjectController can be any object even myObject itself.