Create a gist now

Instantly share code, notes, and snippets.

Embed
What would you like to do?
This is a guard that tracks down UIKit access on threads other than main. This snippet is taken from the commercial iOS PDF framework http://pspdfkit.com, but relicensed under MIT. Works because a lot of calls internally call setNeedsDisplay or setNeedsLayout. Won't catch everything, but it's very lightweight and usually does the job.You might n…
// 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.
// PLEASE DUPE rdar://27192338 (https://openradar.appspot.com/27192338) if you would like to see this in UIKit.
#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
// 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) {
NSCParameterAssert(c);
NSCParameterAssert(origSEL);
NSCParameterAssert(newSEL);
NSCParameterAssert(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)]);
}
#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)
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);
}
__attribute__((constructor)) static void PSPDFUIKitMainThreadGuard(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(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);
});
}
}
}
}
@NorrinRadd

This comment has been minimized.

Show comment
Hide comment
@NorrinRadd

NorrinRadd May 28, 2013

PSPDFAssert doesn't seem to be defined.

PSPDFAssert doesn't seem to be defined.

@steipete

This comment has been minimized.

Show comment
Hide comment
@steipete

steipete May 28, 2013

@NorrinRadd: Thanks! I knew something was missing. Added it in the latest version of my gist.

Owner

steipete commented May 28, 2013

@NorrinRadd: Thanks! I knew something was missing. Added it in the latest version of my gist.

@kolyuchiy

This comment has been minimized.

Show comment
Hide comment
@kolyuchiy

kolyuchiy May 28, 2013

Triggers on WebThread

* thread #5: tid = 0x2a03, 0x3bd97350 libsystem_kernel.dylib`__pthread_kill + 8, stop reason = signal SIGABRT
    frame #0: 0x3bd97350 libsystem_kernel.dylib`__pthread_kill + 8
    frame #1: 0x3bd0dfb6 libsystem_c.dylib`pthread_kill + 58
    frame #2: 0x3bd4a36a libsystem_c.dylib`abort + 94
    frame #3: 0x002b5236 XXX`PSPDFAssertIfNotMainThread + 358 at PSPDFUIKitMainThreadGuard.m:41
    frame #4: 0x002b5254 XXX`__PSPDFUIKitMainThreadGuard_block_invoke_2(.block_descriptor=0x1e0202d0, _self=0x1e19cdb0) + 24 at PSPDFUIKitMainThreadGuard.m:59
    frame #5: 0x3576a608 QuartzCore`CA::Layer::property_did_change(CA::Transaction*, unsigned int) + 1308
    frame #6: 0x3576a0c4 QuartzCore`CA::Layer::end_change(CA::Transaction*, unsigned int, objc_object*) + 64
    frame #7: 0x357695a0 QuartzCore`CA::Layer::remove_sublayer(CA::Transaction*, CALayer*) + 200
    frame #8: 0x3576946e QuartzCore`CA::Layer::remove_from_superlayer() + 38
    frame #9: 0x39c862a8 WebCore`WebCore::TileGridTile::~TileGridTile() + 132
    frame #10: 0x39c8621e WebCore`WebCore::TileGridTile::~TileGridTile() + 10
    frame #11: 0x39b80fa8 WebCore`WTF::HashTable<WebCore::IntPoint, std::__1::pair<WebCore::IntPoint, WTF::RefPtr<WebCore::TileGridTile> >, WTF::PairFirstExtractor<std::__1::pair<WebCore::IntPoint, WTF::RefPtr<WebCore::TileGridTile> > >, WTF::IntPointHash, WTF::HashMapValueTraits<WTF::HashTraits<WebCore::IntPoint>, WTF::HashTraits<WTF::RefPtr<WebCore::TileGridTile> > >, WTF::HashTraits<WebCore::IntPoint> >::deallocateTable(std::__1::pair<WebCore::IntPoint, WTF::RefPtr<WebCore::TileGridTile> >*, int) + 152
    frame #12: 0x39d746f4 WebCore`WebCore::TileGrid::~TileGrid() + 92
    frame #13: 0x39d74528 WebCore`WebCore::TileCache::~TileCache() + 228
    frame #14: 0x39d7443e WebCore`WebCore::TileCache::~TileCache() + 10
    frame #15: 0x39d74398 WebCore`-[WAKWindow dealloc] + 132
    frame #16: 0x39b8b840 WebCore`HandleWebThreadReleaseSource(void*) + 116
    frame #17: 0x33b7d8f6 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 14
    frame #18: 0x33b7d15c CoreFoundation`__CFRunLoopDoSources0 + 212
    frame #19: 0x33b7bf2e CoreFoundation`__CFRunLoopRun + 646
    frame #20: 0x33aef23c CoreFoundation`CFRunLoopRunSpecific + 356
    frame #21: 0x33aef0c8 CoreFoundation`CFRunLoopRunInMode + 104
    frame #22: 0x39af7394 WebCore`RunWebThread(void*) + 444
    frame #23: 0x3bcf00e0 libsystem_c.dylib`_pthread_start + 308

