ios iOS9 此应用程序正在从后台线程修改自动布局引擎,这可能会导致引擎损坏和奇怪的崩溃

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32619740/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 07:39:53  来源:igfitidea点击:

iOS9 This application is modifying the autolayout engine from a background thread, which can lead to engine corruption and weird crashes

iosswiftios9

提问by Benjamin

I have just dowloaded the latest XCode (7.1 beta) and have started playing around with iOS9.

我刚刚下载了最新的 XCode(7.1 测试版)并开始使用 iOS9。

I had an app working perfectly with no errors in iOS8 but now I get the following error from overriding the drawRect method in a UITableViewCell class :

我有一个应用程序在 iOS8 中完美运行,没有错误,但现在我通过覆盖 UITableViewCell 类中的 drawRect 方法得到以下错误:

"This application is modifying the autolayout engine from a background thread, which can lead to engine corruption and weird crashes. This will cause an exception in a future release."

“此应用程序正在从后台线程修改自动布局引擎,这可能导致引擎损坏和奇怪的崩溃。这将导致未来版本中的异常。”

here is the backtrace :

这是回溯:

Stack:(
0   CoreFoundation                      0x000000010a749f65 __exceptionPreprocess + 165
1   libobjc.A.dylib                     0x0000000109dcfdeb objc_exception_throw + 48
2   CoreFoundation                      0x000000010a749e9d +[NSException raise:format:] + 205
3   Foundation                          0x0000000109b442e5 _AssertAutolayoutOnMainThreadOnly + 79
4   Foundation                          0x00000001099a4ece -[NSISEngine withBehaviors:performModifications:] + 31
5   UIKit                               0x000000010b9d425b -[UIView(AdditionalLayoutSupport) _withAutomaticEngineOptimizationDisabledIfEngineExists:] + 58
6   UIKit                               0x000000010b9d4d9e -[UIView(AdditionalLayoutSupport) updateConstraintsIfNeeded] + 254
7   UIKit                               0x000000010b702760 -[UITableViewCellContentView updateConstraintsIfNeeded] + 185
8   UIKit                               0x000000010b9d5ab3 -[UIView(AdditionalLayoutSupport) _updateConstraintsAtEngineLevelIfNeeded] + 272
9   UIKit                               0x000000010b1e6274 -[UIView(Hierarchy) _updateConstraintsAsNecessaryAndApplyLayoutFromEngine] + 159
10  UIKit                               0x000000010b1f5d84 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 710
11  QuartzCore                          0x000000010ae1059a -[CALayer layoutSublayers] + 146
12  QuartzCore                          0x000000010ae04e70 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 366
13  QuartzCore                          0x000000010ae04cee _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 24
14  QuartzCore                          0x000000010adf9475 _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 277
15  QuartzCore                          0x000000010ae26c0a _ZN2CA11Transaction6commitEv + 486
16  QuartzCore                          0x000000010ae2737c _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv + 92
17  CoreFoundation                      0x000000010a675967 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23
18  CoreFoundation                      0x000000010a6758d7 __CFRunLoopDoObservers + 391
19  CoreFoundation                      0x000000010a66ae4c CFRunLoopRunSpecific + 524
20  CoreFoundation                      0x000000010a71e011 CFRunLoopRun + 97
21  SDWebImage                          0x000000010971773c -[SDWebImageDownloaderOperation start] + 1868
22  Foundation                          0x0000000109961e47 __NSOQSchedule_f + 194
23  libdispatch.dylib                   0x000000010d93849b _dispatch_client_callout + 8
24  libdispatch.dylib                   0x000000010d91e8ec _dispatch_queue_drain + 2215
25  libdispatch.dylib                   0x000000010d91de0d _dispatch_queue_invoke + 601
26  libdispatch.dylib                   0x000000010d920a56 _dispatch_root_queue_drain + 1420
27  libdispatch.dylib                   0x000000010d9204c5 _dispatch_worker_thread3 + 111
28  libsystem_pthread.dylib             0x000000010dc80a9d _pthread_wqthread + 729
29  libsystem_pthread.dylib             0x000000010dc7e3dd start_wqthread + 13
)

Here is the drawRect Method :

这是 drawRect 方法:

override func drawRect(rect: CGRect) {

    super.drawRect(rect)

    let cellRect = rect

    // Values
    let buttonBoxX = CELL_MARGIN + CELL_MARGIN/2
    let buttonBoxY = cellRect.height - buttonBoxHeight
    let buttonBoxWidth = rect.width - CELL_MARGIN * 3


    // Set Button Box
    let buttonBoxRect = CGRectMake(buttonBoxX, buttonBoxY, buttonBoxWidth, buttonBoxHeight )
    let buttonBox = UIBezierPath(roundedRect: buttonBoxRect, byRoundingCorners: [.BottomRight, .BottomLeft], cornerRadii: CGSize(width: CORNER_RADIUS, height: CORNER_RADIUS)) // Create the path
    UIColor.whiteColor().setFill() // Set the Fill to be white

    buttonBox.fill()


}

My understanding (cf. this question) is that complicated calculations etc should be done on a background thread and the the update of the UI on the main thread because UI is not thread safe.

