Keep a reference to an NSThread around and message its objects? - cocoa

I am a bit uncertain on how to do this:
I start a "worker-thread" that runs for the duration of my apps "life".
[NSThread detachNewThreadSelector:#selector(updateModel) toTarget:self withObject:nil];
then
- (void) updateModel {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
BackgroundUpdate *update = [[BackgroundUpdate alloc] initWithTimerInterval:5];
[[NSRunLoop currentRunLoop] run]; //keeps it going 'forever'
[update release];
[pool release];
}
Now the thread "wakes" up every 5 seconds(initWithTimerInterval) to see if
there are any tasks it can do. All the tasks in the BackGroundUpdate Class are only time dependent for now. I would like to have a few that were "event dependent". e.g. I would like to call the Background Object from my main thread and tell it to "speedUp", "slowDown", "reset" or any method on the object.
To do this I guess I need something like performSelectorOnThread but how to get a reference to the NSthread and the Background Object?

Direct answer: Instead of +[NSThread detachNewThreadSelector:toTarget:withObject:], use [[NSThread alloc] initWithTarget:selector:object:]. Don't forget to call -start!
Other thoughts:
Consider using NSOperation/NSOperationQueue instead. Easier and more efficient for most worker thread uses.
Consider whether you really need to do that periodic check on a background thread. Could you just do it on the main run loop, and then throw off work onto other threads as needed? Threads aren't free.
Consider whether polling is the best implementation, too. Look into NSCondition and/or NSConditionLock for more efficient ways to wake up threads when something happens (like adding work to a queue), no polling necessary.

Related

NSThread with delegate for SimplePing

I'm currently using Apple's SimplePing in a Mac OS X application to ping a URL before transferring data, which works fine, but locks up my UI. I may not have looked in the right places, but how do I keep this from happening? I'm currently using the currentRunLoop, which I think is the problem, but I still want the user to be able to interact with the UI (e.g. cancel) during this action. How do I create a run loop for Simple Ping so my UI doesn't lock up?
SimplePing *localPing = [SimplePing simplePingWithHostName:pingHost];
[localPing setDelegate:self];
[localPing start];
do {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
} while (localPing != nil);
This reason your UI is locked while using SimplePing is because ping utility take certain time to complete. And it seems that you are doing this in your main thread, resulting locking the UI interface white ping task is in process.
So you can use following code
-(void) ping:(NSString *) ip
{
SimplePing *localPing = [SimplePing simplePingWithHostName:pingHost];
[localPing setDelegate:self];
[localPing start];
}
and then call ping function in new thread like
[NSThread detachNewThreadSelector:#selector(ping:) toTarget:self. withObject:#"IP Address"];

NSOperationQueue and Dispatch Queue as replacement of NSThread performing a repetitive task

I have an application in which I am repetitively calling a method in background. I implemented this by following below steps:
created a background thread,
called the appropriate method on the created thread,
called sleep method on the thread, and
again called the previously invoked method.
Below is the code which I used:
- (void) applicationDidFinishLaunching:(NSNotification *)notification
[NSApplication detachDrawingThread:#selector(refreshUserIdPassword) toTarget:self withObject:nil];
}
-(void)refreshUserIdPassword
{
[self getAllUserIdsPasswordsContinousely];
[NSThread sleepForTimeInterval:180];
[self refreshUserIdPassword];
}
I have read that NSThread is not the best way to perform background task, and there are other classes provided in cocoa, such as - NSOperationQueue and GCD, which should be preferred over NSThread to perform an asynchronous task. So I am trying to implement the above specified functionality using the alternative classes.
Problem is - though I am able to perform an asynchronous task using
these classes, I am unable to perform a repetitive task (as in my
case) using these classes.
Can someone throw some light on this and guide me towards the correct direction?
I think you'll get a stack overflow (no pun intended) using the code you've posted. -refreshUserIdPassword recurses infinitely...
How about using GCD?
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
dispatch_source_t timerSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
dispatch_source_set_timer(timerSource, dispatch_time(DISPATCH_TIME_NOW, 0), 180*NSEC_PER_SEC, 10*NSEC_PER_SEC);
dispatch_source_set_event_handler(timerSource, ^{
[self getAllUserIdsPasswordsContinuously];
});
dispatch_resume(timerSource);
self.timer = timerSource;
}
You're looking in the wrong place. As you say, NSOperationQueue isn't suited for this type of task. NSTimer is Cocoa's solution to this problem.
As the question also has a grand-central-dispatch tag:
If you need to run something in the background based on a regular interval, you could also use a dispatch_source timer.
Apple provides a very extensive example in the Concurrency Programing Guide.
If you don't need a background thread, you could use NSTimer (as paulbailey mentioned) or even more simple:
NSObject's performSelector:withObject:afterDelay:

Can I save to a managed object context from a completion block on an NSOperation?

My app uses Core Data and NSOperationQueue. In keeping with Apple's guidelines, I'm creating a separate managed object context for each queue. In my case this is pretty simple: I have one background queue that does all the heavy work, and another on the main thread that just reads the data.
It would seem to make sense for me to do something like this:
On the background queue, create an operation that does a bunch of work on the managed object context.
Add a completion block to that operation that saves the managed object context.
But I read in the NSOperation documentation:
The exact execution context for your completion block is not guaranteed but is typically a secondary thread. Therefore, you should not use this block to do any work that requires a very specific execution context. Instead, you should shunt that work to your application’s main thread or to the specific thread that is capable of doing it.
Of course, it's essential that this save be carried out from the same thread that 'owns' the managed object context. But I'm not always clear on whether 'thread' refers to operation queues or not. (It's sometimes used in more or less specific ways.)
Is my 'completion block' strategy workable?
Do a little trick to solve this issue anywhere in code:
Create moc:
moc = [[NSManagedObjectContext alloc] init];
[moc setUndoManager:nil];
[moc setPersistentStoreCoordinator:coordinator];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(importerDidSave:) name:NSManagedObjectContextDidSaveNotification object:self.moc];
don't forget remove observer :
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSManagedObjectContextDidSaveNotification object:self.moc];
[moc release];
[super dealloc];
}
and, finally, check before u will merge changes, if it main thread:
- (void)importerDidSave:(NSNotification *)saveNotification {
NSLog(#"MERGE in client controller");
if ([NSThread isMainThread]) {
[self.mainMoc mergeChangesFromContextDidSaveNotification:saveNotification];
} else {
[self performSelectorOnMainThread:#selector(importerDidSave:) withObject:saveNotification waitUntilDone:NO];
}
}

How to create a run loop that only listens to performSelector:onThread: and GUI events?

I want to create a separate thread that runs its own window. Frankly, the documentation does not make sense to me.
So I create an NSThread with a main function. I start the thread, create an NSAutoreleasePool, and run the run loop:
// Global:
BOOL shouldKeepRunning = YES;
- (void)threadMain {
NSAutoreleasePool *pool = [NSAutoreleasePool new];
// Load a nib file, set up its controllers etc.
while (shouldKeepRunning) {
NSAutoreleasePool *loopPool = [NSAutoreleasePool new];
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate distantFuture]];
[loopPool drain];
}
[pool drain];
}
But since there is no registered port or observer, runUntilDate: exits immediately and CPU utilization goes to 100%.
All thread communication is handled by calls to performSelector:onThread:withObject:waitUntilDone:. Clearly, I am not using the API correctly. So, what am I doing wrong?
Much of AppKit is not thread-safe and will not work properly (1) when manipulated outside the main thread. You will find only pain and misery trying to ignore this fact.
What are you really trying to do that requires a different thread for this window? Are you merely trying to keep a responsive UI? If so, there're much better ways of doing it. See NSOperation / NSOperationQueue (where "units of work" and "queues" are the focus, not "this window shall run on this thread, etc.").
I'd recommend restating your question with your specific goal detailed clearly.
(1) For some classes, it takes a lot of careful work. For others, they are quite firmly off limits.

