Correct handling of asynchronous NSObject - cocoa

I have a situation similar to this:
Objective-C: How to use memory managment properly for asynchronous methods
I have an object that asynchronously downloads & parses an xml doc. It then has a delegate method that transfers the data it retrieved to the caller.
My 2 questions are:
When do I release the data retrieving object? The link I posted above gives 2 answers, one says release in the delegate and one says release immediately, which is correct (or which is better if both answers are correct)
My second question is, what is the best way to transfer the retrieved data to the caller? At the moment I have
self.imagesDataSource = [articleImagesParserObject.returnedArray copy];
I used copy because, as far as I understand, that makes the mutable array immutable. Is that correct?

I'm going to pick you up on a couple of things.. It might start the ball rolling :)
You say
It then has a delegate method that
transfers the data it retrieved to the
caller
-- EDIT --
You mean that you send a message to the NSURLConnection's delegate. Yes, it is just semantics, but it is clearer.
You say
The link I posted above gives 2
answers, one says release in the
delegate and one says release
immediately
The post you link to says that if you launch your secondary thread with NSThread +detachNewThreadSelector:toTarget:withObject: the thread will retain your object, so if you have finished with it, you can release it, as is normal practise. You are not doing this.
The second suggested method is to supply a method to callback when your background operation has completed. As you are using NSURLConnection and it already provides you with callback hooks, and in fact you are using them to return your downloaded data, this seems like the way to go.
Copying a mutable array does give you an immutable copy, which you own - so it should be self.imagesDataSource = [[articleImagesParserObject.returnedArray copy] autorelease] unless imagesDataSource doesn't retain - which would be irregular.

Related

Some data losts when I use NSURLConnection to get data asynchronously

I deal with the data and do some UI working according to the data in the method
-(void)connection:didReiveiceData(I use delegate as callback), and I find that UI working is always not finished completely.Maybe when the data is received, the UI threading is still busy, so some data losts.You may suggest me to deal with data in -(void)connectionFinishLoading:,it will cause other problems.
You've correctly suggested you need to process the received data in connectionDidFinishLoading:.
Before that, you need to collect all the receivedData (eg into an NSMutableData instance). Append the received data each time didReceiveData: is called (it may be called multiple times before it finishes).
the reason why some data lost is all about the method –rangeOfData:options:range:
I use it wrong.BTW, I think this method is very weird,the option accept only one of two value, NSDataSearchBackwards and NSDataSearchAnchored.why no "NSDataSearchForewards" or something like that?

What would be a thread-safe way to save a context form Core Data?

