SwiftUI 解码 HTML 实体错误:同时访问 0x7ff43ff29b50,但修改需要独占访问

问题描述

当我使用 NSAttributedString 解码 html 实体时出现错误。我使用 Swift 5。我只想要 html 到字符串。我不想要网页视图。

SwiftUI 文本

Text("It&#39s a party!".decoded)

字符串扩展

extension String {
    var decoded: String {
        let attr = try? NSAttributedString(data: Data(utf8),options: [
        .documentType: NSAttributedString.DocumentType.html,.characterEncoding: String.Encoding.utf8.rawValue
    ],documentAttributes: nil)

        return attr?.string ?? self
    }
}

错误日志

== AttributeGraph: cycle detected through attribute 123464 ===
=== AttributeGraph: cycle detected through attribute 143224 ===
=== AttributeGraph: cycle detected through attribute 128744 ===
Simultaneous accesses to 0x7ff43ff29b50,but modification requires exclusive access.
PrevIoUs access (a modification) started at SwiftUI`LayoutComputer.EngineDelegate.spacing() + 44 (0x7fff566b85dc).
Current access (a modification) started at:
0    libswiftCore.dylib                 0x00007fff2f41fe90 swift_beginAccess + 568
1    SwiftUI                            0x00007fff566b85b0 LayoutComputer.EngineDelegate.spacing() + 44
2    SwiftUI                            0x00007fff56657140 accumulateSpacing #1 (ofChild:) in StackLayout.Header.init(layoutContext:proxies:majorAxis:minorAxisAlignment:uniformSpacing:childStorage:capacity:resizeChildrenWithTrailingOverflow:) + 289
3    SwiftUI                            0x00007fff56656bf0 StackLayout.Header.init(layoutContext:proxies:majorAxis:minorAxisAlignment:uniformSpacing:childStorage:capacity:resizeChildrenWithTrailingOverflow:) + 414
4    SwiftUI                            0x00007fff561d9d00 specialized ManagedBufferPointer.init(bufferClass:minimumCapacity:makingHeaderWith:) + 296
5    SwiftUI                            0x00007fff561da140 specialized closure #2 in Hvstack.updateLayoutComputer<A>(rule:layoutContext:children:) + 142
6    SwiftUI                            0x00007fff5626cce0 specialized closure #2 in Hvstack.updateLayoutComputer<A>(rule:layoutContext:children:) + 41
7    SwiftUI                            0x00007fff56278590 partial apply for specialized closure #2 in Hvstack.updateLayoutComputer<A>(rule:layoutContext:children:) + 43
8    SwiftUI                            0x00007fff561da3e0 specialized static LayoutComputerDelegate.update<A>(_:maybeInPlace:create:) + 136
9    SwiftUI                            0x00007fff56139060 specialized StatefulRule<>.updateLayoutComputer<A>(layout:environment:layoutComputers:) + 176
10   SwiftUI                            0x00007fff562692a0 specialized implicit closure #2 in implicit closure #1 in closure #1 in closure #1 in Attribute.init<A>(_:) + 236
11   AttributeGraph                     0x00007fff4be8952a AG::Graph::UpdateStack::update() + 505
12   AttributeGraph                     0x00007fff4be89a6a AG::Graph::update_attribute(AG::data::ptr<AG::Node>,bool) + 335
13   AttributeGraph                     0x00007fff4be8e654 AG::Graph::input_value_ref_slow(AG::data::ptr<AG::Node>,AG::AttributeID,unsigned int,AGSwiftMetadata const*,bool*,long) + 523
14   AttributeGraph                     0x00007fff4be9fcfa AGGraphGetValue + 203
15   SwiftUI                            0x00007fff56030ec0 specialized implicit closure #2 in implicit closure #1 in closure #1 in closure #1 in Attribute.init<A>(_:) + 32
16   AttributeGraph                     0x00007fff4be8952a AG::Graph::UpdateStack::update() + 505
17   AttributeGraph                     0x00007fff4be89a6a AG::Graph::update_attribute(AG::data::ptr<AG::Node>,bool) + 335
18   AttributeGraph                     0x00007fff4be8e654 AG::Graph::input_value_ref_slow(AG::data::ptr<AG::Node>,long) + 523
19   AttributeGraph                     0x00007fff4be9fcfa AGGraphGetValue + 203
20   SwiftUI                            0x00007fff56438660 DynamicLayoutViewChildGeometry.childGeometries.getter + 53
21   SwiftUI                            0x00007fff56438740 DynamicLayoutViewChildGeometry.updateValue() + 201
22   SwiftUI                            0x00007fff5626e730 partial apply for specialized implicit closure #2 in implicit closure #1 in closure #1 in closure #1 in Attribute.init<A>(_:) + 15
23   AttributeGraph                     0x00007fff4be8952a AG::Graph::UpdateStack::update() + 505
24   AttributeGraph                     0x00007fff4be89a6a AG::Graph::update_attribute(AG::data::ptr<AG::Node>,bool) + 335
25   AttributeGraph                     0x00007fff4be8e654 AG::Graph::input_value_ref_slow(AG::data::ptr<AG::Node>,long) + 523
26   AttributeGraph                     0x00007fff4be9fcfa AGGraphGetValue + 203
27   SwiftUI                            0x00007fff55f66120 specialized UnaryChildGeometry.parentSize.getter + 28
28   SwiftUI                            0x00007fff55f66920 specialized UnaryChildGeometry.value.getter + 91
29   SwiftUI                            0x00007fff56035830 specialized implicit closure #2 in implicit closure #1 in closure #1 in closure #1 in Attribute.init<A>(_:) + 28
30   SwiftUI                            0x00007fff5605a690 partial apply for specialized implicit closure #2 in implicit closure #1 in closure #1 in closure #1 in Attribute.init<A>(_:) + 20
31   AttributeGraph                     0x00007fff4be8952a AG::Graph::UpdateStack::update() + 505
32   AttributeGraph                     0x00007fff4be89a6a AG::Graph::update_attribute(AG::data::ptr<AG::Node>,bool) + 335
33   AttributeGraph                     0x00007fff4be8e654 AG::Graph::input_value_ref_slow(AG::data::ptr<AG::Node>,long) + 523
34   AttributeGraph                     0x00007fff4be9fcfa AGGraphGetValue + 203
35   SwiftUI                            0x00007fff55f66120 specialized UnaryChildGeometry.parentSize.getter + 28
36   SwiftUI                            0x00007fff55f666b0 specialized UnaryChildGeometry.value.getter + 91
37   SwiftUI                            0x00007fff56035830 specialized implicit closure #2 in implicit closure #1 in closure #1 in closure #1 in Attribute.init<A>(_:) + 28
38   SwiftUI                            0x00007fff56040ed0 partial apply for specialized implicit closure #2 in implicit closure #1 in closure #1 in closure #1 in Attribute.init<A>(_:) + 20
39   AttributeGraph                     0x00007fff4be8952a AG::Graph::UpdateStack::update() + 505
40   AttributeGraph                     0x00007fff4be89a6a AG::Graph::update_attribute(AG::data::ptr<AG::Node>,bool) + 335
41   AttributeGraph                     0x00007fff4be8e654 AG::Graph::input_value_ref_slow(AG::data::ptr<AG::Node>,long) + 523
42   AttributeGraph                     0x00007fff4be9fcfa AGGraphGetValue + 203
43   SwiftUI                            0x00007fff55f66120 specialized UnaryChildGeometry.parentSize.getter + 28
44   SwiftUI                            0x00007fff55f666b0 specialized UnaryChildGeometry.value.getter + 91
45   SwiftUI                            0x00007fff56035830 specialized implicit closure #2 in implicit closure #1 in closure #1 in closure #1 in Attribute.init<A>(_:) + 28
46   SwiftUI                            0x00007fff56040ed0 partial apply for specialized implicit closure #2 in implicit closure #1 in closure #1 in closure #1 in Attribute.init<A>(_:) + 20
47   AttributeGraph                     0x00007fff4be8952a AG::Graph::UpdateStack::update() + 505
48   AttributeGraph                     0x00007fff4be89a6a AG::Graph::update_attribute(AG::data::ptr<AG::Node>,bool) + 335
49   AttributeGraph                     0x00007fff4be8e654 AG::Graph::input_value_ref_slow(AG::data::ptr<AG::Node>,long) + 523
50   AttributeGraph                     0x00007fff4be9fcfa AGGraphGetValue + 203
51   SwiftUI                            0x00007fff55f66120 specialized UnaryChildGeometry.parentSize.getter + 28
52   SwiftUI                            0x00007fff55f66440 specialized UnaryChildGeometry.value.getter + 91
53   SwiftUI                            0x00007fff56035830 specialized implicit closure #2 in implicit closure #1 in closure #1 in closure #1 in Attribute.init<A>(_:) + 28
54   SwiftUI                            0x00007fff5604f800 partial apply for specialized implicit closure #2 in implicit closure #1 in closure #1 in closure #1 in Attribute.init<A>(_:) + 20
55   AttributeGraph                     0x00007fff4be8952a AG::Graph::UpdateStack::update() + 505
56   AttributeGraph                     0x00007fff4be89a6a AG::Graph::update_attribute(AG::data::ptr<AG::Node>,bool) + 335
57   AttributeGraph                     0x00007fff4be8e654 AG::Graph::input_value_ref_slow(AG::data::ptr<AG::Node>,long) + 523
58   AttributeGraph                     0x00007fff4be9fcfa AGGraphGetValue + 203
59   SwiftUI                            0x00007fff55f66120 specialized UnaryChildGeometry.parentSize.getter + 28
60   SwiftUI                            0x00007fff55f666b0 specialized UnaryChildGeometry.value.getter + 91
61   SwiftUI                            0x00007fff56035830 specialized implicit closure #2 in implicit closure #1 in closure #1 in closure #1 in Attribute.init<A>(_:) + 28
62   SwiftUI                            0x00007fff56040ed0 partial apply for specialized implicit closure #2 in implicit closure #1 in closure #1 in closure #1 in Attribute.init<A>(_:) + 20
63   AttributeGraph                     0x00007fff4be8952a AG::Graph::UpdateStack::update() + 505
64   AttributeGraph                     0x00007fff4be89a6a AG::Graph::update_attribute(AG::data::ptr<AG::Node>,bool) + 335
65   AttributeGraph                     0x00007fff4be8e654 AG::Graph::input_value_ref_slow(AG::data::ptr<AG::Node>,long) + 523
66   AttributeGraph                     0x00007fff4be9fcfa AGGraphGetValue + 203
67   SwiftUI                            0x00007fff56030ec0 specialized implicit closure #2 in implicit closure #1 in closure #1 in closure #1 in Attribute.init<A>(_:) + 55
68   AttributeGraph                     0x00007fff4be8952a AG::Graph::UpdateStack::update() + 505
69   AttributeGraph                     0x00007fff4be89a6a AG::Graph::update_attribute(AG::data::ptr<AG::Node>,bool) + 335
70   AttributeGraph                     0x00007fff4be8e654 AG::Graph::input_value_ref_slow(AG::data::ptr<AG::Node>,long) + 523
71   AttributeGraph                     0x00007fff4be9fcfa AGGraphGetValue + 203
72   SwiftUI                            0x00007fff56438660 DynamicLayoutViewChildGeometry.childGeometries.getter + 53
73   SwiftUI                            0x00007fff56438740 DynamicLayoutViewChildGeometry.updateValue() + 201
74   SwiftUI                            0x00007fff5626e730 partial apply for specialized implicit closure #2 in implicit closure #1 in closure #1 in closure #1 in Attribute.init<A>(_:) + 15
75   AttributeGraph                     0x00007fff4be8952a AG::Graph::UpdateStack::update() + 505
76   AttributeGraph                     0x00007fff4be89a6a AG::Graph::update_attribute(AG::data::ptr<AG::Node>,bool) + 335
77   AttributeGraph                     0x00007fff4be8e654 AG::Graph::input_value_ref_slow(AG::data::ptr<AG::Node>,long) + 523
78   AttributeGraph                     0x00007fff4be9fcfa AGGraphGetValue + 203
79   SwiftUI                            0x00007fff56030ec0 specialized implicit closure #2 in implicit closure #1 in closure #1 in closure #1 in Attribute.init<A>(_:) + 55
80   AttributeGraph                     0x00007fff4be8952a AG::Graph::UpdateStack::update() + 505
81   AttributeGraph                     0x00007fff4be89a6a AG::Graph::update_attribute(AG::data::ptr<AG::Node>,bool) + 335
82   AttributeGraph                     0x00007fff4be8e654 AG::Graph::input_value_ref_slow(AG::data::ptr<AG::Node>,long) + 523
83   AttributeGraph                     0x00007fff4be9fcfa AGGraphGetValue + 203
84   SwiftUI                            0x00007fff56033ef0 specialized implicit closure #2 in implicit closure #1 in closure #1 in closure #1 in Attribute.init<A>(_:) + 63
85   AttributeGraph                     0x00007fff4be8952a AG::Graph::UpdateStack::update() + 505
86   AttributeGraph                     0x00007fff4be89a6a AG::Graph::update_attribute(AG::data::ptr<AG::Node>,bool) + 335
87   AttributeGraph                     0x00007fff4be8e654 AG::Graph::input_value_ref_slow(AG::data::ptr<AG::Node>,long) + 523
88   AttributeGraph                     0x00007fff4be9fcfa AGGraphGetValue + 203
89   SwiftUI                            0x00007fff568536f0 StyledTextChildGeometry.parentSize.getter + 27
90   SwiftUI                            0x00007fff560324b0 specialized implicit closure #2 in implicit closure #1 in closure #1 in closure #1 in Attribute.init<A>(_:) + 41
91   AttributeGraph                     0x00007fff4be8952a AG::Graph::UpdateStack::update() + 505
92   AttributeGraph                     0x00007fff4be89a6a AG::Graph::update_attribute(AG::data::ptr<AG::Node>,bool) + 335
93   AttributeGraph                     0x00007fff4be8e654 AG::Graph::input_value_ref_slow(AG::data::ptr<AG::Node>,long) + 523
94   AttributeGraph                     0x00007fff4be9fcfa AGGraphGetValue + 203
95   SwiftUI                            0x00007fff5608cb10 LayoutPositionQuery.localPosition.getter + 22
96   SwiftUI                            0x00007fff5608cbd0 LayoutPositionQuery.updateValue() + 32
97   SwiftUI                            0x00007fff5628dab0 partial apply for specialized implicit closure #2 in implicit closure #1 in closure #1 in closure #1 in Attribute.init<A>(_:) + 15
98   AttributeGraph                     0x00007fff4be8952a AG::Graph::UpdateStack::update() + 505
99   AttributeGraph                     0x00007fff4be89a6a AG::Graph::update_attribute(AG::data::ptr<AG::Node>,bool) + 335
100  AttributeGraph                     0x00007fff4be8e654 AG::Graph::input_value_ref_slow(AG::data::ptr<AG::Node>,long) + 523
101  AttributeGraph                     0x00007fff4be9fcfa AGGraphGetValue + 203
102  SwiftUI                            0x00007fff568cd9e0 AnimatableFrameAttribute.updateValue() + 45
103  SwiftUI                            0x00007fff5628e5c0 partial apply for specialized implicit closure #2 in implicit closure #1 in closure #1 in closure #1 in Attribute.init<A>(_:) + 15
104  AttributeGraph                     0x00007fff4be8952a AG::Graph::UpdateStack::update() + 505
105  AttributeGraph                     0x00007fff4be89a6a AG::Graph::update_attribute(AG::data::ptr<AG::Node>,bool) + 335
106  AttributeGraph                     0x00007fff4be91884 AG::Subgraph::update(unsigned int) + 781
107  SwiftUI                            0x00007fff5693a5f0 GraphHost.runTransaction() + 186
108  SwiftUI                            0x00007fff5640e3c0 ViewGraph.updateOutputs(at:) + 90
109  SwiftUI                            0x00007fff5689a880 closure #1 in VieWrendererHost.render(interval:updatedisplayList:) + 1305
110  SwiftUI                            0x00007fff5688dc20 VieWrendererHost.render(interval:updatedisplayList:) + 340
111  SwiftUI                            0x00007fff56a0a640 _UIHostingView.layoutSubviews() + 241
112  SwiftUI                            0x00007fff56a0a740 @objc _UIHostingView.layoutSubviews() + 21
113  UIKitCore                          0x00007fff24bd69a0 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 2924
114  QuartzCore                         0x00007fff27a7708d -[CALayer layoutSublayers] + 258
115  QuartzCore                         0x00007fff27a7d402 CA::Layer::layout_if_needed(CA::Transaction*) + 575
116  QuartzCore                         0x00007fff27a89358 CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 65
117  QuartzCore                         0x00007fff279c8f24 CA::Context::commit_transaction(CA::Transaction*,double,double*) + 496
118  QuartzCore                         0x00007fff279ffba0 CA::Transaction::commit() + 783
119  QuartzCore                         0x00007fff27a0101c CA::Transaction::observer_callback(__CFRunLoopObserver*,unsigned long,void*) + 79
120  CoreFoundation                     0x00007fff2038b1d1 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23
121  CoreFoundation                     0x00007fff20385844 __CFRunLoopDoObservers + 547
122  CoreFoundation                     0x00007fff2038548f CFRunLoopRunSpecific + 691
123  UIFoundation                       0x00007fff23a483bd -[NSHTMLReader _loadUsingWebKit] + 1847
124  UIFoundation                       0x00007fff23a49a13 -[NSHTMLReader attributedString] + 22
125  UIFoundation                       0x00007fff239c835a _NSReadAttributedStringFromURLOrData + 9439
126  UIFoundation                       0x00007fff239c8255 -[NSAttributedString(NSAttributedStringUIFoundationAdditions) initWithData:options:documentAttributes:error:] + 144
(lldb) 

解决方法

NSAttributedString 在解析 HTML 时处理运行循环。您可以在对 initWithData: 的调用中对 CFRUnLoopRunSpecific 的调用中看到它。这是 NSAttributedString 的一个非常古老的问题。 (我想我第一次遇到它是在 OS X 10.5 左右,但我确定它比那个更旧。)

因为它处理运行循环,所以在评估 HTML 字符串的过程中可能会发生各种各样的事情。计时器可以触发。可以调用延迟选择器。而在 SwiftUI 中,这意味着 UI 可能会尝试更新。一团糟。它会在明显安全的代码中生成竞争条件。你真的有点幸运,Swift 发现了这种问题并崩溃了。其他常见的症状甚至更难调试(由于代码重入导致的“不可能”死锁是我遇到的最常见的情况)。

简短的回答是使用 NSAttributedString 同步评估 HTML 是不安全的。文档不会就此向您发出警告,并且该方法的名称也没有给出任何提示。但你不能。有些人会告诉你只需要确保在主线程上评估它,但即使这样也不能保证如果你在运行循环中还有其他待处理的东西,你不会有真正奇怪的重入错误。

您需要以另一种方式评估此字符串。例如,参见 Martin R's answerHow do I decode HTML entities in Swift?

有关即使在主线程上运行它也会给您带来麻烦的快速示例,请考虑以下代码:

func delayed() {
    print("Should be last")
}

func dothing() {
    // Run this on the next runloop
    DispatchQueue.main.async { self.delayed() }

    // These should print in order
    print("Prints first")
    print("It&#39s a party!".decoded)
}

// somewhere on the main queue. There's no background threads needed
dothing()

输出:

Prints first
2020-12-20 22:16:07.976101-0500 test[84698:5693517] [plugin] AddInstanceForFactory: No factory registered for id <CFUUID 0x600000bb8520> F8BB1C28-BAE8-11D6-9C31-00039315CD46
Should be last
It's a party!

如果 delayed 改变任何状态,那么即使一切都在主线程上,事情可能会发生变化。

(出现奇怪的错误是因为我在 didFinishLaunching 中对此进行了测试,这意味着 runloop 在应用完成启动之前得到处理。这正是您遇到的错误类型。)