【问题标题】:Swift: Crash on removeFromSuperview()斯威夫特:在 removeFromSuperview() 上崩溃
【发布时间】:2017-04-24 13:42:59
【问题描述】:

问题:当我尝试删除子视图时应用程序崩溃。

上下文:我有一个 UICollectionViewCell,它使用模型对象进行更新。我已经尝试将这些方法放入 prepareForReuse 和/或 update(model: viewModel) 方法中。

注意:That view's superview: NO SUPERVIEW 行突出。我在调用 removeFromSuperView() 之前将子视图重新添加到其中,以确保在 superView 中有一些并且它仍然会崩溃。

我稍后会在清除所有旧实例后重新添加新生成的 timelineView 类实例。

它只会在滚动大约 50-150 个单元格后崩溃

更新:感谢您在 cmets 中的反馈。我不会从单元格中删除所有视图。只有一个带有可变数量组件的stackview。但是您有权删除子视图与取消。我更好奇stackView.subviews.first?.removeFromSuperview() 之类的事情是如何失败的,因为这似乎是一个相对安全的调用(或者我对此完全错了)。

第一次尝试

for subview in stackView.subviews {
    subview.removeFromSuperview() // Crash
}

第二次尝试

let subviews = stackView.subviews
for subview in subviews {
    if stackView != nil && subview.superview != nil && subview.responds(to: #selector(removeFromSuperview)) && stackView.superview != nil {
            stackView.subviews.first?.removeFromSuperview() // Crash
    }
}

第三次尝试

let subviews = stackView.subviews
for subview in subviews {
    if stackView != nil && subview.superview != nil && subview.responds(to:#selector(removeFromSuperview)) && stackView.superview != nil {
        if var firstSubview = stackView.subviews.first {
            for constraint in firstSubview.constraints {
                constraint.isActive = false
            }
            stackView.removeArrangedSubview(firstSubview)
            firstSubview.removeFromSuperview()  // Crash
        }
    }
}

例外:

    2016-12-09 00:56:26.792168 appName[59081:4139912] [LayoutConstraints] View hierarchy unprepared for constraint.
    Constraint: <NSLayoutConstraint:0x618000490cc0 'UISV-canvas-connection' UIStackView:0x7fe25cd25dd0.bottom == appName.timeLineView:0x7fe25e007890.bottom   (active)>
    Container hierarchy: 
    <UIStackView: 0x7fe25cd25dd0; frame = (0 0; 718 40); opaque = NO; autoresize = RM+BM; layer = <CATransformLayer: 0x6080002295a0>>
    View not found in container hierarchy: <appName.timeLineView: 0x7fe25e007890; frame = (0 0; 718 40); autoresize = W+H; layer = <CALayer: 0x610000835c40>>
    That view's superview: NO SUPERVIEW