I have a NSOperationQueue set to NSOperationQueueDefaultMaxConcurrentOperationCount. It is filled with NSOperation objects (nothing weird so far). I subclassed the NSOperation to do some background tasks.
Download data from the internet.
Parse the data so I can read it.
Create a NSManagedObject:
[NSEntityDescription insertNewObjectForEntityForName:#"Channel" inManagedObjectContext:context];
Save it with the context.
[managedObjectContext save:&error]
I like this all to happen in the background so the UI won't get blocked. I read this article about concurrency with core data, and as far as I understood it. The best way would be to create a new NSManagedObjectContext in every NSOperation, but share the same persistent store coordinator.
That's easily done, however, when it comes to saving the context it says in the documentation it is error prone to do so. So my question is the following:
If I have different operations running in the NSOperationQueue, could those operations interfere with each other while saving the managed object context? Or does it wait to execute the following operation till the saving has been complete?
Can I safely save the context in a NSOperation? Or is it really bad practice?
I hope someone can shine a light on this matter, because I am really stuck at the moment.
What you need to do is the following:
Create a managed object context for each NSOperation. Create this new context on the main method, because this is when it's executing on the right thread.
Assign the context persistent store coordinator.
Create an observer to receive the NSManagedObjectContextDidSaveNotification. This is the only way the main context will know at the time the changes were made on the NSOperation's context. Make sure the merge call is made on the thread/block the merging context lives in. If you are merging with the main thread's context, call the mergeChangesFromContextDidSaveNotification: method on the main thread with the notification from the NSOperation's context.
Also, ask yourself if you really want to have all these operations working concurrently. Per the documentation:
The default maximum number of operations is determined dynamically by the NSOperationQueue object based on current system conditions.
You do not have control over how many NSOperations will be operating at the same time. If this is not what you want, you might be better if you just go with a serial NSOperationQueue (maxConcurrentOperation=1), considering the fact that you are going to be locking the database to do the save, and also because you have networking being done as well.
You can safely save inside the NSOperation's main method, if you take the precautions mentioned above.

NSManagedObject subclass woes

Hey guys, I've got a subclass of an NSManagedObject. In awakeFromInsert and awakeFromFetch I'm calling an initialization method which, among other things, starts an NSTimer.
Now i need a place to invalidate the timer. However, dealloc, finalize, didTurnIntoFault, prepareForDeletion and willTurnIntoFault aren't getting called.
According to the documentation, these methods should all get called when the object is cleared from memory. None of them are, however all the data is saved in the persistent store. I'm puzzled as to why or how.
Is there anything i could be doing that could cause these methods to not get called during the objects life cycle?
Core data controls the lifetime of NSManagedObjects. It's not going to flush an object from memory by itself unless you ask it to. Looking at the documentation, there appear to be two ways:
sending refresh:mergeChanges: to the MOC causes the object to turn into a fault.
sending reset to the MOC causes it to reset itself as if it has just been created.
However, any of the above requires explicit action on your part, so you might as well add a method to the object to invalidate its timer and invoke that.
In fact, your problem probably indicates a design issue. An NSTimer is essentially a user interface event. It should probably be controlled by your MVC controller which sends a message to the model object (the NSManagedObject) to do the action.

Is NSPasteboard thread-safe?

Is it safe to write data to an NSPasteboard object from a background thread? I can't seem to find a definitive answer anywhere. I think the assumption is that the data will be written to the pasteboard before the drag begins.
Background:
I have an application that is fetching data from Evernote. When the application first loads, it gets the meta data for each note, but not the note content. The note stubs are then listed in an outline view. When the user starts to drag a note, the notes are passed to the background thread that handles getting the note content from Evernote. Having the main thread block until the data is gotten results in a significant delay and a poor user experience, so I have the [outlineView:writeItems:toPasteboard:] function return YES while the background thread processes the data and invokes the main thread to write the data to the pasteboard object. If the note content gets transferred before the user drops the note somewhere, everything works perfectly. If the user drops the note somewhere before the data has been processed... well, everything blocks forever. Is it safe to just have the background thread write the data to the pasteboard?
You can promise the data to the pasteboard without actually having the data yet.
One way is to declare the type of the data on the pasteboard, passing yourself as the pasteboard's owner, and respond to a pasteboard:provideDataForType: message by providing the data (blocking, if necessary, until the data either arrives or fails to arrive). This means that you'll need to remember which objects were copied (by stashing them in an array, for example) so you can extract/generate the data from them when the promise comes due.
The other way, referenced in Harald Scheirich's answer, is to make your model objects conform to the NSPasteboardWriting protocol, ideally in a category (to separate interface-independent logic from Mac-specific logic). This is much cleaner than the old way, but requires Mac OS X 10.6 and later.
With NSPasteboardWriting, you'll implement promises by having the model objects' writingOptionsForType:pasteboard: method return the NSPasteboardWritingPromised option. Their pasteboardPropertyListForType: method will return the data, or at least try to—as before, this method should block until the data either arrives or fails to arrive.
Oh, and to answer the question in the title (“Is NSPasteboard thread-safe?”): There's no specific answer in the Thread Safety Summary, but there is this general statement:
… mutable objects are generally not thread-safe. To use mutable objects in a threaded application, the application must synchronize appropriately.
I would consider an NSPasteboard to be a mutable object, so no.
In practice, this isn't a problem: You typically only work with NSPasteboard in response to an action message (e.g., copy:), a drag, or a service invocation, and those all only happen on the main thread anyway. For them to happen on a secondary thread, you would have to explicitly send such messages yourself from code running on a secondary thread, in which case you are already doing something very wrong.
Conjecture:
I think your problem has nothing to do with threading but the fact that by returning YES you told the system that the data is ready. have you tried moving your data into a custom class supporting NSPasteboardWriting and NSPasteboardReading? this way the accessor to your data can block until the data is ready.
See the Pasteboard Documentation

Cocoa Controllers - best practice for notifying on completion, for disposal?

I have an ObjC controller object.
After alloc/init of the object, I get it to do a job asynchronously:
[myObject doSomeThingsOverTime];
The method sets things in motion, and then returns immediately.
Question: what is the best way to be notified of the result in the future, so that I can release myObject and react to the work having been completed? Should I observe/post notifications? Or supply the object with a method to callback? Or other?
I'm personally a fan of the notification center route. It allows for more than one observer (may or may not be relevant to you).
The delegate route is also valid, and is used quite frequently in the frameworks.
I think it comes down to personal preference. If it's your own code, you should go for what's most readable and simple for your particular situation. I don't think one is more or less valid than the other.
Have you looked at the NSOperation and NSOperationQueue classes? You can observe the isFinished of an NSOperation object so you will get notified when it is completed.

Resources