web-dev-qa-db-ja.com

iOS9このアプリケーションは、自動レイアウトエンジンをバックグラウンドスレッドから変更しているため、エンジンの破損や奇妙なクラッシュにつながる可能性があります

最新のXCode(7.1ベータ版)をダウンロードして、iOS9で遊んでみました。

IOS8ではアプリはエラーなしで完全に機能していましたが、UITableViewCellクラスのdrawRectメソッドをオーバーライドすると次のエラーが発生します。

「このアプリケーションは、自動レイアウトエンジンをバックグラウンドスレッドから変更しているため、エンジンの破損や奇妙なクラッシュにつながる可能性があります。これにより、将来のリリースで例外が発生します。」

ここにバックトレースがあります:

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()


}

私の理解(cf. this question )は、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.

助言がありますか ?

解決策:OK。こちらが取引です。画像をUIImageViewに非同期で読み込みましたが、メインスレッドのUIではなくバックグラウンドスレッドで「ペイント」していませんでした。さらに;画像をUIに追加した後、UITableViewCellでsetNeedsDisplayを呼び出していたため、もう一度drawRectメソッドを呼び出しましたが、今回はバックグラウンドスレッドで行いました。

12
Benjamin

メモした最初のエラーは、drawRect:コードに関連するものではないようです。 drawRect:は問題ありません。特に複雑なことはしていません。パスがより複雑な場合は、おそらくそれをキャッシュしたいと思うでしょうが、それはおそらく現状のままで問題ありません。バックグラウンドスレッドのどこかでUIを変更しようとしている可能性があります。 UIの更新方法が変わるため、drawRect:をオーバーライドしたときにのみ発生する可能性があります(カスタムdrawRect:がないと、システムは単純な変換を適用できる場合があります)。バックグラウンドスレッドでUIKit呼び出しを行っている場所を探す必要があります。

疑わしい場合は、UIで始まる場合、バックグラウンドスレッドで使用できない可能性があります。これは完全に真実ではありません(バックグラウンドスレッドでUIBezierPathを作成でき、それを非スクリーンコンテキストに描画することもできます)が、最初の概算として、探すことをお勧めします。

このコードは単に間違っています:

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

drawRect:への呼び出しは、メインキューですでにbeの方が優れていました(メインキューへのディスパッチは役に立ちません)。そうでない場合は、上記を参照してください。このブロックが実行されるときまでに、描画サイクルは終了しているため、これ以上描画するコンテキストはありません。

22
Rob Napier

基本的に_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 }を使用するか、

スウィフト3:


_DispatchQueue.main.async {
    buttonBox.fill()
}
_
2
superarts.org

シンボリックブレークポイントを試して問題を検出する必要があります:- enter image description here

次に、UI更新コードをメインスレッドに配置します

DispatchQueue.main.async {}
0
Abdul Hameed