How to lazy/async load various elements(data in labels) in a View? - ios

I have a ViewController with various labels. Each of these labels get dynamically populated at the run time based on various regex parsing logic running on an html page. The problem is, each regex match is taking 2-3 seconds and I have 8 such labels so that means i have to wait somewhere around 20-25 seconds before the view shows up!
This is a very very bad user experience. I want that to make this less painful for the user and therefore want to load each label independently as and when they get the data after the regex is processed and not wait for all 8 labels to finish retrieving their regex matches.
Any way this can be achieved in ios 5?

Create a separate function which calculates the values that you need.
(You probably already have this anyway for code readability/maintainability.)
Run this thread in a background thread.
When you are ready to actually set the text, make sure that you do it on the main thread:
Here is an example:
- (void)calculateLabelText {
NSString *label1Text = // However you calculate this...
dispatch_async(dispatch_get_main_queue(), ^(void) {
self.label1.text = label1Text;
});
NSString *label2Text = // However you calculate this...
dispatch_async(dispatch_get_main_queue(), ^(void) {
self.label2.text = label2Text;
});
}
In viewDidLoad, add this:
[self performSelectorInBackground:#selector(calculateLabelText) withObject:nil];

Use Grand Central Dispatch (GCD). It'll handle queues and threading etc for you. There's no need to create a method just for one set of operations that happens once, and regardless, dispatch_async() is faster than performing a selector on a background thread and you get to keep your existing code structure - you just wrap it up in a nice concurrent bundle that won't block the main thread :)
// get a reference to the global concurrent queue (no need to create our own).
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
for each label's regex operation {
dispatch_async(queue, ^{
// your regex here for one
// execute back in the main thread since UIKit only operates in the main thread.
dispatch_async(dispatch_get_main_queue(), ^{
[myLabel setText:<result of operations>];
});
});
}

if you want to use this, you have to pay some attention to get your code separated. one part do data loading work, the other one set data to controls. and you have to make sure that you sqlite (i assume u used this db) cool with multi thread.

Related

Heavy calculations on a background thread in getter

I need to perform a lot of calculations every time a getter is called from my app. The data returned from the getter is constantly changing based on the environment, and it has to do a lot of calculations to compute what it should return. Therefore, I don't want the code in the getter running on the main thread. This is what I have so far:
#interface Calculator ()
#property (nonatomic, strong) dispatch_queue_t calculationThread;
#end
- (dispatch_queue_t)calculationThread {
if (!_calculationThread) {
_calculationThread = dispatch_queue_create("calculation_thread", NULL);
}
return _calculationThread;
}
- (NSArray *)calculation {
// perform calculation in calculationThread, which should not be on main thread and be asynchronous
return arrayContainingCalculations;
}
I basically want to know how to use GCD to replace the comment. I have tried using dispatch_queue_t and dispatch_group_notify, but I don't seem to be implementing it correctly.
I think using a callback is probably the simplest and most efficient solution to this problem.
It is simply impossible to use only a single getter to do an asynchronous calculation without blocking the thread it was called on, as you expect code called after it to continue executing while it does the calculation.
You just have to create a new method with a callback, for example:
-(void) doCalculation:(void(^)(NSArray* result))callback {
dispatch_async(self.calculationQueue, ^{
NSArray* result = self.calculation; // make sure this is doing a synchronous calculation. If it's asynchronous, you'll have to use a semaphore (or another callback!).
if (callback) {
dispatch_async(dispatch_get_main_queue(), ^{ // return to main thread
callback(result);
});
}
});
}
Then you can simply invoke it on your main thread like so:
[calculator doCalculation:^(NSArray* result) {
textView.text = [result[0] stringValue]; // update UI with new info.
}];
That way you can easily keep your resulting code in-line with the call to the method.
It's also worth noting that your calculationQueue's getter (I renamed it, as the word thread is misleading when you're working with queues) isn't thread-safe. I would advise you use a dispatch_once to make it thread-safe:
-(dispatch_queue_t) calculationQueue {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_calculationQueue = dispatch_queue_create("calculation_queue", DISPATCH_QUEUE_SERIAL);
});
return _calculationQueue;
}
You can use the following to put it on your queue asynchronously. The problem however is that the method is going to return immediately.
dispatch_async(your_queue, ^{
// Code to be executed on background thread
});
What you probably want is to have some kind of method calculateWithCompletion where the caller can define a block that you can invoke once the completion is finished.
As you said in your comment to Peter, you want to keep it so you can call self.calculation and get your logic executed and return the calculation synchronously.
However because you want to avoid locking the UI while this logic is executing, you would like it to execute on a background thread.
Therefore, all you should need to do is use dispatch_sync instead of dispatch_async inside of your calculate method.
What dispatch_sync does is it places a task (the block that contains your logic) onto a specified queue (probably should pick a global concurrent queue), which then executes your task on a thread the OS picks for you (not the main thread). dispatch_async does the same, Except that dispatch_async will continue execution immediately after dispatching your task onto a queue.
dispatch_sync on the other hand, will block execution in the current run loop until your tasks returns.
This will allow you to execute your expensive logic on a background thread, while still remaining synchronous so that you can continue using self.calculation

