在我的使用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 ""
    }
}


因此,我首先将数据读取到Array中,等待所有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字符串进行一些处理并将数据读入数组

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))
}


更新1:this在主线程中运行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) { }

最佳答案

您不能只是陷入忙碌的循环中等待结果,因为这样做会阻塞主运行循环/线程/调度队列。

而是在该点返回,从而允许主运行循环运行。然后,在完成处理程序中,检查是否已经获得了所有期望的响应,如果是,请在忙碌的while循环之后执行当前要执行的操作。

08-04 03:15