「このアプリケーションは、自動レイアウトエンジンをバックグラウンドスレッドから変更しています」エラーが発生しましたか?


310

私のOS Xでswiftを使用してこのエラーが頻繁に発生しています:

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

私のNSWindowcontentViewあり、ウィンドウのビューにスワップしています。ウィンドウでを実行しようとしたり、ウィンドウにを追加したりすると、エラーが発生します。自動サイズ変更機能を無効にしてみましたが、自動レイアウトを使用するものはありません。何かご意見は?NSApp.beginSheetsubview

時にはそれは大丈夫で何も起こりません、他の時にはそれは完全に私UIを壊し、何もロードしません


2
何らかの理由で、以下の優れた回答が削除されました: github.com/nrbrook/NBUIKitMainThreadGuard
Fattie

少なくとも数時間節約できました。ありがとう@Fattie
oyalhi

右@oyalhi。使用には注意してください。私は本当に楽しんでいましたが、他の問題も抱えていました-難しい分野です!それが役に立てば幸い!
Fattie

回答:


638

スレッド関数の実行が完了するとすぐにUIを更新できるように、別のスレッド内に配置する必要があります。

モダンスウィフト:

DispatchQueue.main.async {
    // Update UI
}

Swift 3より前の古いバージョンのSwift。

dispatch_async(dispatch_get_main_queue(){
    // code here
})

Objective-C:

dispatch_async(dispatch_get_main_queue(), ^{
    // code here
});