Triggers on WebThread

* thread #5: tid = 0x2a03, 0x3bd97350 libsystem_kernel.dylib`__pthread_kill + 8, stop reason = signal SIGABRT
    frame #0: 0x3bd97350 libsystem_kernel.dylib`__pthread_kill + 8
    frame #1: 0x3bd0dfb6 libsystem_c.dylib`pthread_kill + 58
    frame #2: 0x3bd4a36a libsystem_c.dylib`abort + 94
    frame #3: 0x002b5236 XXX`PSPDFAssertIfNotMainThread + 358 at PSPDFUIKitMainThreadGuard.m:41
    frame #4: 0x002b5254 XXX`__PSPDFUIKitMainThreadGuard_block_invoke_2(.block_descriptor=0x1e0202d0, _self=0x1e19cdb0) + 24 at PSPDFUIKitMainThreadGuard.m:59
    frame #5: 0x3576a608 QuartzCore`CA::Layer::property_did_change(CA::Transaction*, unsigned int) + 1308
    frame #6: 0x3576a0c4 QuartzCore`CA::Layer::end_change(CA::Transaction*, unsigned int, objc_object*) + 64
    frame #7: 0x357695a0 QuartzCore`CA::Layer::remove_sublayer(CA::Transaction*, CALayer*) + 200
    frame #8: 0x3576946e QuartzCore`CA::Layer::remove_from_superlayer() + 38
    frame #9: 0x39c862a8 WebCore`WebCore::TileGridTile::~TileGridTile() + 132
    frame #10: 0x39c8621e WebCore`WebCore::TileGridTile::~TileGridTile() + 10
    frame #11: 0x39b80fa8 WebCore`WTF::HashTable<WebCore::IntPoint, std::__1::pair<WebCore::IntPoint, WTF::RefPtr<WebCore::TileGridTile> >, WTF::PairFirstExtractor<std::__1::pair<WebCore::IntPoint, WTF::RefPtr<WebCore::TileGridTile> > >, WTF::IntPointHash, WTF::HashMapValueTraits<WTF::HashTraits<WebCore::IntPoint>, WTF::HashTraits<WTF::RefPtr<WebCore::TileGridTile> > >, WTF::HashTraits<WebCore::IntPoint> >::deallocateTable(std::__1::pair<WebCore::IntPoint, WTF::RefPtr<WebCore::TileGridTile> >*, int) + 152
    frame #12: 0x39d746f4 WebCore`WebCore::TileGrid::~TileGrid() + 92
    frame #13: 0x39d74528 WebCore`WebCore::TileCache::~TileCache() + 228
    frame #14: 0x39d7443e WebCore`WebCore::TileCache::~TileCache() + 10
    frame #15: 0x39d74398 WebCore`-[WAKWindow dealloc] + 132
    frame #16: 0x39b8b840 WebCore`HandleWebThreadReleaseSource(void*) + 116
    frame #17: 0x33b7d8f6 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 14
    frame #18: 0x33b7d15c CoreFoundation`__CFRunLoopDoSources0 + 212
    frame #19: 0x33b7bf2e CoreFoundation`__CFRunLoopRun + 646
    frame #20: 0x33aef23c CoreFoundation`CFRunLoopRunSpecific + 356
    frame #21: 0x33aef0c8 CoreFoundation`CFRunLoopRunInMode + 104
    frame #22: 0x39af7394 WebCore`RunWebThread(void*) + 444
    frame #23: 0x3bcf00e0 libsystem_c.dylib`_pthread_start + 308
