我的应用程序在iOS 5中崩溃,因为我有一些代码从辅助线程调用UIKit实例。当您看到以下错误时,您知道有这个问题:
bool _WebTryThreadLock(bool),0x811bf20:不允许在网络线程上进行多次锁定!请提交一个错误。现在崩溃......
所以我的问题是我可以通过哪些方法找到从辅助线程调用UIKit实例的代码?
以下是我已经尝试过的一些事情:
assert([NSThread isMainThread])
_WebTryThreadLock
这些东西帮助我找到问题所在。但是,在我的最终崩溃中,_WebTryThreadLock
断点在任何其他线程中都没有堆栈跟踪。那么,如何在没有堆栈跟踪的情况下找到导致问题的代码呢?
谢谢你的时间!
答案 0 :(得分:3)
您的assert()
可能是最有价值的工具。我已经知道在Controller类的每个方法的开头都有类似的断言。如果找不到,我将断言添加到我的View类中。如果没有找到它,我将它添加到我认为只是主线程的任何Model类中。
对于@ craig的评论,它声称是内部错误的事实可能是准确的。但我认为你是在正确的道路上仔细检查你自己的代码。
答案 1 :(得分:3)
我改编了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);
});
}
}
}
}
它会自动将调用踢入主线程中,因此除了将代码放入其中之外,您甚至不必做任何事情。
答案 2 :(得分:1)
出现此问题的原因是您希望以某种方式从辅助线程访问UI,它可以从其他任何内容的webview访问。这是不允许的,因为UIKit不是线程安全的,只能从MainThread访问。
您可以做的第一件事就是将线程调用更改为[self performSelectorOnMainThread:@selector(myMethod) withObject:nil waitUntilDone:NO];
(查找文档)。
如果您没有其他选择,可以使用GCD(Grand Central Dispathc)...
答案 3 :(得分:1)
此代码(仅添加到项目并在没有ARC的情况下编译此文件)会导致主线程外的UIKit访问断言:https://gist.github.com/steipete/5664345
我刚刚使用它来解决我刚接到的一些代码中的大量UIKit /主线程问题。