Eddy
Eddy

Reputation: 3703

Never getting out of while loop

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

Answers (4)

Nikita Took
Nikita Took

Reputation: 4012

Here is updated post, because it the real author's problem is not a "infinite loop".

Working with Parse

Parse provides two ways of getting/putting data to its servers:

  • sync
  • async

Sync

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

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.

App's architecture

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

Cy-4AH
Cy-4AH

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

Andrey Chernukha
Andrey Chernukha

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

Shanti K
Shanti K

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

Related Questions