Forked from steipete/PSPDFUIKitMainThreadGuard.m
Last active
October 28, 2019 11:15
-
-
Save tony0x59/7fe8a74d00fc5f27726e to your computer and use it in GitHub Desktop.
iOS,在DEBUG模式自动探测非主线程视图更新操作,将.m加入项目即可生效。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Taken from the commercial iOS PDF framework http://pspdfkit.com. | |
// Copyright (c) 2014 Peter Steinberger, PSPDFKit GmbH. All rights reserved. | |
// Licensed under MIT (http://opensource.org/licenses/MIT) | |
// | |
// You should only use this in debug builds. It doesn't use private API, but I wouldn't ship it. | |
#ifdef DEBUG //only use this in debug builds | |
#import <objc/runtime.h> | |
#import <objc/message.h> | |
// Compile-time selector checks. | |
#if DEBUG | |
#define PROPERTY(propName) NSStringFromSelector(@selector(propName)) | |
#else | |
#define PROPERTY(propName) @#propName | |
#endif | |
#define PSPDFAssert(expression, ...) \ | |
do { if(!(expression)) { \ | |
NSLog(@"%@", [NSString stringWithFormat: @"Assertion failure: %s in %s on line %s:%d. %@", #expression, __PRETTY_FUNCTION__, __FILE__, __LINE__, [NSString stringWithFormat:@"" __VA_ARGS__]]); \ | |
abort(); }} while(0) | |
#define PSPDFLogError NSLog | |
// http://www.mikeash.com/pyblog/friday-qa-2010-01-29-method-replacement-for-fun-and-profit.html | |
BOOL PSPDFReplaceMethodWithBlock(Class c, SEL origSEL, SEL newSEL, id block) { | |
PSPDFAssert(c && origSEL && newSEL && block); | |
if ([c instancesRespondToSelector:newSEL]) return YES; // Selector already implemented, skip silently. | |
Method origMethod = class_getInstanceMethod(c, origSEL); | |
// Add the new method. | |
IMP impl = imp_implementationWithBlock(block); | |
if (!class_addMethod(c, newSEL, impl, method_getTypeEncoding(origMethod))) { | |
PSPDFLogError(@"Failed to add method: %@ on %@", NSStringFromSelector(newSEL), c); | |
return NO; | |
}else { | |
Method newMethod = class_getInstanceMethod(c, newSEL); | |
// If original doesn't implement the method we want to swizzle, create it. | |
if (class_addMethod(c, origSEL, method_getImplementation(newMethod), method_getTypeEncoding(origMethod))) { | |
class_replaceMethod(c, newSEL, method_getImplementation(origMethod), method_getTypeEncoding(newMethod)); | |
}else { | |
method_exchangeImplementations(origMethod, newMethod); | |
} | |
} | |
return YES; | |
} | |
SEL _PSPDFPrefixedSelector(SEL selector) { | |
return NSSelectorFromString([NSString stringWithFormat:@"pspdf_%@", NSStringFromSelector(selector)]); | |
} | |
void PSPDFAssertIfNotMainThread(void) { | |
// PSPDFAssert(NSThread.isMainThread, @"\nERROR: All calls to UIKit need to happen on the main thread. You have a bug in your code. Use dispatch_async(dispatch_get_main_queue(), ^{ ... }); if you're unsure what thread you're in.\n\nBreak on PSPDFAssertIfNotMainThread to find out where.\n\nStacktrace: %@", NSThread.callStackSymbols); | |
if (!NSThread.isMainThread) { | |
@try { | |
PSPDFLogError(@"ERROR: All calls to UIKit need to happen on the main thread. You have a bug in your code. Use dispatch_async(dispatch_get_main_queue(), ^{ ... }); if you're unsure what thread you're in.\n\nBreak on PSPDFAssertIfNotMainThread to find out where.\n"); | |
PSPDFLogError(@"Stacktrace: %@", NSThread.callStackSymbols); | |
@throw [NSException exceptionWithName:@"UIKit-MainThreadGuard" reason:@"All calls to UIKit need to happen on the main thread." userInfo:nil]; | |
} | |
@catch (NSException *exception) { | |
// 为了让Xcode进入断点 | |
NSLog(@"%@", exception); | |
} | |
} | |
} | |
__attribute__((constructor)) static void PSPDFUIKitMainThreadGuard(void) { | |
@autoreleasepool { | |
NSLog(@">>>> PSPDFUIKitMainThreadGuard Enabled"); | |
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); | |
}); | |
} | |
} | |
} | |
} | |
#endif //ifdef DEBUG |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment