-
-
Save andrewmichaelson/8264280 to your computer and use it in GitHub Desktop.
Modified to work with AppKit on OS X instead of UIKit on iOS.
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) 2013 Peter Steinberger. 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. | |
#import <objc/runtime.h> | |
#import <objc/message.h> | |
#import <Foundation/Foundation.h> | |
#import <Cocoa/Cocoa.h> | |
// Compile-time selector checks. | |
#if DEBUG | |
#define PROPERTY(propName) NSStringFromSelector(@selector(propName)) | |
#else | |
#define PROPERTY(propName) @#propName | |
#endif | |
// 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) | |
/////////////////////////////////////////////////////////////////////////////////////////// | |
#pragma mark - Helper for Swizzling | |
BOOL PSPDFReplaceMethodWithBlock(Class c, SEL origSEL, SEL newSEL, id block) { | |
PSPDFAssert(c && origSEL && newSEL && block); | |
Method origMethod = class_getInstanceMethod(c, origSEL); | |
const char *encoding = method_getTypeEncoding(origMethod); | |
// Add the new method. | |
IMP impl = imp_implementationWithBlock(block); | |
if (!class_addMethod(c, newSEL, impl, encoding)) { | |
NSLog(@"Failed to add method: %@ on %@", NSStringFromSelector(newSEL), c); | |
return NO; | |
} else { | |
// Ensure the new selector has the same parameters as the existing selector. | |
Method newMethod = class_getInstanceMethod(c, newSEL); | |
PSPDFAssert(strcmp(method_getTypeEncoding(origMethod), method_getTypeEncoding(newMethod)) == 0, @"Encoding must be the same."); | |
// If original doesn't implement the method we want to swizzle, create it. | |
if (class_addMethod(c, origSEL, method_getImplementation(newMethod), encoding)) { | |
class_replaceMethod(c, newSEL, method_getImplementation(origMethod), encoding); | |
} else { | |
method_exchangeImplementations(origMethod, newMethod); | |
} | |
} | |
return YES; | |
} | |
/////////////////////////////////////////////////////////////////////////////////////////// | |
#pragma mark - Tracks down calls to AppKit from a Thread other than Main | |
static void PSPDFAssertIfNotMainThread(void) { | |
PSPDFAssert(NSThread.isMainThread, @"\nERROR: All calls to AppKit 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 AppKit. | |
// 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 PSPDFAppKitMainThreadGuard(void) { | |
@autoreleasepool { | |
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(NSView.class, selector, newSelector, ^(__unsafe_unretained NSView *_self, CGRect r) { | |
PSPDFAssertIfNotMainThread(); | |
((void ( *)(id, SEL, CGRect))objc_msgSend)(_self, newSelector, r); | |
}); | |
} else { | |
PSPDFReplaceMethodWithBlock(NSView.class, selector, newSelector, ^(__unsafe_unretained NSView *_self) { | |
PSPDFAssertIfNotMainThread(); | |
((void ( *)(id, SEL))objc_msgSend)(_self, newSelector); | |
}); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Sorry, I meant El Capitan 10.11.3, XCode 7.2.1 and XCode 7.3beta5.
Further, I just noticed that a TableView in the Main Window is actually trying to refresh something, while the open panel is waiting, but I am still puzzled why this could involve an additional thread.