error : This application is modifying the autolayout engine from a background thread
此問題是由于在子的線程中更新了 UI 所導致的,
解決方法:找到在子線程中更新ui的代碼,將他們在主線程更新即可;
但 Xcode 提示錯誤信息不太明確,在調用堆棧根本無法發現錯誤是由哪句代碼導致的,當然要調試這個問題可以使用PSPDFUIKitMainThreadGuard.m 定位錯誤的代碼,這個是 PSPDFKit 框架中用到的專門查找代碼中哪些代碼會在輔線程中更新 UI 的工具,PSPDFUIKitMainThreadGuard是通過在攔截所有 UIKit 中對 setNeedsDisplay 和 setNeedsLayout 的調用,判斷當前線程如果是子線程則拋出異常
使用時將PSPDFUIKitMainThreadGuard手動拖入項目,并將其編譯選項設置為 -fno-objc-arc
運行后,當有任何類似的錯誤發生時,Xcode 會在錯誤發生的代碼停下
部分代碼:
for (NSString *selStr in @[PROPERTY(setNeedsLayout), PROPERTY(setNeedsDisplay), PROPERTY(setNeedsDisplayInRect:)]) {
SEL selector = NSSelectorFromString(selStr);
SEL newSelector = NSSelectorFromString([NSString stringWithFormat:@"pspdf_%@", selStr]);
if ([selStr hasSuffix:@":"]) {
PSPDFReplaceMethodWithBlock(UIView.class, selector, newSelector, ^(__unsafe_unretained UIView *_self, CGRect r) {
// Check for window, since *some* UIKit methods are indeed thread safe.
// https://developer.apple.com/library/ios/#releasenotes/General/WhatsNewIniPhoneOS/Articles/iPhoneOS4.html
/*
Drawing to a graphics context in UIKit is now thread-safe. Specifically:
The routines used to access and manipulate the graphics context can now correctly handle contexts residing on different threads.
String and image drawing is now thread-safe.
Using color and font objects in multiple threads is now safe to do.
*/
if (_self.window) PSPDFAssertIfNotMainThread();
((void ( *)(id, SEL, CGRect))objc_msgSend)(_self, newSelector, r);
});
}else {
PSPDFReplaceMethodWithBlock(UIView.class, selector, newSelector, ^(__unsafe_unretained UIView *_self) {
if (_self.window) {
if (!NSThread.isMainThread) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
dispatch_queue_t queue = dispatch_get_current_queue();
#pragma clang diagnostic pop
// iOS 8 layouts the MFMailComposeController in a background thread on an UIKit queue.
// https://github.com/PSPDFKit/PSPDFKit/issues/1423
if (!queue || !strstr(dispatch_queue_get_label(queue), "UIKit")) {
PSPDFAssertIfNotMainThread();
}
}
}
((void ( *)(id, SEL))objc_msgSend)(_self, newSelector);
});
}
}
}
}