在 dataTaskWithURL 之后从闭包中分配数据导致 nil 值
Assigning data from closure after dataTaskWithURL resulting in nil values
对于我描述的简单性,我深表歉意,我是 Swift 语言的新手。
我有一个 REST api 可以为我的应用程序提供所有数据。我正在尝试从 JSON 提要填充 table 视图。本质上,我正在尽力坚持我在 Web 开发中使用的 MVC 模式。所以我构建了我的数据对象的模型,然后构建了一个列表模型来包含该对象的各个实例。
然而,实际问题是,当我启动整个过程(最后一个代码示例)时,结果计数为 0,但这仅仅是因为 API 调用的数据响应尚未发生。我宁愿不使用同步调用,但我觉得这是我要解决这个问题的唯一方法,除非我开始将我的 API 调用直接混入控制器本身?
class NewsItem {
var id: Int
var headline: String
var summary: String
var created: String
var source: String
var company: String
var companyLogo: UIImage
init(headline: String, summary: String, created: String, source: String, company: String, companyLogoUrl: String, id: Int) {
self.id = id
self.headline = headline
self.summary = summary
self.created = created
self.source = source
self.company = company
if let url = NSURL(string: companyLogoUrl) {
if let data = NSData(contentsOfURL: url) {
companyLogo = UIImage(data: data)!
} else {
companyLogo = UIImage(named: "DefaultLogo")!
}
} else {
companyLogo = UIImage(named: "DefaultLogo")!
}
}
}
然后我有我的 NewsList class,它实际上使用 rest 调用的闭包构建了一个新闻项数组。
class NewsList {
var name: String
var newsItems: [NewsItem]
init(named: String, includeNewsItems: [NewsItem]) {
name = named
newsItems = includeNewsItems
}
class func getNewsItems() -> [NewsList] {
return [self.parsedNews()]
}
private class func parsedNews() -> NewsList {
var newsItems = [NewsItem]()
api.getLatestActivityData({JSONData, error -> Void in
if (JSONData != nil) {
for (_, subJSON) in JSONData["data"] {
let headline = subJSON["headline"].string!
let summary = subJSON["summary"].string!
let created = subJSON["created"].string!
let source = subJSON["source"].string!
let company = subJSON["company"].string!
let companyLogo = subJSON["companyLogo"].string!
let id = subJSON["id"].int!
dispatch_async(dispatch_get_main_queue(), {
newsItems.append(NewsItem(headline: headline, summary: summary, created: created, source: source, company: company, companyLogoUrl: companyLogo, id: id))
})
}
} else {
print("api data fetch failed")
print(error)
}
})
return NewsList(named: "News", includeNewsItems: newsItems)
}
}
对于上面的调用,我在我的 RemoteAPI class
中有以下方法
func getLatestActivityData(completionHandler: ((JSON!, NSError!) -> Void)!) -> Void {
let requestUrl = self.baseUrl + "xxxxxxx"
print("Request URL: " + requestUrl)
let url = NSURL(string: requestUrl)!
let request = NSMutableURLRequest(URL: url)
request.setValue("text/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
let task = NSURLSession.sharedSession().dataTaskWithURL(url, completionHandler: {data, response, error -> Void in
if (error != nil) {
return completionHandler(nil, error)
}
print("Latest activity response received")
let returnString = NSString(data: data!, encoding: NSUTF8StringEncoding)
let jsonData = returnString!.dataUsingEncoding(NSUTF8StringEncoding)!
let readableJSON = JSON(data: jsonData, options: NSJSONReadingOptions.MutableContainers, error: nil)
if (error != nil) {
return completionHandler(nil, error)
} else {
return completionHandler(readableJSON, nil)
}
})
task.resume()
}
为了真正启动上述整个复杂的过程,我在我的视图控制器中有这个
var newsItems: [NewsItem] {
var newsList = NewsList.getNewsItems()
return newsList[0].newsItems
}
这里是我们的痛点。如果我在上述调用后触发 print(newsItems.count)
,结果为 0。但是一两秒后,回调处理程序将实际接收数据并开始将项目分配给 newsList
数组。
如何在实际接收到数据之前阻止数组赋值?我只是以完全错误的方式处理这件事吗?
XCODE 7 + Swift 2.0 + iOS 9.0.x
我的错误在于我使用闭包的方式。我依靠闭包在闭包外分配值,然后完成处理程序 return 这些值。
解法:
我删除了整个 NewsList class,它实际上对我所做的是多余的
我更改了视图控制器中的调用以执行以下操作
var newsItems = [NewsItem]()
api.getLatestActivityData({JSONData, error -> Void in
if (JSONData != nil) {
dispatch_async(dispatch_get_main_queue(), {
for (_, subJSON) in JSONData["data"] {
let headline = subJSON["headline"].string!
let summary = subJSON["summary"].string!
let created = subJSON["created"].string!
let source = subJSON["source"].string!
let company = subJSON["company"].string!
let companyLogo = subJSON["companyLogo"].string!
let id = subJSON["id"].int!
newsItems.append(NewsItem(headline: headline, summary: summary, created: created, source: source, company: company, companyLogoUrl: companyLogo, id: id))
}
self.activityTableView.reloadData()
})
} else {
print("api data fetch failed")
print(error)
}
})
现在我需要做的就是在 for 循环完成后,我应该能够通过重新加载来更新 table。
对于像我这样的 'newer' swift 用户,一些我不明白的东西昨天知道会有所帮助......当你调用 tableView.reloadData()
时,应用程序将触发所有tabieView 相关事件,如果你的数据源已经像我上面那样从闭包中更新,你的 cellForRowAtIndexPath
和 numberOfRowsInSection
将能够正确更新。只需在 cellForRowAtIndexPath
函数中分配您的数据。
希望这对某人有所帮助!
对于我描述的简单性,我深表歉意,我是 Swift 语言的新手。
我有一个 REST api 可以为我的应用程序提供所有数据。我正在尝试从 JSON 提要填充 table 视图。本质上,我正在尽力坚持我在 Web 开发中使用的 MVC 模式。所以我构建了我的数据对象的模型,然后构建了一个列表模型来包含该对象的各个实例。
然而,实际问题是,当我启动整个过程(最后一个代码示例)时,结果计数为 0,但这仅仅是因为 API 调用的数据响应尚未发生。我宁愿不使用同步调用,但我觉得这是我要解决这个问题的唯一方法,除非我开始将我的 API 调用直接混入控制器本身?
class NewsItem {
var id: Int
var headline: String
var summary: String
var created: String
var source: String
var company: String
var companyLogo: UIImage
init(headline: String, summary: String, created: String, source: String, company: String, companyLogoUrl: String, id: Int) {
self.id = id
self.headline = headline
self.summary = summary
self.created = created
self.source = source
self.company = company
if let url = NSURL(string: companyLogoUrl) {
if let data = NSData(contentsOfURL: url) {
companyLogo = UIImage(data: data)!
} else {
companyLogo = UIImage(named: "DefaultLogo")!
}
} else {
companyLogo = UIImage(named: "DefaultLogo")!
}
}
}
然后我有我的 NewsList class,它实际上使用 rest 调用的闭包构建了一个新闻项数组。
class NewsList {
var name: String
var newsItems: [NewsItem]
init(named: String, includeNewsItems: [NewsItem]) {
name = named
newsItems = includeNewsItems
}
class func getNewsItems() -> [NewsList] {
return [self.parsedNews()]
}
private class func parsedNews() -> NewsList {
var newsItems = [NewsItem]()
api.getLatestActivityData({JSONData, error -> Void in
if (JSONData != nil) {
for (_, subJSON) in JSONData["data"] {
let headline = subJSON["headline"].string!
let summary = subJSON["summary"].string!
let created = subJSON["created"].string!
let source = subJSON["source"].string!
let company = subJSON["company"].string!
let companyLogo = subJSON["companyLogo"].string!
let id = subJSON["id"].int!
dispatch_async(dispatch_get_main_queue(), {
newsItems.append(NewsItem(headline: headline, summary: summary, created: created, source: source, company: company, companyLogoUrl: companyLogo, id: id))
})
}
} else {
print("api data fetch failed")
print(error)
}
})
return NewsList(named: "News", includeNewsItems: newsItems)
}
}
对于上面的调用,我在我的 RemoteAPI class
中有以下方法func getLatestActivityData(completionHandler: ((JSON!, NSError!) -> Void)!) -> Void {
let requestUrl = self.baseUrl + "xxxxxxx"
print("Request URL: " + requestUrl)
let url = NSURL(string: requestUrl)!
let request = NSMutableURLRequest(URL: url)
request.setValue("text/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
let task = NSURLSession.sharedSession().dataTaskWithURL(url, completionHandler: {data, response, error -> Void in
if (error != nil) {
return completionHandler(nil, error)
}
print("Latest activity response received")
let returnString = NSString(data: data!, encoding: NSUTF8StringEncoding)
let jsonData = returnString!.dataUsingEncoding(NSUTF8StringEncoding)!
let readableJSON = JSON(data: jsonData, options: NSJSONReadingOptions.MutableContainers, error: nil)
if (error != nil) {
return completionHandler(nil, error)
} else {
return completionHandler(readableJSON, nil)
}
})
task.resume()
}
为了真正启动上述整个复杂的过程,我在我的视图控制器中有这个
var newsItems: [NewsItem] {
var newsList = NewsList.getNewsItems()
return newsList[0].newsItems
}
这里是我们的痛点。如果我在上述调用后触发 print(newsItems.count)
,结果为 0。但是一两秒后,回调处理程序将实际接收数据并开始将项目分配给 newsList
数组。
如何在实际接收到数据之前阻止数组赋值?我只是以完全错误的方式处理这件事吗?
XCODE 7 + Swift 2.0 + iOS 9.0.x
我的错误在于我使用闭包的方式。我依靠闭包在闭包外分配值,然后完成处理程序 return 这些值。
解法:
我删除了整个 NewsList class,它实际上对我所做的是多余的
我更改了视图控制器中的调用以执行以下操作
var newsItems = [NewsItem]() api.getLatestActivityData({JSONData, error -> Void in if (JSONData != nil) { dispatch_async(dispatch_get_main_queue(), { for (_, subJSON) in JSONData["data"] { let headline = subJSON["headline"].string! let summary = subJSON["summary"].string! let created = subJSON["created"].string! let source = subJSON["source"].string! let company = subJSON["company"].string! let companyLogo = subJSON["companyLogo"].string! let id = subJSON["id"].int! newsItems.append(NewsItem(headline: headline, summary: summary, created: created, source: source, company: company, companyLogoUrl: companyLogo, id: id)) } self.activityTableView.reloadData() }) } else { print("api data fetch failed") print(error) } })
现在我需要做的就是在 for 循环完成后,我应该能够通过重新加载来更新 table。
对于像我这样的 'newer' swift 用户,一些我不明白的东西昨天知道会有所帮助......当你调用 tableView.reloadData()
时,应用程序将触发所有tabieView 相关事件,如果你的数据源已经像我上面那样从闭包中更新,你的 cellForRowAtIndexPath
和 numberOfRowsInSection
将能够正确更新。只需在 cellForRowAtIndexPath
函数中分配您的数据。
希望这对某人有所帮助!