我如何find从辅助线程调用UIKit实例?

我的应用程序在iOS 5中崩溃,因为我有一些从辅助线程调用UIKit实例的代码。 当您看到以下错误时,您知道您有这个问题:

布尔_WebTryThreadLock(布尔),0x811bf20:不允许在Web线程上的多个锁! 请提交一个错误。 现在崩溃…

所以我的问题是什么是一些方法,我可以find从辅助线程调用UIKit实例的代码?

以下是我已经试过的一些东西:

  1. 注释掉可能违反规则的块
  2. 在可能在辅助线程中处理的地方添加了assert([NSThread isMainThread])
  3. _WebTryThreadLock添加了一个符号断点

这些东西帮助我find问题的地方。 但是,在最后的崩溃中, _WebTryThreadLock断点在任何其他线程中都没有堆栈跟踪。 那么,我怎样才能find没有堆栈跟踪导致问题的代码?

谢谢你的时间!

你的assert()可能是最有价值的工具。 我已经知道在我的控制器类的每个方法的开始时都会放置类似的断言。 如果没有find它,我将断言添加到我的视图类。 如果没有find它,我将它添加到我认为只是主线程的任何Model类。

对@ craig的评论,它声称是一个内部错误的事实可能是准确的。 但是我认为你是在正确的道路上仔细检查自己的代码。

我改编了PSPDFUIKitMainThreadGuard.m,让人不必担心这些事情。 这里: https : //gist.github.com/k3zi/98ca835b15077d11dafc :

 #import <objc/runtime.h> #import <objc/message.h> // Compile-time selector checks. #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) /////////////////////////////////////////////////////////////////////////////////////////// #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; } // 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 *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) { if(!NSThread.isMainThread){ dispatch_async(dispatch_get_main_queue(), ^{ ((void ( *)(id, SEL, CGRect))objc_msgSend)(_self, newSelector, r); }); }else{ ((void ( *)(id, SEL, CGRect))objc_msgSend)(_self, newSelector, r); } }); }else { PSPDFReplaceMethodWithBlock(UIView.class, selector, newSelector, ^(__unsafe_unretained UIView *_self) { if(!NSThread.isMainThread){ dispatch_async(dispatch_get_main_queue(), ^{ ((void ( *)(id, SEL))objc_msgSend)(_self, newSelector); }); }else ((void ( *)(id, SEL))objc_msgSend)(_self, newSelector); }); } } } } 

它会自动踢到主线程的调用,因此你甚至不需要做任何事情,而是把代码放进去。

这个问题是因为你想以某种方式从辅助线程访问UI,它可以从任何其他的webview。 这是不允许的,因为UIKit不是线程安全的,只能从MainThread访问。 你可以做的第一件事是改变你的线程调用[self performSelectorOnMainThread:@selector(myMethod) withObject:nil waitUntilDone:NO]; (查找文档)。 如果你没有别的select,你可以使用GCD(Grand Central Dispathc)…

这个代码(只是添加到项目和编译这个文件没有弧)导致主线外的UIKit访问断言: https : //gist.github.com/steipete/5664345

我刚刚用它来拾取我刚刚拿起的一些代码中的许多UIKit /主线程问题。