NSAutoreleasePool in NSOperation main?

The documentation for +[NSThread detachNewThreadSelector:toTarget:withObject:] says:
For non garbage-collected applications, the method aSelector is responsible
for setting up an autorelease pool for the newly detached thread and freeing
that pool before it exits.
My question is, do I need to create my own NSAutoreleasePool in my override of the -[NSOperation main] method, or is the creation of the NSAutoreleasePool handled by NSOperation?
Good question, even Apple's own documents and example code aren't very clear on this. I believe I've found the answer though:
Because operations are Objective-C
objects, you should always create an
autorelease pool early in the
implementation of your task code. An
autorelease pool provides protection
against the leaking of Objective-C
objects that are autoreleased during
your task’s execution. Although there
might already be a pool in place by
the time your custom code is executed,
you should never rely on that behavior
and should always provide your own.
Basically, even though there may be a pool in place as David mentioned, you should still create your own.
Yes, you do. You're defining a self-contained piece of work which the NSOperationQueue will execute on "some" thread, so you're responsible for managing memory in that work piece.
You don't need to create your own NSAutoreleasePool in your main, the system does it for you. To see this, use the Xcode menu command Run > Show> Breakpoints to open the Breakpoints window and type in:
-[NSAutoreleasePool init]
Now run your program, and you'll see an autorelease pool getting created inside NSOperation.
See also, Apple's examples, for example, http://developer.apple.com/Cocoa/managingconcurrency.html which don't create their own autorelease pool.
Yes, you need to create an NSAutoreleasePool in your [NSOperation main] method, unless you are creating a "concurrent" (slightly unfortunate nomenclature) NSOperation subclass and your overridden [NSOperation start] method creates the NSAutoreleasePool before calling `[NSOperation main].
The NSOperation class documentation has a good description of all of this:
http://developer.apple.com/documentation/Cocoa/Reference/NSOperation_class/Reference/Reference.html.
yes, you need to.
- (void) main
{
NSAutoreleasePool *thePool = [[NSAutoreleasePool alloc] init];
//your code here
//more code
[thePool release];
}
if you don't create an autorelease pool, any convinience class-initializer (like [NSString stringWithFormat:]) will leak as these initializers return autoreleased objects.

Resources