How to assign result of NSURLSession to a variable in same method?

拜拜、爱过 提交于 2019-12-25 16:45:12

问题


To say I'm new to Objective-C would be a huge understatement. I'm primarily a Ruby/Rails developer and it completely spoiled me when it comes to OOP & programming in general.

After getting tired of reading tutorials, I decided to try to use NSRULSession to hit one of my Rails apps (an Elder Scrolls Online skill planner) & display some of the JSON response on my iOS app. Delegates make no sense, I'm not sure how to break this functionality up into methods, etc, so I thought I'd keep it simple & do it all in the viewDidLoad() method (yes, I know it's bad practice).

- (void)viewDidLoad {
    [super viewDidLoad];

    __block NSDictionary *skillData; // No clue what __block is

    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionDataTask *dataTask = [session dataTaskWithURL:[NSURL URLWithString:@"http://www.esomix.com/skill_builds/17.json"] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
        NSLog(@"%@", json[@"name"]);
        skillData = json;
    }];

    [dataTask resume];

    UILabel *myLabel = [[UILabel alloc] initWithFrame:CGRectMake(50, 100, 200, 100)];
    myLabel.textColor = [UIColor colorWithRed:255.0/255.0 green:255.0/255.0 blue:255.0/255.0 alpha:1];
    [self.view addSubview:myLabel];

    NSLog(@"%@", skillData[@"name"]);
    NSLog(@"bottom of method");
}

After lots of toying around, I figured out that despite the NSURLSession code before my NSLogs at the bottom, it returns its data after they're rendered. No wonder my label.text (not shown) wasn't getting set! Here's the order of my three NSLogs:

(null)
bottom of method
Single-Target Lockdown Sniper

I guess my question is what's the simplest, proper way to make an JSON API request and use the data to generate a UI element/other tasks after the data has returned. Thanks so much!


回答1:


A couple of clarifying points:

  1. You ask

    what's the simplest, proper way to make an JSON API request and use the data to generate a UI element/other tasks after the data has returned

    In short, use the JSON response inside the completion block, not after it, for example:

    - (void)viewDidLoad 
    {
        [super viewDidLoad];
    
        NSURLSession *session = [NSURLSession sharedSession];
        NSURLSessionDataTask *dataTask = [session dataTaskWithURL:[NSURL URLWithString:@"http://www.esomix.com/skill_builds/17.json"] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
    
            NSDictionary *skillData = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
    
            // use `skillData` here
    
            // finally, any UI/model updates should happen on main queue
    
            dispatch_async(dispatch_get_main_queue(), ^{
                UILabel *myLabel = [[UILabel alloc] initWithFrame:CGRectMake(50, 100, 200, 100)];
                myLabel.textColor = [UIColor colorWithRed:255.0/255.0 green:255.0/255.0 blue:255.0/255.0 alpha:1];
                myLabel.text = skillData[@"name"]; // or whatever you wanted from `skillData`
                [self.view addSubview:myLabel];
            });
        }];
    
        [dataTask resume];
    
        // don't try to use `skillData` here, as the above block runs asynchronously,
        // and thus `skillData` will not have been set yet
    }
    
  2. The purpose of the __block qualifier is to let the block update a variable whose scope is outside the block. But, because the NSURLSessionDataTask runs asynchronously, there's no point in trying to reference skillData outside that block (because the viewDidLoad method will have completed well before the completionHandler for the NSURLSessionDataTask is invoked, as illustrated by your NSLog results).

    So, since there's no point in referencing skillData outside the block, then there's no point in defining it with the __block qualifier outside of the block. Just make it a local variable inside the block. If you want to update your model (or perhaps some properties of the view controller), you can do that (but when dealing with class properties and ivars, no __block qualifier is needed).



来源:https://stackoverflow.com/questions/20917297/how-to-assign-result-of-nsurlsession-to-a-variable-in-same-method

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!