2015-09-16 4 views
12

最新のXCode(7.1ベータ版)をダウンロードして、iOS9で再生を開始しました。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() 


} 

私の理解(参照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() 
    }) 


} 

I今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ではなくバックグラウンドスレッドの "ペイント"しませんでした。さらに、 UITableViewCellでsetNeedsDisplayを呼び出してUIにイメージを追加した後、再度drawRectメソッドを呼び出していましたが、今回はバックグラウンドスレッドで行いました。

+0

あなたの質問から、どのコードがバックグラウンドで実行されているかは明確ではありませんおい?ちょうど 'drawRect'か何か他の?どのくらい正確にメインキューを使用するように変更しましたか?通常はdrawRectを自分でディスパッチしませんが、UIKitの必要に応じて呼び出されます。 – Paulw11

+0

あなたの答えに私の質問を編集しました。実際、drawRectはUIKitによって呼び出され、私によっては呼び出されません。ただし、drawRectのUIに何が起きているかによってエラーが発生します。 – Benjamin

+0

'drawRect'のメインキューにディスパッチするのが遅すぎます - バックグラウンドスレッドからUIを更新している他の場所 - ネットワークタスクからセルデータを取得しているのでしょうか?これは、メインキュー – Paulw11

答えて

22

最初のエラーは、あなたのdrawRect:コードに関連しているようには見えません。あなたのdrawRect:はうまく見えます。特に複雑なことはしていません。あなたのパスがもっと複​​雑ならば、それをキャッシュしたいと思うかもしれませんが、それはおそらくそのままの状態です。おそらく、バックグラウンドスレッドのどこかでUIを変更しようとしている可能性があります。 drawRect:をオーバーライドすると、UI更新の仕方が変わります(カスタムdrawRect:がなくてもシンプルな変換を適用できます)。バックグラウンドスレッドでUIKitコールをどこで作成しているかを調べる必要があります。

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

このコード

は単に間違っている:

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

drawRect:への呼び出しが良く、すでにがメインキューになりました(そのメインキューに派遣することは有用ではありません)。そうでない場合は、上記を参照してください。このブロックが実行される頃には、描画サイクルは終了しているので、これ以上描画するコンテキストはありません。あなたはUIを更新するべきではありません

+4

あなたはまったく正しいです!このエラーは私のdrawRectメソッドでは発生しませんでしたが、非同期リクエストでイメージを取得してメインキューにディスパッチするのを忘れていました...新しいルール:1AM後には決してコード化しないでください。 – Benjamin

+15

最高のコードは1AM後に起こります! –

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 }を使用し固定するために、または

スウィフト3:


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

が問題を検出するために、シンボリックブレークポイントを試してみてください: - enter image description here

その後、あなたのUIの更新コードが

メインスレッドに入れます
DispatchQueue.main.async {} 
+0

私の場合は動作しません –