2011-10-20 31 views
7

我的應用程序在iOS 5中崩潰,因爲我有一些從輔助線程調用UIKit實例的代碼。當您看到以下錯誤時,您知道存在此問題:如何從輔助線程找到對UIKit實例的調用?

bool _WebTryThreadLock(bool),0x811bf20:不允許在Web線程上使用多個鎖!請提交一個錯誤。現在崩潰...

所以我的問題是什麼是一些方法,我可以找到從輔助線程調用UIKit實例的代碼?

這裏有一些事情我已經嘗試:

  1. 註釋掉塊可能被侵犯的地方規則
  2. 新增assert([NSThread isMainThread]),可能是處理輔助線程
  3. 增加了一個象徵性的斷點爲_WebTryThreadLock

這些事情幫助我找到問題領域。但是,在我最後的崩潰中,_WebTryThreadLock斷點在任何其他線程中都沒有堆棧跟蹤。那麼,我如何找到導致問題而沒有堆棧跟蹤的代碼?

謝謝你的時間!

+0

這可能是一個錯誤的模擬器。在實際的iOS設備上運行時是否也會發生該問題? –

+0

是的,它發生在運行iOS 5的iOS 5模擬器和iOS設備上。 – johnnieb

+0

啊,好的。Google錯誤信息的快速搜索讓我相信這是一個模擬器錯誤,但如果它發生在其他地方,那麼我不完全確定。你有沒有可能在屏幕上同時顯示2+ UIWebViews? –

回答

3

您的assert()可能是最有價值的工具。我已經知道在我的控制器類的每個方法的開始處放置類似的斷言。如果沒有找到它,我將斷言添加到我的View類。如果沒有找到它,我將它添加到我認爲只有主線程的任何Model類中。

對@ craig的評論,它聲稱是一個內部錯誤的事實可能是準確的。但我認爲你正在正確的道路上,首先仔細檢查你自己的代碼。

1

這個問題出現是因爲你想以某種方式從輔助線程訪問UI,它可以從任何其他的webview中訪問。這是不允許的,因爲UIKit不是線程安全的,只能從MainThread訪問。 您可以做的第一件事是將您的線程調用改爲[self performSelectorOnMainThread:@selector(myMethod) withObject:nil waitUntilDone:NO];(查找文檔)。 在情況下,當你有沒有其他的選擇,你可以使用GCD(中央大Dispathc)...

1

此代碼(只需添加到項目並編譯沒有ARC這個文件)會導致在主線程之外的UIKit訪問斷言:https://gist.github.com/steipete/5664345

我剛剛用它來拾取我剛剛拾取的代碼中的許多UIKit /主線程問題。

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); 
       }); 
      } 
     } 
    } 
} 

它會自動調用踢成主線程,因此你甚至不會做任何事情,但在撲通代碼

+0

您的要點不再存在 –

+0

@AdamMendoza對不起,剛更新了鏈接 – kezi

相關問題