Return 来自异步线程的值
Return value from asynchronous thread
在 playground 文件中,我有一个函数应该 return 一个数组,其中 Lesson 是自定义数据类型。在我屏幕右侧的游乐场中,它确实显示了一个数组,其中包含应该在数组中的值,但如果我尝试将 returned 数组分配给一个变量以供进一步使用,该数组似乎为空。
我在网上读到这与 NSURLSession.sharedSession() 作为异步线程有关,所以我的数组在函数获取所有数据之前被 returned。
代码:
//
// CCApp
//
// Created by Milo Cesar on 02-03-15.
// Copyright (c) 2015 Experium. All rights reserved.
//
import Foundation
import XCPlayground
/*
Custom Data Types
Day: Used to easily transition from day to int
Lesson: Used to store all the necessary info to be displayed to the user
*/
//Day Enum
enum Day: Int{
case Maandag = 0, Dinsdag, Woensdag, Donderdag, Vrijdag
}
//Lesson Struct
struct Lesson {
var day:Day
var start:String
var end:String
var lesson:String
var room:String
var teacher:String
var groups:String
var type:String
var isBreak:Bool
var isCanceled:Bool
}
/*
Code from medium.com used to get & parse JSON
source: https://medium.com/swift-programming/learn-nsurlsession-using-swift-ebd80205f87c
*/
func httpGet(request: NSURLRequest!, callback: (String, String?) -> Void) {
var session = NSURLSession.sharedSession()
var task = session.dataTaskWithRequest(request){
(data, response, error) -> Void in
if error != nil {
callback("", error.localizedDescription)
} else {
var result = NSString(data: data, encoding:
NSASCIIStringEncoding)!
callback(result, nil)
}
}
task.resume()
}
func parseJSON(inputData: NSData) -> Array<AnyObject>{
var error: NSError?
var boardsDictionary = NSJSONSerialization.JSONObjectWithData(inputData, options: NSJSONReadingOptions.MutableContainers, error: &error) as Array<AnyObject>
return boardsDictionary
}
/*
Code from medium.com mixed with own input and adaptations.
*/
var request = NSMutableURLRequest(URL: NSURL(string: "http://api.ccapp.it/v1/student/110919/schedule/10")!)
func loadLesson() -> Array<Lesson>{
var loadedLesson:Array<Lesson> = []
//Get JSON Data using http://api.ccapp.it/v1/student/110919/schedule/10 as JSON location
httpGet(request) {
(data, error) -> Void in
//Check for Errors
if error != nil {
println(error)
} else {
//If there are no errors debug (print data) and transistion from JSON to Lesson Struct
println(data)
loadedLesson = setupLesson(data)
}
}
return loadedLesson
}
func setupLesson(data: String) -> Array<Lesson>{
//Clear Array when we get load a new schedule
var loadedLesson:Array<Lesson> = []
//Encode NSString to NSData
let jsonData = data.dataUsingEncoding(NSASCIIStringEncoding)
//parse NSData and return Array<AnyObject>
//AnyObject is here a Array<NSDictionary>
var jsonArray = parseJSON(jsonData!)
//Loop through the avaidable days in the schedule
for days in 0...jsonArray.count-1{
//Validate the before stated Array<NSDictionary>
if let day: AnyObject = jsonArray[days] as? Array<NSDictionary>{
//I think this line and the previous line should be able to be one line of code but I got no clue how so just cast it as a Array<NSDictionary> (again?)
if let dayArray:Array<NSDictionary> = day as AnyObject? as Array<NSDictionary>? {
//Loop through the lessons of the day
for lessonList in 0...dayArray.count-1{
//Get all values and store them in a local variable for easy access
var day:Day
var start:String = dayArray[lessonList].valueForKey("start") as String
var end:String = dayArray[lessonList].valueForKey("end") as String
var lessonID:String = dayArray[lessonList].valueForKey("lesson") as String
var room:String = dayArray[lessonList].valueForKey("room") as String
var teacher:String = dayArray[lessonList].valueForKey("teacher") as String
var groups:String = dayArray[lessonList].valueForKey("groups") as String
var type:String = dayArray[lessonList].valueForKey("type") as String
var isBreak:Bool = dayArray[lessonList].valueForKey("break") as Bool
var isCanceled:Bool = dayArray[lessonList].valueForKey("canceled") as Bool
//Create a new Lesson Value for easy storage
var lesson = Lesson(day: Day.Maandag, start:start, end:end, lesson:lessonID, room:room, teacher:teacher, groups:groups, type:type, isBreak:isBreak, isCanceled:isCanceled)
//Add the lesson to the array
loadedLesson.append(lesson)
}
}
}
}
//return the now filled array of lessons
return loadedLesson
}
//Receive a empty Array List
var lessonList = loadLesson()
lessonList.isEmpty
XCPSetExecutionShouldContinueIndefinitely(continueIndefinitely: true)
您需要将请求完成后要执行的所有内容放入完成块中。您在请求有机会完成之前返回 loadedLesson,因此它 returns 一个空数组。
处理此问题的一种方法是在加载数据时调用另一个函数。
例如:
func finishedLoading(array: Array<Lesson>) {
// Do something with the array
}
func loadLesson() {
//Get JSON Data using http://api.ccapp.it/v1/student/110919/schedule/10 as JSON location
httpGet(request) {
(data, error) -> Void in
//Check for Errors
if error != nil {
println(error)
} else {
//If there are no errors debug (print data) and transistion from JSON to Lesson Struct
println(data)
finishedLoading(setupLesson(data))
}
}
}
旁注:
您可以使用 ..<
而不是 for lessonList in 0...dayArray.count-1
,这将在数组结束前的 1 处停止迭代:
for lessonList in 0..<dayArray.count {
// inner loop
}
正确:您的 HTTP 请求是异步的,这意味着该请求在后台线程上运行,同时允许用户界面操作和其他任何操作在主线程上不受阻碍地继续 运行。为了获得结果数据,您可以将匿名回调函数作为参数传递给 loadLesson
,当请求已完全处理后,这将 return 您的数组:
func loadLesson(onComplete: ([Lesson]) -> ()) {
httpGet(request) {
(data, error) -> Void in
if error != nil {
println(error)
} else {
let lessons = setupLesson(data)
onComplete(lessons)
}
}
}
loadLesson { (lessons) in
for lesson in lessons {
println(lesson)
}
}
现在,您作为参数传递给 loadLesson
的块不会立即尝试访问您的数据,而是等待执行,直到从 httpGet
.
中调用它
在 playground 文件中,我有一个函数应该 return 一个数组,其中 Lesson 是自定义数据类型。在我屏幕右侧的游乐场中,它确实显示了一个数组,其中包含应该在数组中的值,但如果我尝试将 returned 数组分配给一个变量以供进一步使用,该数组似乎为空。
我在网上读到这与 NSURLSession.sharedSession() 作为异步线程有关,所以我的数组在函数获取所有数据之前被 returned。
代码:
//
// CCApp
//
// Created by Milo Cesar on 02-03-15.
// Copyright (c) 2015 Experium. All rights reserved.
//
import Foundation
import XCPlayground
/*
Custom Data Types
Day: Used to easily transition from day to int
Lesson: Used to store all the necessary info to be displayed to the user
*/
//Day Enum
enum Day: Int{
case Maandag = 0, Dinsdag, Woensdag, Donderdag, Vrijdag
}
//Lesson Struct
struct Lesson {
var day:Day
var start:String
var end:String
var lesson:String
var room:String
var teacher:String
var groups:String
var type:String
var isBreak:Bool
var isCanceled:Bool
}
/*
Code from medium.com used to get & parse JSON
source: https://medium.com/swift-programming/learn-nsurlsession-using-swift-ebd80205f87c
*/
func httpGet(request: NSURLRequest!, callback: (String, String?) -> Void) {
var session = NSURLSession.sharedSession()
var task = session.dataTaskWithRequest(request){
(data, response, error) -> Void in
if error != nil {
callback("", error.localizedDescription)
} else {
var result = NSString(data: data, encoding:
NSASCIIStringEncoding)!
callback(result, nil)
}
}
task.resume()
}
func parseJSON(inputData: NSData) -> Array<AnyObject>{
var error: NSError?
var boardsDictionary = NSJSONSerialization.JSONObjectWithData(inputData, options: NSJSONReadingOptions.MutableContainers, error: &error) as Array<AnyObject>
return boardsDictionary
}
/*
Code from medium.com mixed with own input and adaptations.
*/
var request = NSMutableURLRequest(URL: NSURL(string: "http://api.ccapp.it/v1/student/110919/schedule/10")!)
func loadLesson() -> Array<Lesson>{
var loadedLesson:Array<Lesson> = []
//Get JSON Data using http://api.ccapp.it/v1/student/110919/schedule/10 as JSON location
httpGet(request) {
(data, error) -> Void in
//Check for Errors
if error != nil {
println(error)
} else {
//If there are no errors debug (print data) and transistion from JSON to Lesson Struct
println(data)
loadedLesson = setupLesson(data)
}
}
return loadedLesson
}
func setupLesson(data: String) -> Array<Lesson>{
//Clear Array when we get load a new schedule
var loadedLesson:Array<Lesson> = []
//Encode NSString to NSData
let jsonData = data.dataUsingEncoding(NSASCIIStringEncoding)
//parse NSData and return Array<AnyObject>
//AnyObject is here a Array<NSDictionary>
var jsonArray = parseJSON(jsonData!)
//Loop through the avaidable days in the schedule
for days in 0...jsonArray.count-1{
//Validate the before stated Array<NSDictionary>
if let day: AnyObject = jsonArray[days] as? Array<NSDictionary>{
//I think this line and the previous line should be able to be one line of code but I got no clue how so just cast it as a Array<NSDictionary> (again?)
if let dayArray:Array<NSDictionary> = day as AnyObject? as Array<NSDictionary>? {
//Loop through the lessons of the day
for lessonList in 0...dayArray.count-1{
//Get all values and store them in a local variable for easy access
var day:Day
var start:String = dayArray[lessonList].valueForKey("start") as String
var end:String = dayArray[lessonList].valueForKey("end") as String
var lessonID:String = dayArray[lessonList].valueForKey("lesson") as String
var room:String = dayArray[lessonList].valueForKey("room") as String
var teacher:String = dayArray[lessonList].valueForKey("teacher") as String
var groups:String = dayArray[lessonList].valueForKey("groups") as String
var type:String = dayArray[lessonList].valueForKey("type") as String
var isBreak:Bool = dayArray[lessonList].valueForKey("break") as Bool
var isCanceled:Bool = dayArray[lessonList].valueForKey("canceled") as Bool
//Create a new Lesson Value for easy storage
var lesson = Lesson(day: Day.Maandag, start:start, end:end, lesson:lessonID, room:room, teacher:teacher, groups:groups, type:type, isBreak:isBreak, isCanceled:isCanceled)
//Add the lesson to the array
loadedLesson.append(lesson)
}
}
}
}
//return the now filled array of lessons
return loadedLesson
}
//Receive a empty Array List
var lessonList = loadLesson()
lessonList.isEmpty
XCPSetExecutionShouldContinueIndefinitely(continueIndefinitely: true)
您需要将请求完成后要执行的所有内容放入完成块中。您在请求有机会完成之前返回 loadedLesson,因此它 returns 一个空数组。
处理此问题的一种方法是在加载数据时调用另一个函数。
例如:
func finishedLoading(array: Array<Lesson>) {
// Do something with the array
}
func loadLesson() {
//Get JSON Data using http://api.ccapp.it/v1/student/110919/schedule/10 as JSON location
httpGet(request) {
(data, error) -> Void in
//Check for Errors
if error != nil {
println(error)
} else {
//If there are no errors debug (print data) and transistion from JSON to Lesson Struct
println(data)
finishedLoading(setupLesson(data))
}
}
}
旁注:
您可以使用 ..<
而不是 for lessonList in 0...dayArray.count-1
,这将在数组结束前的 1 处停止迭代:
for lessonList in 0..<dayArray.count {
// inner loop
}
正确:您的 HTTP 请求是异步的,这意味着该请求在后台线程上运行,同时允许用户界面操作和其他任何操作在主线程上不受阻碍地继续 运行。为了获得结果数据,您可以将匿名回调函数作为参数传递给 loadLesson
,当请求已完全处理后,这将 return 您的数组:
func loadLesson(onComplete: ([Lesson]) -> ()) {
httpGet(request) {
(data, error) -> Void in
if error != nil {
println(error)
} else {
let lessons = setupLesson(data)
onComplete(lessons)
}
}
}
loadLesson { (lessons) in
for lesson in lessons {
println(lesson)
}
}
现在,您作为参数传递给 loadLesson
的块不会立即尝试访问您的数据,而是等待执行,直到从 httpGet
.