Reputation: 3703
I have a method that needs to wait for another method to finish loading some objects from the datastore. So I'm using a while loop like this:
-(void) findAGoodCar {
[self.indicator startAnimating];
while(appDelegate.availableCars == nil || [appDelegate.availableCars count] < 1) {
sleep(0.01);
}
// do some stuff
}
While this starts working the other process, for loading the cars, has already started.
Now, the indicator never starts animating. The loading of objects never finishes. The loop never ends. Why?
EDIT: This is my loadCars method. It is called from the appDelegate when the app starts, from (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
and is doing the query to Parse.com's cloud asynchronously:
+(void) loadCars {
PFQuery *query = [PFQuery queryWithClassName:kCarObjectKey];
[query whereKey:kActiveKey equalTo:[NSNumber numberWithInteger:kActiveNumber]];
NSSortDescriptor *sortByPrice = [[NSSortDescriptor alloc] initWithKey:kPriceKey ascending:YES];
[query orderBySortDescriptors:[NSArray arrayWithObjects:sortByPrice, nil]];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if(!error) {
AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
appDelegate.availableCars = objects;
} else {
NSLog(@"Error in loadCars %@", error.localizedDescription);
}
}];
}
Upvotes: 1
Views: 175
Reputation: 4012
Here is updated post, because it the real author's problem is not a "infinite loop".
Parse provides two ways of getting/putting data to its servers:
Drawbacks of sync method is obvious - it blocks UI thread and user see unresponsive UI.
Then why was that group of methods inroduced?
There are some cases in your app's login when you can't continue working without knowing the result. In that case you have to use sync methods.
Async approach doesn't block UI. But it requires deeper understaning of process.
Of course, you can use such loops:
while(appDelegate.availableCars == nil || [appDelegate.availableCars count] < 1) {
sleep(0.01);
}
That approach will give you the result, but you block a UI thread. Using such approach for async operation is bad practive. You should consider using block for handling asynchronyous requests.
Here is my point of view on app's architecture. I prefer isolating all the code, working with Parse (and other APIs) into separate classes. So I make a class, and make it singleton
ParseClient.h file:
@interface ParseClient : NSObject
// singleton
+ (instancetype) sharedInstance;
@end
ParseClient.m file:
@implementation ParseClient
+ (instancetype) sharedInstance {
static ParseClient *sharedInstace = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstace = [[ParseClient alloc] init];
});
return sharedInstace;
}
@end
So now you have access to parse object in whole app. Now you can add required function to this class. You can add cache (for example, store an array as instance variable) and make your own cache policy.
For example, you can add the following function to ParseClient:
- (void) loadCarsWithCompletionBlock:(void (^)(NSArray *objects, NSError *error))completionBlock {
PFQuery *query = [PFQuery queryWithClassName:kCarObjectKey];
[query whereKey:kActiveKey equalTo:[NSNumber numberWithInteger:kActiveNumber]];
NSSortDescriptor *sortByPrice = [[NSSortDescriptor alloc] initWithKey:kPriceKey ascending:YES];
[query orderBySortDescriptors:[NSArray arrayWithObjects:sortByPrice, nil]];
[query findObjectsInBackgroundWithBlock:completionBlock];
}
and call it in anywhere in your app
- (void) foo {
...
[[ParseClient sharedInstance] loadCarsWithCompletionBlock:^(NSArray *objects, NSError *error){
// you have array of cars here
}];
...
}
Upvotes: 5
Reputation: 4585
I think you are loading cars in the same thread. Try:
while(0 == appDelegate.availableCars.count) {
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate distantFuture]];
}
Upvotes: 0
Reputation: 21808
It is still bad, but i don't see your whole picture. Try this:
while(appDelegate.availableCars == nil || [appDelegate.availableCars count] < 1) {
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
}
This won't block main thread
Upvotes: 2
Reputation: 2918
Seems like 'findAGoodCar' is executed on main thread. In that case, you are blocking the main thread of the app. It will block all the UI events.
Upvotes: 2