Too many objects to delete in Parse, blocks UI: Doesnt respond, cancel deletion

I am trying to delete elements from Parse after filtering. Everything works fine when the number of elements is relatively small. However, when it increases, the problem arises. Basically, I am filtering among hundreds of elements stored in Parse. Among those, hundreds will have to be deleted.
Here are my codes:
dispatch_async(dispatch_get_main_queue(), {
var x2 = self.X as [NSArray]
for po2 in CULA {
var arr2 = po2 as! NSArray
if contains(x2, arr2) {
}
else {
PFUser.currentUser()!["myLocation"]?.removeObject(arr2)
} } })
I am using a dispatch_async at the beginning because I want this part to be executed last and separately from the codes above as the filtering occurs. I think the removeObject function is one that blocks activity, however I dont know how to get around that.
Any idea ?
Thanks a lot,
You can use the deleteInBackground method that will delete all of your objects async and not muddle up your main thread or your UI.
You are absolutely dispatching to a queue, but the main queue, which is also hte UI queue. Try putting it in a queue that will be processed by a thread that is not the main UI thread.
Here's a simple snippet that can illestrate how to dispatch for async processing off of the UI thread (and then if UI updates based on the async processing how to queue it for processing on the main UI thread)
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 { //take off main ui thread to prevent any blocking
//Do work
dispatch_async(dispatch_get_main_queue(), {//put back on to the main ui thread
}
}
There's a whole world to understand within Grand Central Dispatch, but for your above snippet, simply queue it up to be processed off the main ui thread.

Using Blocks and GCD to manage tasks

I'm learning iOS and when it comes to GCD, it's confusing.
Let's get it out of the way, I'm writing a small program that fetch data from the internet.
Here is my viewcontroller
NSMutableArray dataArray = [NSMutableArray array];
[querysomethingwithblock:(^ {
//do some stuff here
[otherquerywithblock:( ^ {
//do some stuff here
// Here I got the data from internet
// Do loop action
[dataArray addObject:data];
})];
})];
// here I want to perform some actions only after get data from internet
[self performAction:dataArray];
How can I achieve this purpose. In practical, [self performAction:dataArray] always get fired before I get the data. I tried to play with GCD but no luck.
Here is some patterns I've tried so far
dispatch_async(queue, ^{
// Do query stuff here
dispatch_async(dispatch_get_mainqueue(), ^{
//perform action here
});
{;
Or using dispatch_group_async, dispatch_group_wait, dispatch_group_notify
The only way I can handle right now is to use dispatch_after but the point is the downloading time is variable, it's not good practice to have a specific time here
Thank you so much for any advice.
The part of code called Do query stuff here i assume is async already, why put it inside a dispatch_queue then?
If instead you manage to do a synchronous query, your code (the second snippet) would work, as the dispatch to the main queue would be executed only after the query finished.
If you don't have an option to execute the query in a synchronous manner, then you need some mechanism to register either a block or a callback to be executed when the download is finished.
At the end of the day, it all depends on what kind of query you have in there and what methods it offers for you to register an action to be performed when the download is finished.

dispatch_async on main_queue?

I have seen this code snippet:
dispatch_async(dispatch_get_main_queue(), ^{
[self doSomeNetworkStuff];
});
This doesn't look like making much sense to me.
EDIT: To clarify the conditions of my question:
The call to dispatch_async is performed from the main thread.
The sent message doSomeNetworkStuff is the heavy lifting worker task.
... and is not only the UI-updating task.
Dispatch, sure, but using the main queue would just pull the dispatched task back to the ui thread and block it.
Please, am I missing something?
Thanks.
dispatch_async lets your app run tasks on many queues, so you can increase performance.
But everything that interacts with the UI must be run on the main thread.
You can run other tasks that don't relate to the UI outside the main thread to increase performance.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//Add some method process in global queue - normal for data processing
dispatch_async(dispatch_get_main_queue(), ^(){
//Add method, task you want perform on mainQueue
//Control UIView, IBOutlet all here
});
//Add some method process in global queue - normal for data processing
});
Swift 3:
DispatchQueue.global(attributes: .qosBackground).async {
print("This is run on the background queue")
DispatchQueue.main.async {
print("This is run on the main queue, after the previous code in outer block")
}
}
when you want to do some Webservicecall or something you dispatch a async call like this below:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{
//Call your webservice here , your app will not freeze at all
});
Now, suppose you want to update or push a ViewController from your dispatched thread, if you directly push viewcontroller from this, app will or may get crashed,as such UI updates should be done in main thread of app,below is the answer for this then.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{
//Call your webservice here , your app will not freeze at all
//To update UIFrom dispatched Thread:
dispatch_async(dispatch_get_main_queue,^{
//Push view controller here
});
});
for detail visit : blackberrymastercracks.blogspot.in
It depends from where this code is being called. Means if its calling from main queue then it doesn't make sense. (Note: it will not cause a crash but it will just add a task in main queue ).
If this code is written in background thread then this is a converging point for the application. Like you are getting data from web service in background thread then wants to update it on UI then you can call it.
-(void) backgroundThreadFunction {
//Some stuff on background thread.
dispatch_async(dispatch_get_main_queue(), ^{
//Wants to update UI or perform any task on main thread.
[self doSomeNetworkStuff];
});
}
You can find more details over apple documentation https://developer.apple.com/library/ios/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html
or from this answer also https://stackoverflow.com/a/19822753/505735
Do post me if its still unclear. I will write a detailed answer.
You'll usually see that syntax inside of another dispatch_async call that runs on a background thread. This is because all updates to the UI should happen on the main thread, not in the background.
I lost track of this question, but as it still gets traction, I'll post an answer to this (using swift)
Assumptions: I do know that UI work has to be done on the main thread.
//
// We are on the main thread here.
// The following will schedule the closure on the main thread after ALL other
// routines currently scheduled on the main thread are done.
//
DispatchQueue.main.async {
//
// So here we are back on the main thread AFTER all routines on the main
// thread have completed.
//
// If the following call does NOT dispatch onto a background thread
// it will block the UI and it was really bad programming.
//
// Thus, for now and for the benefit of the doubt, let's assume
// `doSomeNetworkStuff()` DOES dispatch to a background thread.
//
// This can only make sense if the the func `doSomeNetworkStuff()`
// relies on results of code paths following this current
// `DispatchQueue.main.async(... we are here ...)`.
//
// If `doSomeNetworkStuff()` does NOT depend on any other code paths:
// Why not directly scheduling it directly on a background thread?
// Which is unnecessary, as as stated above it MUST dispatch on to the
// background anyways.
//
// Moreover, there is few possibility that `doSomeNetworkStuff()` does
// depend on other codepaths, because `self` is already captured by
// the closure.
//
self.doSomeNetworkStuff()
}
Taking all this together IMHO the original code does not make very much sense. It could be replaced with:
// We are on the main thread here
self.doSomeNetworkStuff()
The original async dispatch onto the main thread to then dispatch to background should be wasteful and confusing (obviously).
Unfortunately I am not in the position anymore to try this out with the original code base.
Am I missing an idea here?

Loading 20+ plist files into memory at app launch

Following up on my question here, I have 22 plist files that I'd like to load at launch time. Right now, it works, but as you can imagine the launch is very slow.
What can I do to best minimize the launch time?
Can I load each one in a seperate thread using NSOperationQueue? I've also read that converting the plists to binary will help.
You can use NSOperationQueue, performSelectorInBackground or Grand Central Dispatch (once you know how to use the later you'll use that very often as it makes threading very easy). The main point is simply is to not load them on the main thread and to load them one after another (if you try to load them all at once each in its own thread performance is likely to not be good either). When the loading is done, call a method on your main thread to assign the result.
For example:
yourQueue = dispatch_queue_create("plist load queue", NULL);
for (filename in plistFilesToLoad) {
dispatch_async(yourQueue, ^{
// This part will be executed in a thread,
// each block after the other.
NSDictionary *dict = [self loadPlist:filename];
dispatch_async(dispatch_get_main_queue(), ^{
// Assign the result on the main thread.
[self finishedLoading:filename withDictionary:dict];
});
});
}
The dispatch_async calls come back quickly, so the loop itself will be executed quickly. But GCD will then execute each block one after another on a separate thread.

Resources