Обновите UILabel с результатами из результатов вызова dispatch_async NSURLSession

Я начал программировать на Objective-C пару недель назад, поэтому мое понимание того, как все эти части сочетаются друг с другом, и порядок, в котором они все происходят, все еще сбивает меня с толку. Я пытаюсь сделать вызов API JSON для одного из моих приложений, используя NSURLSession. Все это работает безупречно, но я хочу обновить метку частью возвращаемых данных, и каждый раз, когда я смотрю/пытаюсь обновить метку, я получаю нулевое значение.

Некоторые из сообщений SO, которые я обнаружил, похожие на мою проблему, включают: >это, это, это и это. Приходя из мира Ruby on Rails, мне вообще не приходилось иметь дело с асинхронными концепциями, но я знаю, что близок к этому.

Вот соответствующий фрагмент рассматриваемого кода:

        if (!jsonError) {
            NSDictionary *skillBuildData = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];

            dispatch_async(dispatch_get_main_queue(), ^{
                    NSLog(@"%@:", skillBuildNameLabel.text);  // should say "Build Name" but returns null
                    NSLog(@"%@", skillBuildData[@"name"]);    // correctly prints the result
                    NSLog(@"%@:", skillBuildNameLabel.text);  // should have contents of skillBuildData[@"name"] but returns null
                    skillBuildNameLabel.text = skillBuildData[@"name"]; // obviously results in null, but I don't know why.
            });
        }

ИЗМЕНИТЬ:

Не уверен, что это актуально, но вот основная часть моего ViewController.h, чтобы дать вам представление о торговых точках и действиях в этом очень простом приложении. Одна кнопка, один метод, IBOutlet, связывающий кнопку и метод вызова JSON, и метка:

@interface ViewController : UIViewController {
    IBOutlet UILabel *skillBuildNameLabel;
    IBOutlet UIButton *getSkillBuildDataButton;
}

- (void)getSkillBuildDataById:(int) skillBuildId;

- (IBAction)buttonPressed;

Кажется, я очень близко, я просто не вижу ссылку, которую мне не хватает. Огромное спасибо заранее!

РЕДАКТИРОВАТЬ 2:

Посмотрите комментарий Бена Кригера к ответу, который я пометил как ответ. Я не связал фактическую метку в своей раскадровке с выходом, который я создал в своем ViewController.h. Я понятия не имел, что можно перетащить строку из элемента в раскадровке в настоящую строку кода. Это была недостающая часть. Похоже, мне еще многое предстоит узнать о Xcode и Objective-C. Спасибо всем, кто мне помог!


person Kyle Carlson    schedule 05.01.2014    source источник
comment
Ваши фрагменты кода слишком узки, чтобы точно сказать, что происходит. Ваш фрагмент обратного вызова данных JSON: где он находится, в ViewController.m? Что происходит, когда вы NSLog(@"%@", skillBuildNameLabel);? Вы делаете хорошее дело, заходя в основной поток, чтобы внести эти изменения, но я все еще не могу получить достаточно четкого представления о том, что происходит в этом первом фрагменте.   -  person Ben Kreeger    schedule 06.01.2014
comment
@BenKreeger: он находится в глубоко вложенном наборе проверок ошибок в completionHandler (обратном вызове?) метода, который изначально вызывает вызов API. Я просто не уверен, почему код не может говорить с меткой.   -  person Kyle Carlson    schedule 06.01.2014
comment
Что происходит, когда вы NSLog(@"%@", skillBuildNameLabel); внутри обратного вызова?   -  person Ben Kreeger    schedule 06.01.2014


Ответы (2)


Возможно, вам повезет больше, объявив IBOutlets как свойства (@property), а не как переменные экземпляра (см. этот ответ, почему weak вместо этого из strong).

@interface ViewController : UIViewController

@property (weak, nonatomic) IBOutlet UILabel *skillBuildNameLabel;
@property (weak, nonatomic) IBOutlet UIButton *getSkillBuildDataButton;

