我正在将一些 HTML 转换为主线程上的 NSAttributedString(这是苹果告诉你该做的方式)。它需要一些时间,然后才会继续执行其他部分的代码块。
现在,如果另一个代码块也排队在主线程中运行(例如,在从 HTTP 请求获取响应后),我希望它在所有其他操作完成后再运行,但实际情况并非如此:它们以并行方式运行,就像它们在不同的线程上一样。我确保它们都在主线程上使用了 assert 语句。
我创建了一个名为 "Single View App" 的实验项目来测试这个问题,其中包含一个包含非常长的 html 字符串的文件,例如 "
现在,如果另一个代码块也排队在主线程中运行(例如,在从 HTTP 请求获取响应后),我希望它在所有其他操作完成后再运行,但实际情况并非如此:它们以并行方式运行,就像它们在不同的线程上一样。我确保它们都在主线程上使用了 assert 语句。
我创建了一个名为 "Single View App" 的实验项目来测试这个问题,其中包含一个包含非常长的 html 字符串的文件,例如 "
lorem
ipsum dolor sit amet",以及以下代码的视图控制器:import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
dispatchStuff()
for _ in 0..<10 {
// slowOperation()
parseHTML()
}
}
func dispatchStuff() {
for i in 0..<10 {
let wait = Double(i) * 0.2
DispatchQueue.main.asyncAfter(deadline: .now() + wait) {
assert(Thread.isMainThread, "not main thread!")
print(" dispatched after \(wait) seconds")
}
}
}
// just loads a big lorem ipsum full of html tags
let html: String = {
let filepath = Bundle.main.path(forResource: "test", ofType: "txt")!
return try! String(contentsOfFile: filepath)
}()
var n = 0
func slowOperation() {
n += 1
assert(Thread.isMainThread, "not main thread!")
print("slowOperation \(n) START")
var x = [0]
for i in 0..<10000 {
x.removeAll()
for j in 0..<i {
x.append(j)
}
}
print("slowOperation \(n) END")
print("")
}
var m = 0
func parseHTML() {
m += 1
assert(Thread.isMainThread, "not main thread!")
print("parseHTML \(m) START")
let options = [NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html]
let attrString = try! NSAttributedString(data: Data(html.utf8), options: options, documentAttributes: nil)
print("parseHTML \(m) END")
print("")
}
}
如果你运行它,控制台会显示以下内容:
所有内容都混杂在一起,这是令人惊讶的(对我来说)行为。
但是如果在 viewDidLoad()
中注释调用parseHTML()
并取消注释slowOperation()
, 你将得到像下面这样的结果:
这就是我所期望的。那么,这里发生了什么?我的线程工作方式的理解是不是完全错误?
NSAttributedString
初始化器必须要进行一些“运行循环”调用,这使得在本应该是同步过程中偶尔运行了一些排队的闭包。 - rmaddy