3
これをObjective Cで機能させるには、コードブロックの{の前に^(void)を置き、その後にセミコロンを置きます。
2015

4
害はありませんが、^の代わりに^(void)は必要ありません。回答のObjective-Cバージョンは問題ありません。
ケラー

2
それは私にとってはうまくいきます。私の問題は、ネットワークリクエストを実行し、成功完了ブロック内で関数を呼び出してUIを更新することです。UIKitはスレッドセーフではないため、UIを更新するにはメインスレッドにディスパッチする必要があります。
レイチェル

1
Swift 3ソリューションについては@Naishtaの回答を参照してください
ナサニエル

1
Swift 3:DispatchQueue.main.async(){ code }、@ Naishtaによると
smukamuka

146

'dispatch_async'を使用せずにprintステートメントでデバッグしているときに同様のエラーメッセージが表示されるので、そのエラーメッセージが表示されたら、使用する時間

スウィフト4

DispatchQueue.main.async { //code }

スウィフト3

DispatchQueue.main.async(){ //code }

以前のSwiftバージョン

dispatch_async(dispatch_get_main_queue()){ //code }

5
構文が間違っているため、次のようにする必要が dispatch_async(dispatch_get_main_queue(), ^{ /* UI related code */ });あります。
ゾルタン

1
または、クロージャー内で、バックグラウンドスレッドからメインスレッドにアクセスするには、次のコードを使用します。self.performSelectorOnMainThread(Selector( "yourFunction:")、withObject: 'yourArray / yourObject'、waitUntilDone:true)
Naishta

1
いいえ、そうではありません。構文を確認してください
Naishta

82

「このアプリケーションは、自動レイアウトエンジンをバックグラウンドスレッドから変更しています」というエラーは、実際の問題が発生してからかなり後にコンソールに記録されるため、ブレークポイントを使用しないとデバッグが困難になる可能性があります。

@markussvenssonの回答を使用して問題を検出し、このシンボリックブレークポイントを使用してそれを見つけました(デバッグ>ブレークポイント>シンボリックブレークポイントの作成):

  1. 記号:[UIView layoutIfNeeded]または[UIView updateConstraintsIfNeeded]
  2. 状態: !(BOOL)[NSThread isMainThread]

ここに画像の説明を入力してください

エミュレーターでアプリをビルドして実行し、エラーメッセージがスローされる原因となる手順を複製します(アプリは通常より遅くなります!)。Xcodeはアプリを停止し、バックグラウンドスレッドからUIにアクセスしているコード行(たとえば、funcの呼び出し)をマークします。


3
うーん。それは理にかなっているようだったので、私はこれを賛成しました。ただし、実行は中断せず、ログにエラーが表示されます。シンボリックブレークポイントを設定するために使用できる他の条件はありますか?
Sjakelien 2017年

私の場合、それは壊れます。ただし、スタックトレースは、どのビューが原因であるかのヒントを私に与えません。そして、私は示されているアセンブラコードを解釈する方法がわかりませんmovq 0x10880ba(%rip), %rsi ; "_wantsReapplicationOfAutoLayoutWithLayoutDirtyOnEntry:"
ReinhardMänner'22

デバッグ時にアプリの実行が非常に遅くなりますか?
イタチ

@イタチうん。スピードアップする方法がわからない。
k06a 2017

2
Xcode 9以降、組み込みの機能です。スキーム設定の「診断」タブで「メインスレッドチェッカー」オプションが有効になっていることを確認してください
AndrewPo

24

テキストフィールドの値を更新するか、バックグラウンドスレッド内にサブビューを追加しようとすると、この問題が発生する可能性があります。そのため、この種のコードはメインスレッドに配置する必要があります。

メインキューを取得するには、dispatch_asynchでUI更新を呼び出すメソッドをラップする必要があります。例えば:

dispatch_async(dispatch_get_main_queue(), { () -> Void in
   self.friendLabel.text = "You are following \(friendCount) accounts"
})

編集-SWIFT 3:

今、私たちは次のコードに従ってそれを行うことができます:

// Move to a background thread to do some long running work
DispatchQueue.global(qos: .userInitiated).async {
   // Do long running task here
   // Bounce back to the main thread to update the UI
   DispatchQueue.main.async {
      self.friendLabel.text = "You are following \(friendCount) accounts"
   }
}

23

私にとって、このエラーメッセージはAdmob SDKのバナーから発信されました。

条件付きブレークポイントを設定することで、「WebThread」の起点を追跡できました。

バックグラウンドスレッドからUIを更新しているユーザーを見つけるための条件付きブレークポイント

次に、バナーの作成を次のようにカプセル化することで問題を取り除くことができました。

dispatch_async(dispatch_get_main_queue(), ^{
   _bannerForTableFooter = [[GADBannerView alloc] initWithAdSize:kGADAdSizeSmartBannerPortrait];
   ...
}

このコードが非メインスレッドからどのように呼び出されたかがわからないので、なぜこれが役に立ったのかわかりません。

それが誰にも役立つことを願っています。


1
同じ問題がありました。例外がWebThreadで発生している理由を混乱させました。私はあなたがしたのと同じ変更を加えました、そしてそれは今うまくいきます。少し古いバージョンのAdMob SDKを使用しています。最新版で修正されたのでしょうか。これをありがとう。見つけられなかったと思います。
Larry

1
AdMobの最新バージョンへのアップデートは私のために、この問題を解決
JH95

このようなシンボリックブレークポイントでブレークが発生しますが、コードは表示されません。:(
ビクターエンゲル

20

NSURLConnection非同期要求完了ハンドラー内でUIを更新するブロックを呼び出しているときにiOS 9 SDKに更新してから、この問題が発生しました。dispatch_main_queueを使用してブロック呼び出しをdispatch_asyncに配置すると、問題が解決しました。

iOS 8では問題なく動作しました。


10

私が使用していたため、同じ問題がありましたperformSelectorInBackground


いいえ、私はバックグラウンドで物事を行う必要がありました。私はdispatch_async(dispatch_get_main_queue()メソッド内NSNotificationCenterの呼び出しを入れて、それが働いた。
ボビー・

私はURLSessionDelegateからデータを取得し、それがNSNotificationを呼び出し、UIViewControllerが通知に応答し、そこでDispatchQueue.main.asyncを使用して、データが良好かどうかをユーザーに示しました。違う!-解決策は、通知をメインキューに入れます。DispatchQueue.main.async {NotificationCenter.default.post(name:NSNotification.Name(rawValue:networkNotificationNames.products.rawValue)、object:self、userInfo:[networkNotificationNames.products.rawValue:productList])}
iCyber​​Paul

7

メインスレッド以外でUIを変更しないでください。UIKitはスレッドセーフではないため、これを行うと上記の問題やその他の奇妙な問題が発生します。アプリがクラッシュすることさえあります。

したがって、UIKit操作を行うには、ブロックを定義してメインキューで実行できるようにする必要があります。

NSOperationQueue.mainQueue().addOperationWithBlock {

}

これはXcode 7.2およびiOS 9.2では使用できません。他の選択肢はありますか?
Jayprakash Dubey

7

明らかに、バックグラウンドスレッドでUIの更新を行っています。コードを見ずに、正確な場所を予測できません。

これらはそれが起こるかもしれないいくつかの状況です:-

あなたはバックグラウンドスレッドで何かをしていて、使用していないかもしれません。同じ関数であるため、このコードを見つけるのは簡単です。

DispatchQueue.main.async { // do UI update here }

バックグラウンドスレッドでWeb要求呼び出しを行うfuncを呼び出し、その完了ハンドラーがui更新を行う他のfuncを呼び出します。これを解決するには、webrequest呼び出し後にUIを更新したコードを確認してください。

// Do something on background thread
DispatchQueue.global(qos: .userInitiated).async {
   // update UI on main thread
   DispatchQueue.main.async {
                // Updating whole table view
                self.myTableview.reloadData()
            }
}

5

「このアプリケーションは自動レイアウトエンジンをバックグラウンドスレッドから変更しています」の主な問題は、実際の問題が発生してから長い時間が経過するとログに記録されるように見えるため、トラブルシューティングが非常に困難になる可能性があります。

私は3つのシンボリックブレークポイントを作成することで問題を解決しました。

デバッグ>ブレークポイント>シンボリックブレークポイントの作成...

ブレークポイント1:

  • シンボル: -[UIView setNeedsLayout]

  • 状態: !(BOOL)[NSThread isMainThread]

ブレークポイント2:

  • シンボル: -[UIView layoutIfNeeded]

  • 状態: !(BOOL)[NSThread isMainThread]

ブレークポイント3:

  • シンボル: -[UIView updateConstraintsIfNeeded]

  • 状態: !(BOOL)[NSThread isMainThread]

これらのブレークポイントを使用すると、非メインスレッドで誤ってUIメソッドを呼び出す実際の行で簡単にブレークすることができます。


4

UITableViewでデータを再読み込み中にこの問題が発生しました。次のようにリロードをディスパッチするだけで問題が解決しました。

    dispatch_async(dispatch_get_main_queue(), { () -> Void in
        self.tableView.reloadData()
    })

これで終わりです!メインキューには他のすべてがありましたが、reloadData()は不要です!
オプリマス2017

3

同じ問題がありました。UIAlertsメインキューが必要なものを使用していたことがわかりました。しかし、それらは廃止れました
をに変更したUIAlertsところ、UIAlertController問題がなくなり、dispatch_asyncコードを使用する必要がなくなりました。レッスン-警告に注意してください。彼らはあなたがそれを期待しないときでも助けます。


3

@Markからの正しいコードの回答は既にありますが、私の発見を共有するだけです。問題は、ビューの変更を要求し、それが即座に発生すると想定していることです。実際には、ビューの読み込みは利用可能なリソースに依存します。すべてが十分に速く読み込まれ、遅延がない場合は、何も気づきません。プロセススレッドがビジーであるなどの理由で遅延が発生するシナリオでは、アプリケーションは、まだ準備ができていなくても何かを表示することになっている状況に陥ります。したがって、これらの要求を非同期キューにディスパッチして、負荷に基づいて実行されるようにすることをお勧めします。


2

TouchIDを使用しているときにこの問題が発生しました。それが他の誰かを助ける場合、メインキューのUIで何かを行う可能性のある成功ロジックをラップします。


2

これは、テキストフィールドやラベルの値を設定したり、バックグラウンドスレッド内にサブビューを追加したりするような単純なもので、フィールドのレイアウトが変更される可能性があります。インターフェースで行うことはすべて、メインスレッドでのみ行われるようにしてください。

このリンクを確認してください:https : //forums.developer.apple.com/thread/7399


2

同じViewControllerのUILabelでエラーメッセージを更新しようとすると、同じ問題が発生しました(通常のコーディングでデータを更新しようとすると、少し時間がかかります)。私DispatchQueueはSwift 3 Xcode 8で使用しましたが、動作します。


2

このエラーをハントしたい場合は、メインスレッドチェッカーを使用して問題を一時停止するチェックボックスを使用します。修正はほとんどの場合簡単で、問題のある行をメインキューにディスパッチします。

ここに画像の説明を入力してください


1
これは正確に何をしますか?「メインスレッドチェッカー」はデフォルトで有効になっていて、「スレッドサニタイザー」と「問題の一時停止」の両方を追加でチェックしましたが、「バックグラウンドスレッドからの変更」メッセージのみがスローされ、情報は追加されません。
ネフ

UIがバックグラウンドスレッドで変更されている時点で、アプリの実行を一時停止します。
rockdaswift

奇妙なことに、私のアプリ(Xcode 10.2.1)ではそれができませんでした。(ここで説明するように)ブレークポイントを手動で追加して一時停止し、コード行にポイントする必要がありました。
Neph

そして、このオプションセットを表示するにはどうすればよいですか?
David Rector、

ターゲットのスキームを編集する
rockdaswift

1

私にとっての問題は次のとおりです。performSegueWithIdentifier:メインスレッドで実行されることを確認してください:

dispatch_async (dispatch_get_main_queue(), ^{
  [self performSegueWithIdentifier:@"ViewController" sender:nil];
});

1

Swift 4

操作キューを使用してメソッドを呼び出す場合を想定します。

operationQueue.addOperation({
            self.searchFavourites()
        })

そして関数searchFavouritesが

func searchFavourites() {
     DispatchQueue.main.async {
                    //Your code
                }
}

呼び出すと、メインスレッドの「searchFavourites」メソッド内のすべてのコードで、UIを更新している場合でもエラーが発生します。

このアプリケーションは、メインスレッドからエンジンにアクセスした後、バックグラウンドスレッドから自動レイアウトエンジンを変更しています。

だからソリューションを使用して、

operationQueue.addOperation({
            DispatchQueue.main.async {
                self.searchFavourites()
            }
        })

このようなシナリオの場合。


1

ここでログからこの行を確認してください

$S12AppName18ViewControllerC11Func()ySS_S2StF + 4420

このようなメインスレッドから関数を呼び出すために必要なバックグラウンドスレッドまたはAPIメソッドを呼び出している場所から呼び出す関数を確認できます。

DispatchQueue.main.async { func()}

func()は、API呼び出しの成功またはその他の結果として呼び出す関数です。

ここにログ

This application is modifying the autolayout engine from a background thread after the engine was accessed from the main thread. This can lead to engine corruption and weird crashes.
 Stack:(
    0   Foundation                          0x00000001c570ce50 <redacted> + 96
    1   Foundation                          0x00000001c5501868 <redacted> + 32
    2   Foundation                          0x00000001c5544370 <redacted> + 540
    3   Foundation                          0x00000001c5543840 <redacted> + 396
    4   Foundation                          0x00000001c554358c <redacted> + 272
    5   Foundation                          0x00000001c5542e10 <redacted> + 264
    6   UIKitCore                           0x00000001f20d62e4 <redacted> + 488
    7   UIKitCore                           0x00000001f20d67b0 <redacted> + 36
    8   UIKitCore                           0x00000001f20d6eb0 <redacted> + 84
    9   Foundation                          0x00000001c571d124 <redacted> + 76
    10  Foundation                          0x00000001c54ff30c <redacted> + 108
    11  Foundation                          0x00000001c54fe304 <redacted> + 328
    12  UIKitCore                           0x00000001f151dc0c <redacted> + 156
    13  UIKitCore                           0x00000001f151e0c0 <redacted> + 152
    14  UIKitCore                           0x00000001f1514834 <redacted> + 868
    15  UIKitCore                           0x00000001f1518760 <redacted> + 104
    16  UIKitCore                           0x00000001f1543370 <redacted> + 1772
    17  UIKitCore                           0x00000001f1546598 <redacted> + 120
    18  UIKitCore                           0x00000001f14fc850 <redacted> + 1452
    19  UIKitCore                           0x00000001f168f318 <redacted> + 196
    20  UIKitCore                           0x00000001f168d330 <redacted> + 144
    21  AppName                        0x0000000100b8ed00 $S12AppName18ViewControllerC11Func()ySS_S2StF + 4420
    22  AppName                        0x0000000100b8d9f4 $S12CcfU0_y10Foundation4DataVSg_So13NSURLResponseCSgs5Error_pSgtcfU_ + 2384
    23  App NAme                        0x0000000100a98f3c $S10Foundation4DataVSgSo13NSURLResponseCSgs5Error_pSgIegggg_So6NSDataCSgAGSo7NSErrorCSgIeyByyy_TR + 316
    24  CFNetwork                           0x00000001c513aa00 <redacted> + 32
    25  CFNetwork                           0x00000001c514f1a0 <redacted> + 176
    26  Foundation                          0x00000001c55ed8bc <redacted> + 16
    27  Foundation                          0x00000001c54f5ab8 <redacted> + 72
    28  Foundation                          0x00000001c54f4f8c <redacted> + 740
    29  Foundation                          0x00000001c55ef790 <redacted> + 272
    30  libdispatch.dylib                   0x000000010286f824 _dispatch_call_block_and_release + 24
    31  libdispatch.dylib                   0x0000000102870dc8 _dispatch_client_callout + 16
    32  libdispatch.dylib                   0x00000001028741c4 _dispatch_continuation_pop + 528
    33  libdispatch.dylib                   0x0000000102873604 _dispatch_async_redirect_invoke + 632
    34  libdispatch.dylib                   0x00000001028821dc _dispatch_root_queue_drain + 376
    35  libdispatch.dylib                   0x0000000102882bc8 _dispatch_worker_thread2 + 156
    36  libsystem_pthread.dylib             0x00000001c477917c _pthread_wqthread + 472
    37  libsystem_pthread.dylib             0x00000001c477bcec start_wqthread + 4
)

0

また、ウィンドウのサイズを初期値よりも小さいサイズに変更したときに、これらの大量のメッセージとスタックトレースが出力に出力されるのを確認して、この問題にも遭遇しました。問題を理解するのに長い時間を費やしていたので、かなり簡単な解決策を共有したいと思いました。私はかつてIBで有効Can Draw ConcurrentlyにしNSTextViewていました。これにより、AppKit draw(_:)は別のスレッドからビューのメソッドを呼び出すことができることがわかります。無効にすると、エラーメッセージが表示されなくなります。macOS 10.14 Betaにアップデートする前は問題はありませんでしたが、同時に、テキストビューで動作するようにコードを変更し始めました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.