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