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

我刚刚下载了最新的XCode(7.1testing版),并开始玩iOS9。

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

“这个应用程序正在从后台线程修改自动布局引擎,这可能会导致引擎崩溃和奇怪的崩溃,这将在未来的版本中引发exception。

这是回溯:

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方法:

 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不是线程安全的。

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

 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错误…

 <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. 

有什么build议么 ?

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

你注意到的第一个错误看起来不像你的drawRect:代码。 你的drawRect:看起来不错。 它没有做任何特别复杂的事情。 如果你的path更复杂,你可能想要caching它,但它现在可能是好的。 更有可能你试图在后台线程的其他地方修改UI。 当你重写drawRect:时,它可能只会popupdrawRect:因为这会改变UI更新的发生(没有自定义drawRect:系统可能能够应用简单的变换)。 您需要查找在后台线程上进行UIKit调用的位置。

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

这段代码简直是不正确的:

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

drawRect:的调用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 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线程中的自动布局相关部分。 要修复使用dispatch_async(dispatch_get_main_queue()) { // your code }

Swift 3:


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

应该尝试使用符号断点检测问题: 在这里输入图像说明

然后把你的UI更新代码放在主线程中

 DispatchQueue.main.async {}