webentwicklung-frage-antwort-db.com.de

iOS9 Diese Anwendung ändert die Autolayout-Engine von einem Hintergrund-Thread aus, was zu einer Beschädigung der Engine und zu seltsamen Abstürzen führen kann

Ich habe gerade den neuesten XCode (7.1 Beta) heruntergeladen und mit iOS9 herumgespielt.

Ich hatte eine App, die einwandfrei und ohne Fehler in iOS8 funktioniert. Jetzt erhalte ich jedoch den folgenden Fehler, wenn Sie die drawRect-Methode in einer UITableViewCell-Klasse überschreiben:

"Diese Anwendung modifiziert die Autolayout-Engine von einem Hintergrund-Thread aus, was zu einer Beschädigung der Engine und zu seltsamen Abstürzen führen kann. Dies führt in einer zukünftigen Version zu einer Ausnahme."

hier ist der Rückweg: 

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
)

Hier ist die drawRect-Methode:

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


}

Ich verstehe (vgl. diese Frage ) ist, dass komplizierte Berechnungen usw. in einem Hintergrundthread und die Aktualisierung der Benutzeroberfläche auf dem Hauptthread ausgeführt werden sollten, da die Benutzeroberfläche nicht threadsicher ist.

WENN ich jedoch Folgendes verwenden: 

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


}

Ich bekomme jetzt einen CGContext-Fehler…

<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.

Irgendwelche Vorschläge ?

LÖSUNG: OK. Hier ist der Deal. Ich habe ein Bild asynchron in eine UIImageView geladen, aber nicht in der Benutzeroberfläche im Haupt-Thread "gemalt", sondern in einem Hintergrund-Thread. Außerdem; Ich habe setNeedsDisplay auf der UITableViewCell aufgerufen, nachdem ich das Image zur UI hinzugefügt habe, also die drawRect-Methode erneut aufgerufen habe, diesmal jedoch im Hintergrundthread. 

12
Benjamin

Der erste Fehler, den Sie beachten, sieht nicht so aus, als ob er mit Ihrem drawRect:-Code zusammenhängt. Ihr drawRect: sieht gut aus. Es macht nichts besonders kompliziertes. Wenn Ihr Pfad komplexer wäre, möchten Sie ihn wahrscheinlich zwischenspeichern, aber es ist wahrscheinlich in Ordnung, wie er ist. Wahrscheinlich versuchen Sie, die Benutzeroberfläche an einer anderen Stelle in einem Hintergrund-Thread zu ändern. Es kann nur entstehen, wenn Sie drawRect: überschreiben, da sich dadurch das Update der Benutzeroberfläche ändert (ohne einen benutzerdefinierten drawRect: kann das System möglicherweise einfache Transformationen anwenden). Sie müssen nachsehen, wo Sie in einem Hintergrund-Thread einen UIKit-Anruf tätigen.

Wenn es im Zweifel mit UI beginnt, können Sie es wahrscheinlich nicht in einem Hintergrundthread verwenden. Das trifft nicht völlig zu (Sie können eine UIBezierPath in einem Hintergrund-Thread erstellen und sie sogar in einen Nicht-Bildschirm-Kontext zeichnen), aber als erste Annäherung ist es eine gute Sache, nach dieser zu suchen.

Dieser Code ist einfach falsch:

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

Der Aufruf von drawRect: sollte besser bereits in der Hauptwarteschlange be sein (daher ist das Versenden in die Hauptwarteschlange nicht hilfreich). Wenn nicht, siehe oben. Zum Zeitpunkt, zu dem dieser Block ausgeführt wird, ist der Zeichenzyklus beendet, sodass es keinen Kontext gibt, in den mehr gezogen werden kann.

22
Rob Napier

Grundsätzlich gibt 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 an, dass Sie die Benutzeroberfläche nicht aktualisieren sollten, insbesondere den mit Autolayout verbundenen Teil in Threads, die keine Benutzeroberfläche sind. Um dies zu beheben, verwenden Sie dispatch_async(dispatch_get_main_queue()) { // your code } oder

Swift 3:


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

Versuchen Sie den symbolischen Haltepunkt, um das Problem zu erkennen: -  enter image description here

Fügen Sie dann Ihren UI-Update-Code in den Haupt-Thread ein

DispatchQueue.main.async {}
0
Abdul Hameed