在dataTaskWithURL之后从闭包中分配数据导致nil值

时间:2015-09-30 21:09:08

标签: rest swift2 ios9 xcode7 nsurlsession

我为我的描述简单而道歉,我是Swift作为一种语言的新手。

我有一个REST api,它为我的应用程序提供了所有数据。我试图从JSON提要填充表视图。从本质上讲,我正在努力坚持我在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 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类中有以下方法

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

1 个答案:

答案 0 :(得分:0)

我的错误在于我如何使用闭包。我依赖于我的闭包来在闭包之外分配值,然后在完成处理程序中返回这些值。

<强>解决方案:

  1. 我删除了整个NewsList类,它实际上是多余的我正在做的事情

  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)
        }
    })
    
  3. 现在我需要做的就是一旦for循环完成,我应该可以通过重新加载来更新表。

    对于像我这样的'新'快速用户,我不明白哪些会帮助知道昨天...当你拨打tableView.reloadData()时,该应用会触发所有与tabieView相关的事件,如果你的数据我的cellForRowAtIndexPathnumberOfRowsInSection将能够正确更新,因此已从封闭更新源代码。只需在cellForRowAtIndexPath功能中分配您的数据。

    希望这有助于某人!