2016-12-09 00:58:38.655 appName[59081:4139912] *** Terminating app due to uncaught exception 'NSGenericException', reason: 'Unable to install constraint on view.  Does the constraint reference something from outside the subtree of the view?  That's illegal. constraint:<NSLayoutConstraint:0x618000490cc0 'UISV-canvas-connection' UIStackView:0x7fe25cd25dd0.bottom == appName.timeLineView:0x7fe25e007890.bottom   (active)> view:<UIStackView: 0x7fe25cd25dd0; frame = (0 0; 718 40); opaque = NO; autoresize = RM+BM; layer = <CATransformLayer: 0x6080002295a0>>'
*** First throw call stack:
(
    0   CoreFoundation                      0x000000010df0434b __exceptionPreprocess + 171
    1   libobjc.A.dylib                     0x000000011215121e objc_exception_throw + 48
    2   CoreFoundation                      0x000000010df6d265 +[NSException raise:format:] + 197
    3   Foundation                          0x000000010f097865 -[NSLayoutConstraint _addToEngine:integralizationAdjustment:mutuallyExclusiveConstraints:] + 200
    4   Foundation                          0x000000010f27c6ed __36-[NSISEngine rebuildFromConstraints]_block_invoke + 208
    5   Foundation                          0x000000010f09dbd0 -[NSISEngine withBehaviors:performModifications:] + 155
    6   Foundation                          0x000000010f27c610 -[NSISEngine rebuildFromConstraints] + 488
    7   Foundation                          0x000000010f0a3e1a -[NSISEngine optimize] + 121
    8   Foundation                          0x000000010f27c883 -[NSISEngine _optimizeIfNotDisabled] + 57
    9   Foundation                          0x000000010f0a4ec3 -[NSISEngine removeConstraintWithMarker:] + 799
    10  Foundation                          0x000000010f09bca8 -[NSLayoutConstraint _removeFromEngine:] + 229
    11  UIKit                               0x00000001112314a0 -[UIView(UIConstraintBasedLayout) _layoutEngine_willRemoveLayoutConstraint:] + 105
    12  UIKit                               0x0000000111231bb1 -[UIView(UIConstraintBasedLayout) nsli_removeConstraint:] + 93
    13  UIKit                               0x0000000111243cef _UIViewRemoveConstraintsMadeDanglyByChangingSuperview + 1016
    14  UIKit                               0x000000011091965a __45-[UIView(Hierarchy) _postMovedFromSuperview:]_block_invoke + 61
    15  UIKit                               0x00000001109195a2 -[UIView(Hierarchy) _postMovedFromSuperview:] + 857
    16  UIKit                               0x000000011091721c __UIViewWasRemovedFromSuperview + 172
    17  UIKit                               0x0000000110916d13 -[UIView(Hierarchy) removeFromSuperview] + 564
    18  appName                          0x0000000109aad884 _TFC10appName22TestCollectionViewCell15prepareForReusefT_T_ + 3380
    19  appName                          0x0000000109aada42 _TToFC10appName22TestCollectionViewCell15prepareForReusefT_T_ + 34
    20  UIKit                               0x00000001111bee14 -[UICollectionView _dequeueReusableViewOfKind:withIdentifier:forIndexPath:viewCategory:] + 561
    21  UIKit                               0x00000001111bf834 -[UICollectionView dequeueReusableCellWithReuseIdentifier:forIndexPath:] + 169
    22  appName                          0x0000000109a83623 _TFC10appName30FlightsViewController14collectionViewfTCSo16UICollectionView13cellForItemAtV10Foundation9IndexPath_CSo20UICollectionViewCell + 339
    23  appName                          0x0000000109a83ca7 _TToFC10appName30FlightsViewController14collectionViewfTCSo16UICollectionView13cellForItemAtV10Foundation9IndexPath_CSo20UICollectionViewCell + 87
    24  UIKit                               0x00000001111aa980 -[UICollectionView _createPreparedCellForItemAtIndexPath:withLayoutAttributes:applyAttributes:isFocused:notify:] + 467
    25  UIKit                               0x00000001111aa7a7 -[UICollectionView _createPreparedCellForItemAtIndexPath:withLayoutAttributes:applyAttributes:] + 35
    26  UIKit                               0x00000001111afc7f -[UICollectionView _updateVisibleCellsNow:] + 4803
    27  UIKit                               0x00000001111b5913 -[UICollectionView layoutSubviews] + 313
    28  UIKit                               0x000000011092cf50 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1237
    29  QuartzCore                          0x00000001102c9cc4 -[CALayer layoutSublayers] + 146
    30  QuartzCore                          0x00000001102bd788 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 366
    31  QuartzCore                          0x00000001102bd606 _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 24
    32  QuartzCore                          0x000000011024b680 _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 280
    33  QuartzCore                          0x0000000110278767 _ZN2CA11Transaction6commitEv + 475
    34  QuartzCore                          0x00000001101d3b97 _ZN2CA7Display11DisplayLink14dispatch_itemsEyyy + 611
    35  CoreFoundation                      0x000000010de96964 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 20
    36  CoreFoundation                      0x000000010de965f3 __CFRunLoopDoTimer + 1075
    37  CoreFoundation                      0x000000010de9617a __CFRunLoopDoTimers + 250
    38  CoreFoundation                      0x000000010de8df01 __CFRunLoopRun + 2065
    39  CoreFoundation                      0x000000010de8d494 CFRunLoopRunSpecific + 420
    40  GraphicsServices                    0x00000001144e9a6f GSEventRunModal + 161
    41  UIKit                               0x0000000110868964 UIApplicationMain + 159
    42  appName                          0x0000000109a2ba9f main + 111
    43  libdyld.dylib                       0x00000001133b468d start + 1
)

