在 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 这些值。

解法:

  1. 我删除了整个 NewsList class,它实际上对我所做的是多余的

  2. 我更改了视图控制器中的调用以执行以下操作

    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 相关事件,如果你的数据源已经像我上面那样从闭包中更新,你的 cellForRowAtIndexPathnumberOfRowsInSection 将能够正确更新。只需在 cellForRowAtIndexPath 函数中分配您的数据。

希望这对某人有所帮助!