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 {}