谢谢

【问题讨论】:

  • 兄弟检查你的约束,当你从超级视图中删除它的约束问题时,你的子视图会崩溃
  • 你能把你那里的所有东西都换掉吗?删除单元格中的子视图真的很慢,只需将该内容设置为 nil 并在需要时重新设置
  • 你为什么要删除子视图???
  • 如果您只需要更新单元格,则需要使用新内容更新视图。您无需删除视图并再次添加它们。我认为,崩溃的发生是因为您删除了不在屏幕上的单元格子视图,而是在重用池中。
  • 现在解决了吗?

标签: ios swift autolayout nslayoutconstraint uistackview


【解决方案1】:

我正在这样做,它似乎比拥有 3 个独立的循环效果更好

extension UIStackView {
    func removeAllArrangedSubviews() {
            arrangedSubviews.forEach {
                self.removeArrangedSubview($0)
                NSLayoutConstraint.deactivate($0.constraints)
                $0.removeFromSuperview()
            }
        }
}

【讨论】:

    【解决方案2】:

    问题与你的观点LayoutConstraints有关。

    似乎一个名为 UISV-canvas-connection 的约束基于您要删除的视图:

    2016-12-09 00:56:26.792168 appName[59081:4139912] [LayoutConstraints] View hierarchy unprepared for constraint.
    Constraint: <NSLayoutConstraint:0x618000490cc0 'UISV-canvas-connection' UIStackView:0x7fe25cd25dd0.bottom == appName.timeLineView:0x7fe25e007890.bottom   (active)>
    Container hierarchy: 
    <UIStackView: 0x7fe25cd25dd0; frame = (0 0; 718 40); opaque = NO; autoresize = RM+BM; layer = <CATransformLayer: 0x6080002295a0>>
    View not found in container hierarchy: <appName.timeLineView: 0x7fe25e007890; frame = (0 0; 718 40); autoresize = W+H; layer = <CALayer: 0x610000835c40>>
    That view's superview: NO SUPERVIEW
    

    此约束基于视图层次结构中不再存在的 UIStackView(因此消息:That view's superview: NO SUPERVIEW)。

    所以约束从父视图中移除后无法基于该视图进行评估。

    【讨论】:

      【解决方案3】:
       for subview in stackView.subviews {
              stackView.removeArrangedSubview(subview)
              NSLayoutConstraint.deactivate(subview.constraints)
              subview.removeFromSuperview()
          }
       print("subviews: \(stackView.subviews)")
       // subviews: []
      

      【讨论】:

        【解决方案4】:

        有一个类似的问题,我想我刚刚解决了这个问题,从堆栈视图中删除项目时要非常小心 - 这是一个总结它的扩展:

        extension UIStackView {
        
            func safelyRemoveArrangedSubviews() {
        
                // Remove all the arranged subviews and save them to an array
                let removedSubviews = arrangedSubviews.reduce([]) { (sum, next) -> [UIView] in
                    self.removeArrangedSubview(next)
                    return sum + [next]
                }
        
                // Deactive all constraints at once
                NSLayoutConstraint.deactivate(removedSubviews.flatMap({ $0.constraints }))
        
                // Remove the views from self
                removedSubviews.forEach({ $0.removeFromSuperview() })
            }
        }
        

        【讨论】:

        • 这有效吗?对我来说,从单元格中删除所有子视图时它会崩溃。
        • 是的,我有类似的代码,现在它正在崩溃,但我已经在其他地方使用它并且它可以工作。所以这取决于或在过去几年发生了一些变化。现在在 iOS 13 中,我似乎可以仅使用 removeArrangedSubviews() 删除,并且不需要停用约束并从超级视图中删除
        • 我发布了我的答案,它似乎工作只是将所有这些代码放在一个循环中
        猜你喜欢
        • 2018-11-02
        • 1970-01-01
        • 2017-07-22
        • 2017-03-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-02-08
        • 2017-11-13
        相关资源
        最近更新 更多