@steipete

This comment has been minimized.

Show comment
Hide comment
@steipete

steipete May 28, 2013

@kolyuchiy Just noticed that myself. Nasty UIWebView accesses CALayer from a background thread. I've updated my gist accordingly.

Owner

steipete commented May 28, 2013

@kolyuchiy Just noticed that myself. Nasty UIWebView accesses CALayer from a background thread. I've updated my gist accordingly.

@WayneWeiZhang

This comment has been minimized.

Show comment
Hide comment
@WayneWeiZhang

WayneWeiZhang May 29, 2013

Hi Peter,
I found an issue when I alloc UIWebview in iOS 5 on main thread, EXC_BAD_ACCESS will show.
However the environment is ARC enabled, Do you have any idea about this?

Hi Peter,
I found an issue when I alloc UIWebview in iOS 5 on main thread, EXC_BAD_ACCESS will show.
However the environment is ARC enabled, Do you have any idea about this?

@WayneWeiZhang

This comment has been minimized.

Show comment
Hide comment
@WayneWeiZhang

WayneWeiZhang May 29, 2013

UIKit`-[UIWebView retain]:
0x1d60d98: pushl %ebp
0x1d60d99: movl %esp, %ebp
0x1d60d9b: pushl %esi
0x1d60d9c: calll 0x1d60da1 ; -[UIWebView retain] + 9
0x1d60da1: popl %ecx
0x1d60da2: movl 5217911(%ecx), %edx
0x1d60da8: movl 8(%ebp), %eax
0x1d60dab: movl (%eax,%edx), %edx
0x1d60dae: movl 5217887(%ecx), %ecx
0x1d60db4: movl $2, %esi
0x1d60db9: lock <---------------------------------------------------------------------------------------Where showed BAD_ACCESS
0x1d60dba: xaddl %esi, (%edx,%ecx)
0x1d60dbe: testl %esi, %esi
0x1d60dc0: jns 0x1d60dc4 ; -[UIWebView retain] + 44
0x1d60dc2: ud2
0x1d60dc4: popl %esi
0x1d60dc5: popl %ebp
0x1d60dc6: ret

UIKit`-[UIWebView retain]:
0x1d60d98: pushl %ebp
0x1d60d99: movl %esp, %ebp
0x1d60d9b: pushl %esi
0x1d60d9c: calll 0x1d60da1 ; -[UIWebView retain] + 9
0x1d60da1: popl %ecx
0x1d60da2: movl 5217911(%ecx), %edx
0x1d60da8: movl 8(%ebp), %eax
0x1d60dab: movl (%eax,%edx), %edx
0x1d60dae: movl 5217887(%ecx), %ecx
0x1d60db4: movl $2, %esi
0x1d60db9: lock <---------------------------------------------------------------------------------------Where showed BAD_ACCESS
0x1d60dba: xaddl %esi, (%edx,%ecx)
0x1d60dbe: testl %esi, %esi
0x1d60dc0: jns 0x1d60dc4 ; -[UIWebView retain] + 44
0x1d60dc2: ud2
0x1d60dc4: popl %esi
0x1d60dc5: popl %ebp
0x1d60dc6: ret

@steipete

This comment has been minimized.

Show comment
Hide comment
@steipete

steipete May 29, 2013

@WayneWeiZhang You might want to compile this debug helper without ARC.

Owner

steipete commented May 29, 2013

@WayneWeiZhang You might want to compile this debug helper without ARC.

@jklundell

This comment has been minimized.

Show comment
Hide comment
@jklundell

jklundell May 31, 2013

PSPDFLogError isn't defined, either. Any reason PSPDFReplaceMethod isn't static?

PSPDFLogError isn't defined, either. Any reason PSPDFReplaceMethod isn't static?

@steipete

This comment has been minimized.

Show comment
Hide comment
@steipete

steipete May 31, 2013

Just use NSLog then. I really copied this from my commercial framework PSPDFKit, where the swizzle/replace is defined in the header. For this example having it static makes sense, yes.

Owner

steipete commented May 31, 2013

Just use NSLog then. I really copied this from my commercial framework PSPDFKit, where the swizzle/replace is defined in the header. For this example having it static makes sense, yes.

