jinglei
jinglei

Reputation: 3363

ios - Program with block executed out of order?

I'm trying to get an array of urls from my backend.

I use AFNetworking and I have a HTTPUtil class implemented as singleton to handle my requests.

HTTPUtil.m

#import "HTTPUtil.h"

@implementation HTTPUtil

+(instancetype)sharedInstance{
    NSLog(@"sharedInstance");    //to check the order
    static HTTPUtil* manager;
    static dispatch_once_t once;
    dispatch_once(&once, ^{
        manager = [[HTTPUtil alloc] init];
    });
    manager.responseSerializer = [AFJSONResponseSerializer serializer];
    manager.requestSerializer = [AFJSONRequestSerializer serializer];
    return manager;
}

-(void)getImageArrayFromURL:(NSString *)url success:(void(^)(NSArray* array))success failure:(void(^)(NSError* error))failure{
    NSLog(@"getting...");    //to check the order
    [self GET:url parameters:nil progress:nil success:^(NSURLSessionDataTask* task, id response){
        NSLog(@"Response: %@", response);
        NSString* imgStr = [[response objectForKey:kResponseDataKey] objectForKey:@"img"];

        //convert nsstring to nsarray
        NSArray* array = [StringUtil arrayFromString:imgStr];

        //construct urls
        NSMutableArray* ret = [[NSMutableArray alloc] init];
        NSMutableString* url;
        for (NSString* rawStr in array) {
            url = [NSMutableString stringWithString:kUrlBase];
            [url appendString:[rawStr stringByReplacingOccurrencesOfString:@"/" withString:@"+"]];
            [ret addObject:url];
        }
        success(ret);
    }failure:^(NSURLSessionDataTask* task, NSError* error){
        NSLog(@"Error: %@", error);
        failure(error);
    }];
}

In my view controller, I call the method to fetch the array.

    _vCycleScrollView = [SDCycleScrollView cycleScrollViewWithFrame:CGRectMake(0, 0, 0, 0) delegate:self placeholderImage:[UIImage imageNamed:@"checked"]];
    NSMutableString* url = [NSMutableString stringWithString:kUrlBase];
    [url appendString:@"activityImgArray"];
//
     __block NSArray* imgarr;
    [[HTTPUtil sharedInstance] getImageArrayFromURL:url success:^(NSArray* array){
        imgarr = [NSArray arrayWithArray:array];
    }failure:^(NSError* error){
        NSLog(@"%@", error);
    }];
    NSLog(@"adding...");
    _vCycleScrollView.imageURLStringsGroup = imgarr;

[self.view addSubview:_vCycleScrollView];
[_vCycleScrollView mas_makeConstraints:^(MASConstraintMaker* make){
    make.top.equalTo(self.view);
    make.left.equalTo(self.view);
    make.right.equalTo(self.view);
    make.height.mas_equalTo(180);
    make.width.equalTo(self.view.mas_width);
}];

In the console, I got

2016-05-20 14:41:19.411 SCUxCHG[10470:4909076] sharedInstance
2016-05-20 14:41:19.415 SCUxCHG[10470:4909076] getting...
2016-05-20 14:41:19.417 SCUxCHG[10470:4909076] adding...
2016-05-20 14:41:19.591 SCUxCHG[10470:4909076] 
Response: {
    data =     {
        img = "[activity/test1, acti/1]";
    };
    message = success;
    result = 0;
}

I thought imgArr should be assigned in the success block and it shouldn't be nil when I assign it to _vCycleScrollView.imageURLStringsGroup.

However, I can tell from the output in the console that the HTTP request is sent after NSLog(@"adding..."); and that leads to the fact that imgArr is still nil when _vCycleScrollView.imageURLStringsGroup = imgarr; is executed.

Why is that?

Upvotes: 0

Views: 64

Answers (3)

Sinisa Drpa
Sinisa Drpa

Reputation: 917

Try bellow:

 __block NSArray* imgarr;
[[HTTPUtil sharedInstance] getImageArrayFromURL:url success:^(NSArray* array){
    imgarr = [NSArray arrayWithArray:array];
    NSLog(@"adding...");
    _vCycleScrollView.imageURLStringsGroup = imgarr;
}failure:^(NSError* error){
    NSLog(@"%@", error);
}];

The completion block is executed once data is fetched. In your case code continues to execute after the completion block is set but data hasn't been fetched yet, that's why imgarr is nil.

Upvotes: 1

JP_Mob
JP_Mob

Reputation: 114

Yes below code is in block so this will continue in background

[[HTTPUtil sharedInstance] getImageArrayFromURL:url success:^(NSArray* array){
    imgarr = [NSArray arrayWithArray:array];
}failure:^(NSError* error){
    NSLog(@"%@", error);
}];

solution - You should add _vCycleScrollView.imageURLStringsGroup = imgarr; inside of success block because you d0 not know when it will completed Or there is another way you should not call in block or should not create block.

Upvotes: 2

gnasher729
gnasher729

Reputation: 52632

That's the whole idea: That blocks are executed out of order. The trick is that you don't wait for a block to finish. Instead, the block finishes and then it does what is needed. The code in your viewcontroller isn't going to work, can't work, and we don't want it to work. Instead, the callback block deposits the image somewhere, and then tells the tableview to reload the row.

Upvotes: 0

Related Questions