...

@end

Тогда вы сможете ссылаться на них как self.skillBuildNameLabel и self.getSkillBuildDataButton в своей реализации.

Имейте в виду, что эта нотация self.x внутри такого обратного вызова может привести к так называемому циклу сохранения. Если это так, Xcode предупредит вас об этом. Вот вам немного о циклах сохранения.

Сноска: я редко вижу (и никогда не пишу) этот синтаксис для объявления переменных экземпляра.

@interface ViewController : UIViewController {
    IBOutlet UILabel *skillBuildNameLabel;
    IBOutlet UIButton *getSkillBuildDataButton;
}

Вместо этого используйте свойства.

person Ben Kreeger    schedule 06.01.2014
comment
Спасибо за ссылки и предложения как свойства, а не торговые точки. Я полагаю, что в этом есть смысл, но я просто предположил, что это было задано по умолчанию, поскольку метка принадлежала ViewController (теперь как свойства в соответствии с вашим предложением). Я попытался зарегистрировать self.skillBuildNameLabel в своем обратном вызове, над ним и под ним, и все они вернули null. Должен ли я на самом деле где-то называть лейбл, как я это делал во времена VB? Как код узнает, что метка в моей раскадровке ЯВЛЯЕТСЯ меткой, которую я хочу обновить? - person Kyle Carlson; 06.01.2014
comment
Вам нужно будет подключить розетку с помощью Interface Builder; это перетаскивание ctrl из вашего UILabel в вашей раскадровке в нужную строку кода. Лучше всего это делать, когда помощник редактора открыт. Вместо того, чтобы создавать новую розетку, вы можете перетащить линию с нажатой клавишей Ctrl прямо на уже созданную розетку. - person Ben Kreeger; 06.01.2014
comment
УСПЕХ!!! Я понятия не имел, что можно перетащить элемент пользовательского интерфейса в строку кода, и не знал, что это необходимо. Я знал, что явно отсутствует ссылка, просто казалось странным, что ViewController не знает о метке. Большое спасибо. Пометка вашего ответа как правильного. - person Kyle Carlson; 06.01.2014

Вы делаете регистрацию перед установкой текста. переехать

skillBuildNameLabel.text = skillBuildData[@"name"];

в начало асинхронного блока, над операторами NSLog.

person Nick    schedule 05.01.2014
comment
Спасибо, но это ничего не изменило. Все команды NSLog, связанные с моей меткой, по-прежнему пусты, даже несмотря на то, что я предоставил своей метке текст по умолчанию. Я бы еще подумал skillBuildLabel.text' would have showed its default Build Name` текст. - person Kyle Carlson; 06.01.2014
comment
Вы инициализировали UILabel? т.е. skillBuildNameLabel = nil при выполнении этого блока? - person Nick; 06.01.2014
comment
Я поместил его на свою раскадровку и создал для него выход, но должен ли я инициализировать что-то еще? Я помню, когда я еще работал с Visual Basic, вы просто называли элемент label/UI, когда помещали его на страницу. Не мог найти нигде, чтобы сделать это в Xcode. - person Kyle Carlson; 06.01.2014
comment
Я бы поставил там точку останова и подтвердил, что метка не равна нулю. Как правило, когда раскадровка позволяет генерировать код для вас, объявление метки должно выглядеть так, как вы видите в ответе Бена Кригера. Если он подключен неправильно, вы можете воссоздать метку в раскадровке или добавить ее программно. - person Nick; 06.01.2014
comment
Я поставил точку останова (да, только что это придумал) и NSLogged внутри, снаружи, выше и ниже моего обратного вызова, и сама метка всегда возвращала значение null. Я просто не понимаю, почему ViewController не видит этикетку. Должен ли я связать это с чем-то из моей раскадровки? - person Kyle Carlson; 06.01.2014
comment
Вы должны опубликовать полный файл .m вашего ViewController, а также код, представляющий этот контроллер представления. - person Nick; 06.01.2014