@posburn

This comment has been minimized.

Show comment
Hide comment
@posburn

posburn May 31, 2013

Super helpful @steipete - thanks! Helped me track down a bug that has been in the codebase for a while now and was tricky to reproduce.

posburn commented May 31, 2013

Super helpful @steipete - thanks! Helped me track down a bug that has been in the codebase for a while now and was tricky to reproduce.

@jklundell

This comment has been minimized.

Show comment
Hide comment
@jklundell

jklundell Jun 1, 2013

I'm considering enabling this in non-debug builds, changing the assert to a Crashlytics log call. The idea is to have a clue in the crash report that this is what (likely) caused the crash. How bad an idea is that?

I'm considering enabling this in non-debug builds, changing the assert to a Crashlytics log call. The idea is to have a clue in the crash report that this is what (likely) caused the crash. How bad an idea is that?

@chandrahasan1

This comment has been minimized.

Show comment
Hide comment
@chandrahasan1

chandrahasan1 Jun 1, 2013

Peter, how can I use this gist? Just include the .m file in the project? Is that all? Thanks.

Peter, how can I use this gist? Just include the .m file in the project? Is that all? Thanks.

@steipete

This comment has been minimized.

Show comment
Hide comment
@steipete

steipete Jun 2, 2013

@jklundell It doesn't use any private API and the performance impact is minimal - so you should be fine. Just don't hard-crash here, we don't know if Apple always plays by the rules (e.g. they use CALayer in a thread with UIWebView...)

@chandrahasan1put it in a file and compile without ARC.

Owner

steipete commented Jun 2, 2013

@jklundell It doesn't use any private API and the performance impact is minimal - so you should be fine. Just don't hard-crash here, we don't know if Apple always plays by the rules (e.g. they use CALayer in a thread with UIWebView...)

@chandrahasan1put it in a file and compile without ARC.

@segiddins

This comment has been minimized.

Show comment
Hide comment
@segiddins

segiddins Jun 2, 2013

@steipete why does the file have to be compiled without ARC?

@steipete why does the file have to be compiled without ARC?

@steipete

This comment has been minimized.

Show comment
Hide comment
@steipete

steipete Jun 2, 2013

@segiddins It probably will work as well with ARC, but the compiler might put retains/releases in there where we don't want them, e.g. when calls happen during dealloc where we would then create a (short) resurrection of the object.

Update: I've added __unsafe_unretained, that should do the trick as well.

Owner

steipete commented Jun 2, 2013

@segiddins It probably will work as well with ARC, but the compiler might put retains/releases in there where we don't want them, e.g. when calls happen during dealloc where we would then create a (short) resurrection of the object.

Update: I've added __unsafe_unretained, that should do the trick as well.

@xanderdunn

This comment has been minimized.

Show comment
Hide comment
@xanderdunn

xanderdunn Jun 2, 2013

This is excellent. I was just recently wondering why this isn't already in Cocoa. Many thanks!

This is excellent. I was just recently wondering why this isn't already in Cocoa. Many thanks!

@pietrorea

This comment has been minimized.

Show comment
Hide comment
@pietrorea

pietrorea Jun 4, 2013

That's great! I wonder if you can use the same approach to check if an NSManagedObjectContext is being accessed from a thread other than the one it was created on

That's great! I wonder if you can use the same approach to check if an NSManagedObjectContext is being accessed from a thread other than the one it was created on

@michaeleisel

This comment has been minimized.

Show comment
Hide comment
@michaeleisel

michaeleisel Jul 10, 2013

When I run it in my app delegate, it tells me "failed to add method: pspdf_..." for every method.

When I run it in my app delegate, it tells me "failed to add method: pspdf_..." for every method.

@admmasters

This comment has been minimized.

Show comment
Hide comment
@admmasters

admmasters Aug 2, 2013

Same as michaeleisel. Have the following output in the console: 2013-08-02 09:26:31:640 [846:3079] Failed to add method: pspdf_setNeedsLayout on UIView
[846:3079] Failed to add method: pspdf_setNeedsDisplay on UIView
[846:3079] Failed to add method: pspdf_setNeedsDisplayInRect: on UIView

This is after calling: PSPDFAssertIfNotMainThread()

