I make an app that accesses some data on the first launch and then displays it. I've been downloading this data this way:
NSData *data = [NSData dataWithContentsOfURL:url];
Is this bad? Right now I've set the method that contains this to run in a background thread using GCD, but I heard that since dataWithContentsOfURL is synchronous, it's bad. Are there any opinions on this?
It's bad if you run it on the main UI thread. That will block the responsiveness of your app which is bad but it's even worse on start up.
You need to make it async. You can do that by either running that method on a background thread (GCD dispatch_async) or by using async methods of NSUrlConnection.
Here's an example of using GCD to work in the background and then update the UI (after done) on the main thread:
GCD, Threads, Program Flow and UI Updating
Another option is async method of NSUrlConnection. See the initWithRequest methods here:
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/Reference/Reference.html
You're safe as long as you're running it in a background thread.
The idea that synchronous loading is bad is only valid for the main UI thread. A long running operation on the main UI thread will make your app unresponsive. Doing it in the background is the correct way to do this. Also, consider using:
+dataWithContentsOfURL:options:error:
so that you can get an error back if anything goes wrong.
Related
A common pattern in Objective C is to run a bit of code in a background thread, then go back to the main thread to make UI adjustments. If the code starts in the main thread, I'd attack this with a pattern like so:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self someBackgroundTask];
dispatch_async(dispatch_get_main_queue(), ^{
[self someUITask];
});
});
However, this seems like a really clunky way to do it, not in the least because it creates two levels of nesting that feeling unnecessary. Is there a better way to do this? Note that the UI code is considered in this instance to be relying on the background task completing, so it can't just be dropped after the first dispatch.
Just move all the threading stuff into someBackgroundTask:
[self someBackgroundTaskWithCompletion:^{
[self someUITask];
}];
Then do your dispatch_async() stuff inside the background task method.
Conceptually, you need to run code on two different threads. All UI operations must run on the main thread, and your blocking operation needs to run on a background thread. At the absolute minimum this would require two lines of code.
GCD makes this fairly simple as you mentioned; I'm not sure any language would have a better way to handle this core issue. Sure, the block syntax is bad (http://fuckingblocksyntax.com); but the core fundamentals are pretty solid.
If the nesting bothers you, try moving all that UI code to a different method, and calling that method from your second nested block. You could even create a method that accepts a 'backgroundWork' selector/block and a 'foregroundWork' selector/block. However - I'd argue that the typical UI work you'd perform in such a case would be very minimal, making the extra nesting a minor inconvenience rather than an actual problem.
As far as asynchronous blocks of code that are inter-dependent, check out PromiseKit or even Sequencer; both are good options for supplementing one of Objective C's primary weakness (sequentially performed multi-threaded operations).
I haven't used it myself, but I understand that Facebook/Parse have also released another solution called BFTask, part of the Bolts framework: https://github.com/BoltsFramework/Bolts-iOS
(Or just use C#)
I have a method which encodes selected songs on iTunes to mp3 using lame. Now I'm calling it from IBAction named "Encode". While encoding, Application fails into not responding state. And when encode is finished, Application come back.
I would like to solve this not responding state. Would you teach me how should I approach?
I guess you are doing the encoding on the main thread and this is why your application becomes unresponsive. You may want to read articles about threading and concurrency in order to solve your problem.
There is also an introduction on raywenderlich.com called "Multithreading and Grand Central Dispatch on iOS for Beginners Tutorial".
You need to dispatch it on a thread different from the main thread. Otherwise it will block the main thread which is where the GUI part of your app runs.
Here is one example of how to do it. Be careful, though, if you want to modify variables outside the block. You might want to look up the __block keyword.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
// INSERT CODE HERE
});
I am using GCD to start a long-running background process ('run_loop') that creates an NSManagedObjectContext ('MOC'), monitors CoreData objects, and sometimes (when they're ready) uploads a serialization of them to a webserver and then deletes them.
I am using AFNetworking for the HTTP calls. The problem is in the request completion handler blocks, as the blocks run in a different thread to the owner of the MOC, which isn't supported by CoreData.
I have tried storing the NSThread from the start of the GCD run_loop block, and using performSelector:onThread:run_thread but this just doesn't seem to actually call the selector at all.
I have tried using dispatch_sync(run_queue) but this doesn't guarantee the thread is the same, only the GCD queue. A different MOC save in the main thread later hangs.
Eventually the only thing that worked was to set a boolean in the completion callback handler, and to introduce extra logic to detect the boolean switch and to perform the MOC work from the main run_loop.
Would anyone be able to suggest a more elegant fix? Or is CoreData simply not compatible with an AFNetworking request started from a GCD queue, and I should look at a lower-level thread control from the start?
Hmm .. the recommended way to deal with MOC and threads is to always make a new MOC that is a sub-moc of your main thread's MOC. Let the main thread do all the saving, but your GCD threads can basically merge changes to the main MOC.
I've had pretty good success working with https://github.com/magicalpanda/MagicalRecord/ to facilitate this in a simpler fashion.
I am in the middle of creating a cloud integration framework for iOS. We allow you to save, query, count and remove with synchronous and asynchronous with selector/callback and block implementations. What is the correct practice? Running the completion blocks on the main thread or a background thread?
For simple cases, I just parameterize it and do all the work i can on secondary threads:
By default, callbacks will be made on any thread (where it is most efficient and direct - typically once the operation has completed). This is the default because messaging via main can be quite costly.
The client may optionally specify that the message must be made on the main thread. This way, it requires one line or argument. If safety is more important than efficiency, then you may want to invert the default value.
You could also attempt to batch and coalesce some messages, or simply use a timer on the main run loop to vend.
Consider both joined and detached models for some of your work.
If you can reduce the task to a result (remove the capability for incremental updates, if not needed), then you can simply run the task, do the work, and provide the result (or error) when complete.
Apple's NSURLConnection class calls back to its delegate methods on the thread from which it was initiated, while doing its work on a background thread. That seems like a sensible procedure. It's likely that a user of your framework will not enjoy having to worry about thread safety when writing a simple callback block, as they would if you created a new thread to run it on.
The two sides of the coin: If the callback touches the GUI, it has to be run on the main thread. On the other hand, if it doesn't, and is going to do a lot of work, running it on the main thread will block the GUI, causing frustration for the end user.
It's probably best to put the callback on a known, documented thread, and let the app programmer make the determination of the effect on the GUI.
When writing the usual view controller code, can I assume that this will only be called from a single event-loop thread? What kind of classes do I need to make thread-safe? What are the usual situations where multiple threads are involved?
The concurrency programming guide is good. Here are some super important things to keep in mind.
– You should only update UI from the main thread. This can get you in subtle ways...
– NSNotifications will be received in the thread from which they are fired. So if you launch a thread and subscribe to a NSNotification to trigger a UI action, you should check what thread you're on when you get it. If it's not on the main thread use NSObject's performSelectorOnMainThread: withObject:waitUntilDone: to get it on the main thread.
– If you're doing some drawing into a non-ui context, I believe core graphics is now thread safe. (I believe CATiledLayer does some clever things because of this)
– Generally for view controllers, the only event loop you should think about is the one on the main thread. Think twice before making your own event loop on another thread.
If you are writing normal UIViewController code, you don't need to worry about thread-safety in iOS. In iOS, any message about UI should be running on the main thread.
If you don't perform some message in background by you self, normally, you don't have to worry about thread, in most situations, it will always be on the main thread.
P.S. Some Frameworks like Game Kit will some times perform messages in background, but it's not about UI and the document from Apple will warn you to make sure if the message is running on main thread.
The Concurrency Programming Guide can be helpful.