iOS9此应用程序正在从后台线程修改autolayout引擎,这可能导致引擎损坏和奇怪的崩溃 [英] iOS9 This application is modifying the autolayout engine from a background thread, which can lead to engine corruption and weird crashes
问题描述
我刚刚下载了最新的XCode(7.1测试版),并开始使用iOS9。
I have just dowloaded the latest XCode (7.1 beta) and have started playing around with iOS9.
我有一个应用程序完美运行,iOS8没有错误但现在我从UITableViewCell类中覆盖drawRect方法得到以下错误:
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 :
此应用程序正在从后台线程修改autolayout引擎,这可能导致引擎损坏和奇怪的崩溃。这将在未来的版本中引起异常。
"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."
这里是回溯:
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
)
这是drawRect方法:
Here is the drawRect Method :
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()
}
我的理解(参见此问题)是复杂的计算等应该在后台线程和主线程上的UI更新,因为UI不是线程安全的。
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.
但是,如果我使用以下:
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()
})
}
我现在得到一个CGContext错误...
I now get a CGContext error…
<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.
有什么建议吗?
解决方案:
好的。这是交易。我正在异步地将图像加载到UIImageView中,但不是在主线程的UI中绘制它,而是在后台线程上。此外;在将图像添加到UI后,我在UITableViewCell上调用setNeedsDisplay,从而再次调用drawRect方法......但这次是在后台线程上。
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.
推荐答案
您注意到的第一个错误看起来与您的 drawRect无关:
代码。你的 drawRect:
看起来很好。它没有做任何特别复杂的事情。如果你的路径更复杂,你可能想要缓存它,但它可能很好。您更有可能尝试在后台线程的其他位置修改UI。它可能只会在您覆盖 drawRect:
时弹出,因为它会更改UI更新的发生方式(没有自定义 drawRect:
,系统可能能够应用简单的变换)。您需要查找在后台线程上进行UIKit调用的位置。
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.
如果有疑问,如果它以 UI开头
,你可能无法在后台线程上使用它。这不完全正确(您可以在后台线程上创建 UIBezierPath
,甚至可以将其绘制到非屏幕上下文中),但作为第一个近似值,它是一个好的事情要找。
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 UIBezierPath
on 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.
此代码完全错误:
dispatch_async(dispatch_get_main_queue(), {
buttonBox.fill()
})
对 drawRect:
的调用最好已经在主队列上 (因此调度到主队列没有帮助)。如果不是,请参见上文。当这个块执行时,绘制周期结束,因此没有任何上下文可以进入。
The call to drawRect:
had better already be on 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.
这篇关于iOS9此应用程序正在从后台线程修改autolayout引擎,这可能导致引擎损坏和奇怪的崩溃的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!