Same as michaeleisel. Have the following output in the console: 2013-08-02 09:26:31:640 [846:3079] Failed to add method: pspdf_setNeedsLayout on UIView
[846:3079] Failed to add method: pspdf_setNeedsDisplay on UIView
[846:3079] Failed to add method: pspdf_setNeedsDisplayInRect: on UIView

This is after calling: PSPDFAssertIfNotMainThread()

@admmasters

This comment has been minimized.

Show comment
Hide comment
@admmasters

admmasters Aug 28, 2013

Ok don't worry - makes sense now.

Ok don't worry - makes sense now.

@andrewebling

This comment has been minimized.

Show comment
Hide comment
@andrewebling

andrewebling Sep 13, 2013

I have DEBUG=1 preprocessor macro enabled for only debug builds (and have confirmed #if DEBUG ...#endif works correctly elsewhere in my code). However this assertion code still seems to be present in Release builds - I've written a little test app which deliberately hits UIKit from a background thread to test it.

I have DEBUG=1 preprocessor macro enabled for only debug builds (and have confirmed #if DEBUG ...#endif works correctly elsewhere in my code). However this assertion code still seems to be present in Release builds - I've written a little test app which deliberately hits UIKit from a background thread to test it.

@chwalters

This comment has been minimized.

Show comment
Hide comment
@chwalters

chwalters Sep 26, 2013

This just saved me a few hours! Love it

This just saved me a few hours! Love it

@andrewmichaelson

This comment has been minimized.

Show comment
Hide comment
@andrewmichaelson

andrewmichaelson Jan 5, 2014

For those interested, I've forked the gist to make trivial changes so it works with AppKit on OS X.

For those interested, I've forked the gist to make trivial changes so it works with AppKit on OS X.

@soniccat

This comment has been minimized.

Show comment
Hide comment
@soniccat

soniccat Mar 7, 2014

Great tool, thanks!

soniccat commented Mar 7, 2014

Great tool, thanks!

@jomnius

This comment has been minimized.

Show comment
Hide comment
@jomnius

jomnius Jun 26, 2014

Thank you! Just found a brand new hibernating UI crash bug in code I'm maintaining, much appreciated!

jomnius commented Jun 26, 2014

Thank you! Just found a brand new hibernating UI crash bug in code I'm maintaining, much appreciated!

@k3zi

This comment has been minimized.

Show comment
Hide comment
@k3zi

k3zi Jul 11, 2014

Why don't we make a swizzle that automatically kicks every call to a UIKit object into the main thread???
Like this: https://gist.github.com/kdogisthebest/98ca835b15077d11dafc

k3zi commented Jul 11, 2014

Why don't we make a swizzle that automatically kicks every call to a UIKit object into the main thread???
Like this: https://gist.github.com/kdogisthebest/98ca835b15077d11dafc

@krzysztofzablocki

This comment has been minimized.

Show comment
Hide comment
@krzysztofzablocki

krzysztofzablocki Jul 22, 2014

@kdogisthebest because that would be hack, this gist is meant to find misuses instead of allowing them :)

@kdogisthebest because that would be hack, this gist is meant to find misuses instead of allowing them :)

@nightwolf-chen

This comment has been minimized.

Show comment
Hide comment
@nightwolf-chen

nightwolf-chen Sep 2, 2014

This is incredible!

This is incredible!

@jomnius

This comment has been minimized.

Show comment
Hide comment
@jomnius

jomnius Sep 17, 2014

Line 30: Implicit declaration of function "PSPDFAssert' is invalid in C99
Line 38: Implicit declaration of function "PSPDFLogError' is invalid in C99

First one can be fixed by moving PSPDFAssert definition higher in code, but the other one is a mystery. Guess it can be replaced by NSLog (or similar)? I put there PSPDFAssert for now.

jomnius commented Sep 17, 2014

Line 30: Implicit declaration of function "PSPDFAssert' is invalid in C99
Line 38: Implicit declaration of function "PSPDFLogError' is invalid in C99

First one can be fixed by moving PSPDFAssert definition higher in code, but the other one is a mystery. Guess it can be replaced by NSLog (or similar)? I put there PSPDFAssert for now.

@dlo

This comment has been minimized.

Show comment
Hide comment
@dlo

dlo Sep 21, 2014

