In my iOS app I need to retrieve some values by using PFObject
queries. And when these are all loaded, the app should continue doing some more stuff.
func processTransaction(){
.. some code to prepare my 2 queries
var balance1 = balance1Query.getFirstObject() as PFObject;
var balance2 = balance2Query.getFirstObject() as PFObject;
.. after these are loaded i have to update them, and 'release' the screen to the user.
}
Now, when using this .getFirstObject()
method, I get a warning in my Xcode debug window saying:
A long-running operation is being executed on the main thread.
Of course I did some research, and every answer I read talks about using getObjectInBackgroundWithId
, which then moves the execution of these queries to the background (I guess doing so it puts them on a new thread.)
But my question is, how should I structure my code / design, to make sure that both objects are loaded, before the code continues to work with these objects?
I tried some things, and create some code like below, but to me it feels like this isn't the right approach. Can't think of what a mess this will become when I have to load and make sure that 4 or 5 objects are loaded.
balanceQuery1.getFirstObjectInBackgroundWithBlock{
(balance1: PFObject!, error1: NSError!) -> Void in
if error1 == nil {
balanceQuery2.getFirstObjectInBackgroundWithBlock{
(balance2: PFObject!, error2: NSError!) -> Void in
if error2 == nil {
... processing here..
}
}
}
My suggestion that your approach was correct was based on the requirement to execute two queries - In this case you can dispatch the first query into the background and then execute the subsequent query in the completion block.
If you are looking for a more general asynchronous dispatch model then I would suggest using dispatch groups to synchronise the activities. To use this technique effectively it is best to have your queries in an array or similar data structure. (Apologies, I haven't converted this to Swift, but it is essentially the same)
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
// Add a task to the group
for (int i=0;i<self.queries.count;i++) {
dispatch_group_async(group, queue, ^{
self.queries[i].getFirstObject ...
});
}
// When you cannot make any more forward progress,
// wait on the group to block the current thread.
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
// Release the group when it is no longer needed.
dispatch_release(group);
If you want to avoid nesting, there is a second approach that can be useful, especially if you have a LOT of queries to run. Three steps:
int completedQueries
variable to 0 that will be incremented when a query finishes int totalQueries
variable that represents the total number of queries to finish For each query's completion block, increment completedQueries
and run a function to check if the number of completions is equal to the total:
balanceQuery1.getFirstObjectInBackgroundWithBlock{ (balance1: PFObject!, error1: NSError!) -> Void in if error1 == nil { completedQueries++ checkQueriesCompleted() } } //balanceQuery2, 3 and so on below...
Finally, have a checkCompletedQueries
function that waits until your total and completed are equal:
func checkQueriesCompleted() {
if (completedQueries == totalQueries) {
//... processing here ....
}
}
This structure is especially powerful if you have 3+ functions, or you have an arbitrary number of calls. I use it a lot when I want to get around the 1000 item return limit on Parse (get a total number of objects, make as many calls as I need with a .skip parameter, wait for all returns before processing) - you get the idea.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.