NSAutoreleasePool in NSOperation main? - cocoa

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.

Related

Design strategy to save in the background with MagicalRecord

Recently I started a new app requiring just one store (no document based app). For some time I was quite happy thinking I could finally get rid of throwing around the NSManagedObjectContext... until I wanted to save in the background :-(
Now I am confused about my own code. For example:
- (void)awakeFromInsert
{
[super awakeFromInsert];
[self resetCard];
self.creationDate = TODAY;
self.dictionary = [Dictionary activeDictionary];
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center postNotificationName:NOTE_NEWCARD object:self];
}
[Dictionary activeDictionary] is a NSManagedObject static function returning a pointer to a NSManagedObject created in the main thread. That will cause a cross/context error during the background save. Because my program always read from the same store, I thought I could avoid writing this:
[Dictionary activeDictionaryWithContext:...]
I suppose that with MagicalRecord, as long as I work always with the same backend is is possible to avoid passing the context pointer. Which function should I use to get that context?
[NSManagedObjectContext MR_defaultContext]
[NSManagedObjectContext MR_context]
[NSManagedObjectContext MR_contextForCurrentThread]
In the example the object sends itself within a notification, something almost granted to cause more conflicts.
In the case of the notification should I always send only the objectID?
It seems to me that my objects should issue side effect operations/notifications only if they are running in the main context. However some of those side operations change my object graph creating new instances of other entities.
Can I safely omit the two problematic function calls I have mentioned if I save with [MagicalRecord MR_saveAll] ?
Should I assume that the objects of the new background saving context will be an exact copy of the ones in my main thread without calling those extra functions?
Now I am having problems because I never expected awakeFromInsert to run several times for the same object of the same store. I was thinking about something like this:
- (void)awakeFromInsert
{
[super awakeFromInsert];
if ([self managedObjectContext] == [NSManagedObjectContext MR_defaultContext]) {
[self resetCard];
self.creationDate = TODAY;
self.dictionary = [Dictionary activeDictionary];
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center postNotificationName:NOTE_NEWCARD object:self];
}
}
That should make my awakeFromInsert code run only once, but not in the background saving context. I am concerned about losing information if I do so
While you can certainly send your object in a notification that way, I would recommend against that. Remember, even with the new parent-child contexts in CoreData, NSManagedObjects are NOT thread safe. If you create or import objects, you will need to save them prior to using them in another context.
MagicalRecord provides a relatively simple API for background saving:
[MagicalRecord saveInBackgroundWithBlock:^(NSManagedObjectContext *localContext){
MyEntity *newEntity = [MyEntity MR_createInContext:localContext];
//perform other entity operations here
}];
This block does all the work for you, without worrying about setting up the NSManagedObjectContext properly.
Another reason you should not pass NSManagedObjects across a notification is that you do not know what thread the notification will be received on. This can potentially lead to a crash, because, again, NSManagedObjects are NOT thread safe.
Another alternative to the notification approach you present is to add an observer to NSManagedObjectContextDidSaveNotification, and merge your changes on that notification. This will fire only after your objects are saved, and are safe for crossing contexts through either the parent-child relationship, or the persistent store (the old way).

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:

When NSDocument Asynchronous Saving is enabled, is there any point in calling -unblockUserInteraction in a simple -dataOfType:error:?

I have a straight-forward, Mac OS X, Cocoa, Document-based application which uses the new 10.7 Autosave, Versions and Asychronous Saving APIs. I am fully using the NSDocument APIs to get all of Apple's Document-based application features for free.
In order to support the new Lion Autosave/Versions/AsyncSaving, I have overridden the following methods in my NSDocument subclass like so:
#implementation MyDocument
...
+ (BOOL)autosavesInPlace { return YES; }
- (BOOL)canAsynchronouslyWriteToURL:(NSURL *)URL ofType:(NSString *)type forSaveOperation:(NSSaveOperationType)op {
return YES;
}
I have also overridden -dataOfType:error: to help implement saving the document's data to disk:
- (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outErr {
NSData *data = nil;
if ([typeName isEqualToString:MY_SUPPORTED_TYPE_NAME]) {
data = makeSnapshotCopyOfMyDocumentData(); // assume return value is autoreleased
} else if (outErr) {
*outErr = [NSError errorWithDomain:NSOSStatusErrorDomain code:unimpErr userInfo:nil];
}
// not sure this is doing much good, since i take no action after this.
[self unblockUserInteraction];
return data;
}
...
#end
See how I'm calling -unblockUserInteraction at the end there?
When supporting the new 10.7 AsyncSaving feature, Apple advises calling -unblockUserInteraction as early as possible (after making a snapshot copy of your document's data) in your -dataOfType:error: implementation. But Apple's example showed them doing much more work after calling -unblockUserInteraction.
However, considering I take no other action after this, I'm wondering if there's any point in calling -unblockUserInteraction there at all.
So my questions:
Considering I take no other action after it, is my call to -unblockUserInteraction doing any good?
Do the Apple Frameworks just call -unblockUserInteraction immediately after -dataOfType:error: returns anyway? Should I just leave it to them?
I just noticed a subtle wording difference between the NSDocument documentation and the comment in NSDocument.h:
Docs:
If saveToURL:ofType:forSaveOperation:completionHandler: is writing on
a non-main thread because
canAsynchronouslyWriteToURL:ofType:forSaveOperation: has returned YES,
but it is still blocking the main thread, this method unblocks the
main thread. Otherwise, it does nothing.
Header:
If -saveToURL:ofType:forSaveOperation:completionHandler: is writing on
a non-main thread because
-canAsynchronouslyWriteToURL:ofType:forSaveOperation: has returned YES, but is still blocking the main thread, unblock the main thread.
Otherwise, do nothing.
I assume the Header is more up to date.
I am working on an application that calls unblockUserInteraction after the last line that has to run on the main thread. (At least that's the way I understood it)
I think our code fits the scenario that Apple had in mind when designing the async saving part of NSDocument:
in fileWrapperOfType: we ...
create a QL preview for our file wrapper (that has to run on the
main thread) ...
unblockUserInteraction ...
... "long" running file saving task (involving compression)

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

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.

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.

Resources