Having the same issue(s) as @jomnius.

dlo commented Sep 21, 2014

Having the same issue(s) as @jomnius.

@danydev

This comment has been minimized.

Show comment
Hide comment
@danydev

danydev Oct 11, 2014

@dlo you just need to use NSLog instead of PSPDFLogError and move PSPDFAssert on line 16. I just created a fork that should compile right away with a few minor changes.
https://gist.github.com/danydev/9cb3539198c6af3446ed

danydev commented Oct 11, 2014

@dlo you just need to use NSLog instead of PSPDFLogError and move PSPDFAssert on line 16. I just created a fork that should compile right away with a few minor changes.
https://gist.github.com/danydev/9cb3539198c6af3446ed

@onmyway133

This comment has been minimized.

Show comment
Hide comment
@onmyway133

onmyway133 Feb 2, 2015

@andrewebling I think we should do compile time check around the constructor
@steipete How to avoid checking certain scenarios ? Like this http://stackoverflow.com/questions/25963367/mpvolumeview-initwithframestyle-not-called-on-main-thread-when-loading-uiweb

@andrewebling I think we should do compile time check around the constructor
@steipete How to avoid checking certain scenarios ? Like this http://stackoverflow.com/questions/25963367/mpvolumeview-initwithframestyle-not-called-on-main-thread-when-loading-uiweb

@vandadschibsted

This comment has been minimized.

Show comment
Hide comment
@vandadschibsted

vandadschibsted Apr 24, 2015

Thanks for this. Even though I didn't find anything, but it's good to know this exists...

I just did a test with this code:

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        UIButton *btn = [UIButton buttonWithType:UIButtonTypeSystem];
        btn.tag = 1;
        [self.view addSubview:btn];
    });

and this gist could not find this simple UI work on a non UI thread... what could be wrong?

Thanks for this. Even though I didn't find anything, but it's good to know this exists...

I just did a test with this code:

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        UIButton *btn = [UIButton buttonWithType:UIButtonTypeSystem];
        btn.tag = 1;
        [self.view addSubview:btn];
    });

and this gist could not find this simple UI work on a non UI thread... what could be wrong?

@jerry-sl

This comment has been minimized.

Show comment
Hide comment
@jerry-sl

jerry-sl Apr 28, 2015

Anyone use this gist in Swift project?

Anyone use this gist in Swift project?

@jeremangnr

This comment has been minimized.

Show comment
Hide comment
@jeremangnr

jeremangnr May 13, 2015

@vandadschibsted I was having the same issue and came up with a simple fix. I removed the if (_self.window) checks from the swizzled methods, that did the trick for me... thanks to this I found the cause of an issue that was disabling all animations across the app!

@vandadschibsted I was having the same issue and came up with a simple fix. I removed the if (_self.window) checks from the swizzled methods, that did the trick for me... thanks to this I found the cause of an issue that was disabling all animations across the app!

@RameshAran

This comment has been minimized.

Show comment
Hide comment
@RameshAran

RameshAran Sep 5, 2015

Thank you @jeremangnr.

After commenting the following condition check "_self.window", its working. Its capturing the inconsistencies like the following

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//UI updation
});

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock:^{
//UI updation
})];

I am getting the following Assertion failure.
Assertion failure: NSThread.isMainThread in void PSPDFAssertIfNotMainThread() on line /Users/<account_name>/Documents/Tutorials/TestSample/PSPDFUIKitMainThreadGuard.m:146.

Thanks you @steipete for sharing this. Its really useful.

Thank you @jeremangnr.

After commenting the following condition check "_self.window", its working. Its capturing the inconsistencies like the following

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//UI updation
});

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock:^{
//UI updation
})];

I am getting the following Assertion failure.
Assertion failure: NSThread.isMainThread in void PSPDFAssertIfNotMainThread() on line /Users/<account_name>/Documents/Tutorials/TestSample/PSPDFUIKitMainThreadGuard.m:146.

Thanks you @steipete for sharing this. Its really useful.

@Yaro812

This comment has been minimized.

Show comment
Hide comment
@Yaro812

Yaro812 Sep 25, 2015

How to properly add this code to the project?

Is adding PSPDFUIKitMainThreadGuard();
to + (void)initialize of my AppDelegate is a proper way to use this code?