我的理解(参见这个问题)是复杂的计算等应该在后台线程上完成,而在主线程上更新 UI,因为 UI 不是线程安全的。

However, IF i use the following :

但是,如果我使用以下内容:

override func drawRect(rect: CGRect) {

    super.drawRect(rect)

    let cellRect = rect

    // Values
    let buttonBoxX = CELL_MARGIN + CELL_MARGIN/2
    let buttonBoxY = cellRect.height - buttonBoxHeight
    let buttonBoxWidth = rect.width - CELL_MARGIN * 3


    // Set Button Box
    let buttonBoxRect = CGRectMake(buttonBoxX, buttonBoxY, buttonBoxWidth, buttonBoxHeight )
    let buttonBox = UIBezierPath(roundedRect: buttonBoxRect, byRoundingCorners: [.BottomRight, .BottomLeft], cornerRadii: CGSize(width: CORNER_RADIUS, height: CORNER_RADIUS)) // Create the path
    UIColor.whiteColor().setFill() // Set the Fill to be white

    dispatch_async(dispatch_get_main_queue(), {
        buttonBox.fill()
    })


}

I now get a CGContext error…

我现在收到一个 CGContext 错误...

<Error>: CGContextSaveGState: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
<Error>: CGContextSetFlatness: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
<Error>: CGContextAddPath: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
<Error>: CGContextDrawPath: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
<Error>: CGContextRestoreGState: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
<Error>: CGContextSaveGState: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
<Error>: CGContextSetFlatness: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
<Error>: CGContextAddPath: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
<Error>: CGContextDrawPath: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
<Error>: CGContextRestoreGState: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.

Any suggestions ?

有什么建议 ?

SOLUTION : OK. here is the deal. I was loading an image into a UIImageView asynchronously, but not "painting" it in the UI on the main thread but on a background thread. Furthermore; I was calling setNeedsDisplay on the UITableViewCell after adding the image to the UI, thus calling the drawRect method again… but this time on the background thread.

解决方案:好的。这是交易。我正在将图像异步加载到 UIImageView 中,但不是在主线程上的 UI 中而是在后台线程上“绘制”它。此外; 将图像添加到 UI 后,我在 UITableViewCell 上调用 setNeedsDisplay,因此再次调用 drawRect 方法……但这次是在后台线程上。

回答by Rob Napier

The first error you note doesn't look like it's related to your drawRect:code. Your drawRect:looks fine. It isn't doing anything particularly complicated. If your path were more complex, you'd probably want to cache it, but it's probably fine as-is. More likely you're trying to modify the UI somewhere else on a background thread. It may only spring up when you override drawRect:because that changes how the UI update happens (without a custom drawRect:, the system may be able to apply simple transforms). You need to look for where you're making a UIKit call on a background thread.

您注意到的第一个错误看起来与您的drawRect:代码无关。你drawRect:看起来不错。它没有做任何特别复杂的事情。如果您的路径更复杂,您可能想要缓存它,但按原样可能没问题。您更有可能尝试在后台线程的其他地方修改 UI。它可能只有在您覆盖时才会出现,drawRect:因为这会改变 UI 更新的发生方式(没有 custom drawRect:,系统可能能够应用简单的转换)。您需要寻找在后台线程上进行 UIKit 调用的位置。

When in doubt, if it starts with UI, you probably can't use it on a background thread. That's not completely true (you can create a UIBezierPathon a background thread, and you can even draw it into a non-screen context), but as a first approximation, it's a good thing to look for.

如有疑问,如果它以 开头UI,则您可能无法在后台线程上使用它。这并不完全正确(您可以UIBezierPath在后台线程上创建一个,甚至可以将其绘制到非屏幕上下文中),但作为第一个近似值,寻找它是一件好事。

This code is simply incorrect:

这段代码完全不正确:

dispatch_async(dispatch_get_main_queue(), {
    buttonBox.fill()
})

The call to drawRect:had better already beon the main queue (so dispatching to the main queue is not helpful). If it's not, see above. By the time this block executes, the draw cycle is over, so there's no context to draw into any more.

要将呼叫drawRect:最好已经主队列(以便调度到主队列不是有用)。如果不是,请参见上文。到此块执行时,绘制周期已结束,因此不再需要绘制上下文。

回答by superarts.org

Basically This application is modifying the autolayout engine from a background thread, which can lead to engine corruption and weird crashes. This will cause an exception in a future releasestates that you shouldn't update UI, especially autolayout related part in non-UI threads. To fix use dispatch_async(dispatch_get_main_queue()) { // your code }, or

基本上This application is modifying the autolayout engine from a background thread, which can lead to engine corruption and weird crashes. This will cause an exception in a future release声明你不应该更新 UI,尤其是非 UI 线程中的自动布局相关部分。修复 use dispatch_async(dispatch_get_main_queue()) { // your code },或

Swift 3:

斯威夫特 3:



DispatchQueue.main.async {
    buttonBox.fill()
}

回答by Abdul Hameed

Should try Symbolic Breakpoint to detect the issue:-enter image description here

应该尝试符号断点来检测问题:-在此处输入图片说明

Then put your UI Update code in main thread

然后将您的 UI 更新代码放在主线程中

DispatchQueue.main.async {}