我是swift并使用异步代码的新手。
我有一种方法可以从API中获取数据,将数据放入对象并将这些对象放入数组中。然后,我想用来自数组中这些对象的数据更新UI。
为了说明UI的更新,我将更改标签的值。
因为从API获取信息的方法是异步的,所以我无法从中返回值。而是通过回调:
@IBOutlet var labelTest: UILabel!
private var eventsArray : Array<Event> = Array<Event>()
override func viewDidLoad() {
APIAccessUtil.getEventsFromAPI({(eventsList: Array<Event>) -> Void in
self.eventsArray = eventsList
})
sleep(5) //this is of course not good, but I'll leave it here to illustrate what's going on.
self.labelTest.text = eventsArray[0].description
}
这个工作因为让线程休眠会留下时间让数组被填满。但是,使主线程处于睡眠状态是一个非常糟糕的主意,并且无法保证此时将返回数据。
如果删除了sleep(...)
,则会在getEventsFromAPI
调用完成之前尝试更新UI标签,从而产生运行时错误(因为数组仍然是nil)。
尝试2:
@IBOutlet var labelTest: UILabel!
private var eventsArray : Array<Event> = Array<Event>()
override func viewDidLoad() {
APIAccessUtil.getEventsFromAPI({(eventsList: Array<Event>) -> Void in
self.eventsArray = eventsList
self.labelTest.text = eventsArray[0].description
})
}
在尝试#2中,我从后台线程更新UI,这不是很好,并产生以下警告:
此应用程序正在从后台线程修改autolayout引擎,这可能导致引擎损坏和奇怪的崩溃。这将在将来的版本中引发异常。
那么,有没有办法在getEventsFromAPI
调用完成后更新主线程的UI(即更改标签文本)(这样数组实例变量现在不是nil)?
答案 0 :(得分:4)
你快到了。最后一招是从主线程调用UI更新:
override func viewDidLoad() {
APIAccessUtil.getEventsFromAPI({(eventsList: Array<Event>) -> Void in
self.eventsArray = eventsList
// Update the UI on the main thread
DispatchQueue.main.async {
self.labelTest.text = eventsArray[0].description
}
})
}
答案 1 :(得分:0)
在Swift 4中,这可以更简单地完成:
APIAccessUtil.getEventsFromAPI({(eventsList: Array<Event>) -> Void in
self.eventsArray = eventsList
// Update the UI on the main thread
DispatchQueue.main.async() {
self.labelTest.text = eventsArray[0].description
}
})