在我使用Swift 3.0(Beta 2)的OS X命令行工具项目中,我需要将HTML数据从多个URL转换为String。使用具有许多后台任务的此类函数存在一个问题(除了主线程之外它没有工作,所以可能有更优雅的方法来控制所有任务的完成并在有或没有解析器的情况下读取此类工具中的HTML数据我需要Swift 3和Mac OS X(Linux在不久的将来)):
func html2text (html: String, usedEncoding: String.Encoding) -> String {
let data = html.data(using: usedEncoding)!
if let htmlString = AttributedString(html: data, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: usedEncoding.rawValue], documentAttributes: nil)?.string {
return htmlString
} else {
return ""
}
}
所以我首先将数据读入一个数组,等待所有DataTasks完成然后在主线程中转换它。还使用全局变量(URL集)来控制每个任务的完成:
import Foundation
import WebKit
var urlArr = [String]()
var urlSet = Set<String>()
var htmlTup : [(url : String, html : String, encoding : String.Encoding)] = []
let session = URLSession.shared
具有多个URLSession DataTasks的for-in循环
for myurl in urlArr {
if urlSet.insert(myurl).inserted {
print ("Loading \(myurl)...")
let inputURL = URL(string: myurl)!
let task = session.dataTask(with: inputURL, completionHandler: {mydata, response, error in
从HTML开始读取编码
var usedEncoding = String.Encoding.utf8
if let encodingName = response!.textEncodingName {
let encoding = CFStringConvertIANACharSetNameToEncoding(encodingName)
if encoding != kCFStringEncodingInvalidId {
usedEncoding = String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding(encoding))
}
}
使用HTML String并将数据读入数组
if let myString = String(data: mydata!, encoding: usedEncoding) {
htmlTup += [(url: myurl,html: myString, encoding: usedEncoding)]
}
// The end of task removing URL from Set
urlSet.remove(myurl)
})
//Run Task
task.resume()
}
}
}
等待完成任务并将HTML转换为文本
while !urlSet.isEmpty {
// Do nothing
}
for (url,html,encoding) in htmlTup {
print ("Writing data from \(url)...")
print (html2text(html: html, usedEncoding: encoding))
}
来自this主线程的更新1: RunLoop 这样的代码用于检查每个任务的完成时间:
var taskArr = [Bool]()
let task = session.dataTask(with: request) { (data, response, error) in
}
taskArr.removeLast()
}
taskArr.append(true)
task.resume()
// Waiting for tasks to complete
let theRL = RunLoop.current
while !taskArr.isEmpty && theRL.run(mode: .defaultRunLoopMode, before: .distantFuture) { }
答案 0 :(得分:0)
你不能在繁忙的循环中旋转等待结果,因为你通过这样做来阻止主运行循环/线程/调度队列。
相反,返回该点,从而允许主运行循环运行。然后,在您的完成处理程序中,检查您是否已经获得了您期望的所有响应,如果是,请在忙碌等待循环之后执行您当前拥有的内容。