Yaro812 commented Sep 25, 2015

How to properly add this code to the project?

Is adding PSPDFUIKitMainThreadGuard();
to + (void)initialize of my AppDelegate is a proper way to use this code?

@kconnor

This comment has been minimized.

Show comment
Hide comment
@kconnor

kconnor Sep 26, 2015

@Yaro812 Just add the file to your project (and Compile Build Phase). It runs on load:
attribute((constructor)) static void PSPDFUIKitMainThreadGuard(void)

kconnor commented Sep 26, 2015

@Yaro812 Just add the file to your project (and Compile Build Phase). It runs on load:
attribute((constructor)) static void PSPDFUIKitMainThreadGuard(void)

@kconnor

This comment has been minimized.

Show comment
Hide comment
@kconnor

kconnor Sep 26, 2015

@steipete My osx version of this gist is unhappy on El Capitan. Looks like it does more UI work off the main thread. It's asserting where it didn't before. Thanks very much for making it public.

kconnor commented Sep 26, 2015

@steipete My osx version of this gist is unhappy on El Capitan. Looks like it does more UI work off the main thread. It's asserting where it didn't before. Thanks very much for making it public.

@nrbrook

This comment has been minimized.

Show comment
Hide comment

nrbrook commented Oct 11, 2015

I made a swift version

@ponchorage

This comment has been minimized.

Show comment
Hide comment
@ponchorage

ponchorage Oct 27, 2015

Still not clear to me how to actually use this. The file is in my project and it appears in the Compile Build Phase. Do I need to import the file in the AppDelegate or somewhere and is there a call I need to make? I ask because it's not catching any problems but the new version of Xcode 7.1 seems to think there are issues. It's giving me a stack trace saying that I'm modifying the autolayout engine from a background thread.

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.

Still not clear to me how to actually use this. The file is in my project and it appears in the Compile Build Phase. Do I need to import the file in the AppDelegate or somewhere and is there a call I need to make? I ask because it's not catching any problems but the new version of Xcode 7.1 seems to think there are issues. It's giving me a stack trace saying that I'm modifying the autolayout engine from a background thread.

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.

@jdarowski

This comment has been minimized.

Show comment
Hide comment
@jdarowski

jdarowski Oct 27, 2015

How much can one love a single gist? <3

How much can one love a single gist? <3

@drct

This comment has been minimized.

Show comment
Hide comment
@drct

drct Nov 12, 2015

Minor improvements: Moved "#define PSPDFAssert(expression, ...) ...", commented "PSPDFLogError ..." out and added "#import <UIKit/UIKit.h>". If interested, you can pull from here: https://gist.github.com/drct/bb4b1f93f8790cca9c9f (since no pull requests are possible with gists.

drct commented Nov 12, 2015

Minor improvements: Moved "#define PSPDFAssert(expression, ...) ...", commented "PSPDFLogError ..." out and added "#import <UIKit/UIKit.h>". If interested, you can pull from here: https://gist.github.com/drct/bb4b1f93f8790cca9c9f (since no pull requests are possible with gists.

@fbartho

This comment has been minimized.

Show comment
Hide comment
@fbartho

fbartho Nov 14, 2015

Is there an OSX version of this?

fbartho commented Nov 14, 2015

Is there an OSX version of this?

@umut-genlik

This comment has been minimized.

Show comment
Hide comment
@umut-genlik

umut-genlik Nov 20, 2015

How to use it:
@ponchorage just add the .m file (the one @drct enhanced) to your project, then after it is added to your project go to Build Phases -> Compile Sources and find the PSPDFUIKitMainThreadGuard.m double click it add the non-arc compiling flag. -fno-objc-arc
That is it.
screen shot 2015-11-20 at 2 06 20 pm

screen shot 2015-11-20 at 2 05 35 pm

How to use it:
@ponchorage just add the .m file (the one @drct enhanced) to your project, then after it is added to your project go to Build Phases -> Compile Sources and find the PSPDFUIKitMainThreadGuard.m double click it add the non-arc compiling flag. -fno-objc-arc
That is it.
screen shot 2015-11-20 at 2 06 20 pm

screen shot 2015-11-20 at 2 05 35 pm

@trusk89

This comment has been minimized.

