-
-
Save jerrymarino/5685341 to your computer and use it in GitHub Desktop.
This file contains 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
#import <objc/runtime.h> | |
#import <objc/message.h> | |
#import <QuartzCore/QuartzCore.h> | |
#define PROPERTY(propName) NSStringFromSelector(@selector(propName)) | |
// A better assert. NSAssert is too runtime dependant, and assert() doesn't log. | |
// http://www.mikeash.com/pyblog/friday-qa-2013-05-03-proper-use-of-asserts.html | |
// Accepts both: | |
// - PSPDFAssert(x > 0); | |
// - PSPDFAssert(y > 3, @"Bad value for y"); | |
#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) | |
// You should only use this in debug builds. It doesn't use private API, but I wouldn't ship it. | |
#ifdef DEBUG | |
static void PSPDFSwizzleMethod(Class c, SEL orig, SEL new) { | |
Method origMethod = class_getInstanceMethod(c, orig); | |
Method newMethod = class_getInstanceMethod(c, new); | |
if (class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) { | |
class_replaceMethod(c, new, method_getImplementation(origMethod), method_getTypeEncoding(origMethod)); | |
}else { | |
method_exchangeImplementations(origMethod, newMethod); | |
} | |
} | |
void PSPDFReplaceMethod(Class c, SEL orig, SEL newSel, IMP impl) { | |
Method method = class_getInstanceMethod(c, orig); | |
if (!class_addMethod(c, newSel, impl, method_getTypeEncoding(method))) { | |
PSPDFLogError(@"Failed to add method: %@ on %@", NSStringFromSelector(newSel), c); | |
}else PSPDFSwizzleMethod(c, orig, newSel); | |
} | |
/////////////////////////////////////////////////////////////////////////////////////////// | |
#pragma mark - Tracks down calls to UIKit from a Thread other than Main | |
static 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]); | |
} | |
// This installs a small guard that checks for the most common threading-errors in UIKit. | |
// This won't really slow down performance but still only is compiled in DEBUG versions of PSPDFKit. | |
// @note No private API is used here. | |
__attribute__((constructor)) static void PSPDFUIKitMainThreadGuard(void) { | |
@autoreleasepool { | |
for (NSString *selector in @[PROPERTY(setNeedsLayout), PROPERTY(setNeedsDisplay), PROPERTY(setNeedsDisplayInRect:)]) { | |
SEL newSelector = NSSelectorFromString([NSString stringWithFormat:@"pspdf_%@", selector]); | |
if ([selector hasSuffix:@":"]) { | |
PSPDFReplaceMethod(UIView.class, NSSelectorFromString(selector), newSelector, imp_implementationWithBlock(^(UIView *_self, CGRect r) { | |
PSPDFAssertIfNotMainThread(); | |
((void ( *)(id, SEL, CGRect))objc_msgSend)(_self, newSelector, r); | |
})); | |
}else { | |
PSPDFReplaceMethod(UIView.class, NSSelectorFromString(selector), newSelector, imp_implementationWithBlock(^(UIView *_self) { | |
PSPDFAssertIfNotMainThread(); | |
((void ( *)(id, SEL))objc_msgSend)(_self, newSelector); | |
})); | |
} | |
} | |
} | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment