¿Cómo puedo encontrar llamadas a instancias de UIKit desde un subprocess secundario?

Mi aplicación se bloquea en iOS 5 porque tengo un código que llama instancias UIKit desde un subprocess secundario. Sabes que tienes este problema cuando ves el siguiente error:

bool _WebTryThreadLock (bool), 0x811bf20: ¡Múltiples lockings en el hilo web no permitidos! Por favor presenta un error. Estrellándose ahora …

Entonces, mi pregunta es ¿de qué maneras puedo encontrar el código que llama a las instancias UIKit desde un subprocess secundario?

Aquí hay algunas cosas que ya he probado:

  1. Comentó los bloques que podrían estar violando la regla
  2. Added assert([NSThread isMainThread]) en lugares que podrían estar procesando en hilo secundario
  3. Se agregó un punto de corte simbólico para _WebTryThreadLock

Estas cosas me han ayudado a encontrar áreas problemáticas. Sin embargo, en mi locking final, el _WebTryThreadLock interrupción _WebTryThreadLock no tiene seguimiento de stack en ninguno de los otros subprocesss. Entonces, ¿cómo puedo encontrar el código que causa el problema sin una traza de stack?

¡Gracias por tu time!

Tu assert() es probablemente la herramienta más valiosa en esto. Se sabe que he puesto una afirmación similar al principio de cada método en mis classs Controller. Si eso no lo encuentra, agrego la aserción a mis classs de Vista. Si no lo encuentra, lo agrego a cualquier class de Modelo que piense que son hilos principales solamente.

Para el comentario de @ craig, el hecho de que afirme ser un error interno puede ser exacto. Pero creo que está en el path correcto para examinar de cerca su propio código primero.

Adapté el PSPDFUIKitMainThreadGuard.m para permitir que uno no tenga que preocuparse por estas cosas. Aquí: 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); }); } } } } 

Lanza automáticamente las llamadas al hilo principal y, por lo tanto, ni siquiera tendrías que hacer nada más que poner el código.

Este problema se debe a que desea acceder a la interfaz de usuario desde el subprocess secundario de alguna manera, puede desde la vista web de cualquier otra cosa. No está permitido porque UIKit no es seguro para subprocesss y solo se puede acceder desde MainThread. Lo primero que puede hacer es cambiar su llamada de subprocess a [self performSelectorOnMainThread:@selector(myMethod) withObject:nil waitUntilDone:NO]; (busque la documentation). En caso de que cuando no tenga otra opción, puede usar GCD (Grand Central Dispathc) …

Este código (simplemente agregue al proyecto y compile este file sin ARC) provoca aserciones en el acceso UIKit fuera del hilo principal: https://gist.github.com/steipete/5664345

Acabo de usarlo para recuperar numerosos problemas de UIKit / thread principal en algún código que acabo de recoger.