Show comment
Hide comment
@trusk89

trusk89 Nov 27, 2015

Doesn't work for me. No exception is caught.

trusk89 commented Nov 27, 2015

Doesn't work for me. No exception is caught.

@onmyway133

This comment has been minimized.

Show comment
Hide comment
@onmyway133

onmyway133 Jan 16, 2016

@jerry-sl You can use swizzle on UIView in Swift, here is an example https://github.com/onmyway133/MainThreadGuard
You can use DEBUG custom flag http://stackoverflow.com/questions/24003291/ifdef-replacement-in-swift-language or use assert

@jerry-sl You can use swizzle on UIView in Swift, here is an example https://github.com/onmyway133/MainThreadGuard
You can use DEBUG custom flag http://stackoverflow.com/questions/24003291/ifdef-replacement-in-swift-language or use assert

@sahabe1

This comment has been minimized.

Show comment
Hide comment
@sahabe1

sahabe1 May 3, 2016

How to use PSPDFUIKitMainThreadGuard.m . I am getting error

Undefined symbols for architecture armv7:
"_PSPDFAssert", referenced from:
_PSPDFReplaceMethodWithBlock in PSPDFUIKitMainThreadGuard.o
(maybe you meant: _PSPDFAssertIfNotMainThread)
ld: symbol(s) not found for architecture armv7
clang: error: linker command failed with exit code 1 (use -v to see invocation)

sahabe1 commented May 3, 2016

How to use PSPDFUIKitMainThreadGuard.m . I am getting error

Undefined symbols for architecture armv7:
"_PSPDFAssert", referenced from:
_PSPDFReplaceMethodWithBlock in PSPDFUIKitMainThreadGuard.o
(maybe you meant: _PSPDFAssertIfNotMainThread)
ld: symbol(s) not found for architecture armv7
clang: error: linker command failed with exit code 1 (use -v to see invocation)

@smarxpan

This comment has been minimized.

Show comment
Hide comment
@smarxpan

smarxpan Jun 7, 2016

@sahabe1 same as me, but look this

drct commented on 12 Nov 2015
Minor improvements: Moved "#define PSPDFAssert(expression, ...) ...", commented "PSPDFLogError ..." out and added "#import ". If interested, you can pull from here: https://gist.github.com/drct/bb4b1f93f8790cca9c9f (since no pull requests are possible with gists.

smarxpan commented Jun 7, 2016

@sahabe1 same as me, but look this

drct commented on 12 Nov 2015
Minor improvements: Moved "#define PSPDFAssert(expression, ...) ...", commented "PSPDFLogError ..." out and added "#import ". If interested, you can pull from here: https://gist.github.com/drct/bb4b1f93f8790cca9c9f (since no pull requests are possible with gists.

@steipete

This comment has been minimized.

Show comment
Hide comment
@steipete

steipete Jul 6, 2016

Please dupe https://openradar.appspot.com/27192338 if you want to see this in UIKit.

Owner

steipete commented Jul 6, 2016

Please dupe https://openradar.appspot.com/27192338 if you want to see this in UIKit.

@vedon

This comment has been minimized.

Show comment
Hide comment
@vedon

vedon Jul 17, 2016

I set the image of UIImageView in another thread ,it can't detect the illegal operation.
Is there another way to detect it ?

vedon commented Jul 17, 2016

I set the image of UIImageView in another thread ,it can't detect the illegal operation.
Is there another way to detect it ?

@messihv5

This comment has been minimized.

Show comment
Hide comment
@messihv5

messihv5 Oct 23, 2016

it just worked for me, thank you!

it just worked for me, thank you!

@Schemetrical

This comment has been minimized.

Show comment
Hide comment
@Schemetrical

Schemetrical Nov 12, 2016

@nrbrook you are literally god

@nrbrook you are literally god

@freak4pc

This comment has been minimized.

Show comment
Hide comment
@freak4pc

freak4pc Mar 29, 2017

FYI the Swift port by @onmyway133 will probably be broken soon. Any other thoughts on how to get this done? This is a great saver when sometimes forgetting to go back to the UI Thread.
image

FYI the Swift port by @onmyway133 will probably be broken soon. Any other thoughts on how to get this done? This is a great saver when sometimes forgetting to go back to the UI Thread.
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment