从包含闭包的 Swift 函数返回值

Posted

技术标签:

【中文标题】从包含闭包的 Swift 函数返回值【英文标题】:Returning a value from a Swift function containing a closure 【发布时间】:2015-09-24 10:06:13 【问题描述】:

我编写了一个应该返回值但该值来自闭包的函数。问题是如果我尝试从闭包内部返回一个值,它会将其视为完成处理程序的返回值。

private func loadData() throws -> [Item] 
    var items = [Item]()
    let jsonUrl = "http://api.openweathermap.org/data/2.5/forecast/daily?units=metric&cnt=7&q=coventry,uk"
    print(jsonUrl)
    let session = NSURLSession.sharedSession()
    guard let shotsUrl = NSURL(string: jsonUrl) else 
        throw JSONError.InvalidURL(jsonUrl)
    
    session.dataTaskWithURL(shotsUrl, completionHandler: (data, response, error) -> Void in
        do 
            let json = try NSJSONSerialization.JSONObjectWithData(data!, options: [])
            print(json)
            guard let days:[AnyObject] = (json["list"] as! [AnyObject]) else 
                throw JSONError.InvalidArray
            
            for day in days 
                guard let timestamp:Double = day["dt"] as? Double else 
                    throw JSONError.InvalidKey("dt")
                
                print(timestamp)
                let date = NSDate(timeIntervalSince1970: NSTimeInterval(timestamp))
                guard let weather:[AnyObject] = day["weather"] as? [AnyObject] else 
                    throw JSONError.InvalidArray
                
                guard let desc:String = weather[0]["description"] as? String else 
                    throw JSONError.InvalidKey("description")
                
                guard let icon:String = weather[0]["icon"] as? String else 
                    throw JSONError.InvalidKey("icon")
                
                guard let url = NSURL(string: "http://openweathermap.org/img/w/\(icon).png") else 
                    throw JSONError.InvalidURL("http://openweathermap.org/img/w/\(icon).png")
                
                guard let data = NSData(contentsOfURL: url) else 
                    throw JSONError.InvalidData
                
                guard let image = UIImage(data: data) else 
                    throw JSONError.InvalidImage
                
                guard let temp:AnyObject = day["temp"] else 
                    throw JSONError.InvalidKey("temp")
                
                guard let max:Float = temp["max"] as? Float else 
                    throw JSONError.InvalidKey("max")
                
                let newDay = Item(date: date, description: desc, maxTemp: max, icon: image)
                print(newDay)
                items.append(newDay)
            
            return items // this line fails because I'm in the closure. I want this to be the value returned by the loadData() function.
         catch 
            print("Fetch failed: \((error as NSError).localizedDescription)")
        
    )

【问题讨论】:

您可以将闭包作为回调或完成处理程序传递,您可以在要返回项目的地方调用它们。你需要写下你愿意如何处理传递给 loadData() 函数的闭包中的数据。 【参考方案1】:

将完成处理程序(在我的示例中名为 dataHandler)添加到您的 loadData 函数:

private func loadData(dataHandler: ([Item])->()) throws 
    var items = [Item]()
    let jsonUrl = "http://api.openweathermap.org/data/2.5/forecast/daily?units=metric&cnt=7&q=coventry,uk"
    print(jsonUrl)
    let session = NSURLSession.sharedSession()
    guard let shotsUrl = NSURL(string: jsonUrl) else 
        throw JSONError.InvalidURL(jsonUrl)
    
    session.dataTaskWithURL(shotsUrl, completionHandler: (data, response, error) -> Void in
        do 
            let json = try NSJSONSerialization.JSONObjectWithData(data!, options: [])
            print(json)
            guard let days:[AnyObject] = (json["list"] as! [AnyObject]) else 
                throw JSONError.InvalidArray
            
            for day in days 
                guard let timestamp:Double = day["dt"] as? Double else 
                    throw JSONError.InvalidKey("dt")
                
                print(timestamp)
                let date = NSDate(timeIntervalSince1970: NSTimeInterval(timestamp))
                guard let weather:[AnyObject] = day["weather"] as? [AnyObject] else 
                    throw JSONError.InvalidArray
                
                guard let desc:String = weather[0]["description"] as? String else 
                    throw JSONError.InvalidKey("description")
                
                guard let icon:String = weather[0]["icon"] as? String else 
                    throw JSONError.InvalidKey("icon")
                
                guard let url = NSURL(string: "http://openweathermap.org/img/w/\(icon).png") else 
                    throw JSONError.InvalidURL("http://openweathermap.org/img/w/\(icon).png")
                
                guard let data = NSData(contentsOfURL: url) else 
                    throw JSONError.InvalidData
                
                guard let image = UIImage(data: data) else 
                    throw JSONError.InvalidImage
                
                guard let temp:AnyObject = day["temp"] else 
                    throw JSONError.InvalidKey("temp")
                
                guard let max:Float = temp["max"] as? Float else 
                    throw JSONError.InvalidKey("max")
                
                let newDay = Item(date: date, description: desc, maxTemp: max, icon: image)
                print(newDay)
                items.append(newDay)
            
            dataHandler(items)
         catch 
            print("Fetch failed: \((error as NSError).localizedDescription)")
        
    ).resume()


do 
    try loadData  itemsArray in
        print(itemsArray)
    
 catch 
    print(error)

我已经在 Playground 中对其进行了测试,它可以正常工作:

【讨论】:

答案应该有效,但它会在操场上引发运行时错误(即使包括结构和错误枚举)。然后我尝试在一个类中公开该方法并从我的视图控制器中调用它。没有错误,但 session.dataTaskWithURL 回调无法运行。 我添加了我的 Playground 运行我的答案没有错误的屏幕截图。请看一看。 // 不要忘记,为了在 Playground 中运行异步代码,您需要启用 XCPSetExecutionShouldContinueIndefinitely! :) 抱歉,忘记添加 XCPSetExecutionShouldContinueIndefinitely(true)

以上是关于从包含闭包的 Swift 函数返回值的主要内容,如果未能解决你的问题,请参考以下文章

Swift学习笔记:闭包

Swift - 从闭包内返回变量

Swift函数和闭包

求助关于 swift 数组,NSArray 和 闭包 的问题

《从零开始学Swift》学习笔记(Day 22)——闭包那些事儿!

Swift的函数与函数指针闭包Closure等相关内容介绍