IOS9 Это приложение модифицирует механизм автозапуска из фонового потока, что может привести к повреждению двигателя и странным сбоям
Я только что загрузил последнюю версию XCode (7.1 beta) и начал играть с iOS9.
У меня было приложение, прекрасно работающее без ошибок в iOS8, но теперь я получаю следующую ошибку от переопределения метода drawRect в классе UITableViewCell:
"Это приложение изменяет механизм автозапуска из фонового потока, что может привести к повреждению двигателя и странным сбоям. Это приведет к исключению в будущей версии".
здесь обратная трассировка:
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()
}
Мое понимание (см. этот вопрос) заключается в том, что сложные вычисления и т.д. должны выполняться в фоновом потоке и обновлении пользовательского интерфейса в основном потоке, потому что пользовательский интерфейс не является надежным потоком.
Однако, если я использую следующее:
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.
Любые предложения?
РЕШЕНИЕ:
ОК. вот сделка. Я загружал изображение в UIImageView асинхронно, но не "рисовал" его в пользовательском интерфейсе в основном потоке, а в фоновом потоке. Более того; Я вызывал setNeedsDisplay в UITableViewCell после добавления изображения в пользовательский интерфейс, тем самым снова вызвав метод drawRect... но на этот раз в фоновом потоке.
Ответы
Ответ 1
Первая ошибка, которую вы заметили, не похожа на ваш код drawRect:
. Ваш drawRect:
выглядит отлично. Это не делает ничего особенно сложного. Если ваш путь был более сложным, вы, вероятно, захотите его кэшировать, но он, вероятно, отлично работает как есть. Скорее всего, вы пытаетесь изменить пользовательский интерфейс где-то еще в фоновом потоке. Он может работать только spring, когда вы переопределяете drawRect:
, потому что это влияет на изменение обновления пользовательского интерфейса (без пользовательского drawRect:
, система может применять простые преобразования). Вам нужно искать, где вы делаете вызов UIKit в фоновом потоке.
Если вы сомневаетесь, если он начинается с UI
, вы, вероятно, не сможете использовать его в фоновом потоке. Это не совсем верно (вы можете создать UIBezierPath
в фоновом потоке, и вы даже можете привлечь его в неэкранный контекст), но в первом приближении это хорошо, что нужно искать.
Этот код просто неверен:
dispatch_async(dispatch_get_main_queue(), {
buttonBox.fill()
})
Вызов drawRect:
лучше уже был в главной очереди (поэтому отправка в основную очередь не помогает). Если это не так, см. Выше. К тому моменту, когда этот блок выполняется, цикл розыгрыша завершен, поэтому нет никакого контекста для рисования.
Ответ 2
В основном 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. Чтобы исправить использование dispatch_async(dispatch_get_main_queue()) { // your code }
или
Swift 3:
DispatchQueue.main.async {
buttonBox.fill()
}
Ответ 3
Попробуйте Symbolic Breakpoint для обнаружения проблемы: - ![введите описание изображения здесь]()
Затем поместите код обновления UI в основной поток
DispatchQueue.main.async {}