ARC: cómo inyectar IMP dealloc personalizado en un object que a su vez llama dealloc original sin causar un error malloc

Estoy tratando de hacer lo siguiente:

  1. get un hold a un IMP classealealloc
  2. inyecta en dicha class un IMP personalizado que esencialmente llama al IMP dealloc original
  3. Cuando una instancia de dicha class se desasigna, ambos IMPs deben ejecutarse.

Este es mi bash:

@implementation ClassB - (void)dealloc { NSLog(@"\n%@ | %@", self, NSStringFromSelector(_cmd)); } @end @implementation ClassC - (void)swizzleMe:(id)target { SEL originalDeallocSelector = NSSelectorFromString(@"dealloc"); __block IMP callerDealloc = [target methodForSelector:originalDeallocSelector]; const char *deallocMethodTypeEncoding = method_getTypeEncoding(class_getInstanceMethod([target class], originalDeallocSelector)); IMP newCallerDealloc = imp_implementationWithBlock(^(id _caller) { NSLog(@"\n new dealloc | calling block %p for %@", callerDealloc, _caller); callerDealloc(_caller, originalDeallocSelector); }); NSLog(@"\nswapping %p for %p", newCallerDealloc, callerDealloc); class_replaceMethod([target class], originalDeallocSelector, newCallerDealloc, deallocMethodTypeEncoding); } @end 

Para ser utilizado así:

 ClassB *b = [[ClassB alloc] init]; ClassC *c = [[ClassC alloc] init]; [c swizzleMe:b]; 

Pero los resultados son:

objects zombie desactivados:

 2013-07-03 13:24:58.368 runtimeTest[38626:11303] swapping 0x96df020 for 0x2840 2013-07-03 13:24:58.369 runtimeTest[38626:11303] new dealloc | calling block 0x2840 for <ClassB: 0x93282f0> 2013-07-03 13:24:58.370 runtimeTest[38626:11303] <ClassB: 0x93282f0> | dealloc 2013-07-03 13:24:58.370 runtimeTest[38626:11303] new dealloc | calling block 0x2840 for <ClassB: 0x93282f0> 2013-07-03 13:24:58.371 runtimeTest[38626:11303] <ClassB: 0x93282f0> | dealloc runtimeTest(38626,0xac55f2c0) malloc: *** error for object 0x93282f0: pointer being freed was not allocated *** set a breakpoint in malloc_error_break to debug 2013-07-03 13:24:58.371 runtimeTest[38626:11303] new dealloc | calling block 0x2840 for <ClassB: 0x93282f0> 2013-07-03 13:24:58.372 runtimeTest[38626:11303] <ClassB: 0x93282f0> | dealloc 

objects zombie habilitados (la línea 11 es un EXC_BAD_ACCESS en la image)

 2013-07-03 13:34:37.466 runtimeTest[38723:11303] swapping 0x97df020 for 0x2840 2013-07-03 13:34:37.467 runtimeTest[38723:11303] new dealloc | calling block 0x2840 for <ClassB: 0x715a920> 2013-07-03 13:34:37.468 runtimeTest[38723:11303] <ClassB: 0x715a920> | dealloc 

¿Alguna idea de lo que estoy haciendo mal?

La línea 11 es un EXC_BAD_ACCESS

Si realmente desea saber cuándo se desasigna un object, use los objects asociados.

Específicamente, asocie un object con el object que desea observar de tal manera que el object que se está observando tenga la única reference sólida (la única reference retenida) al object. Entonces, puede anular dealloc y saber que cuando se llama, el object que se observa ha sido (o está a punto de ser) desasignado.

¡No te metas con el object que está siendo desasignado, sin embargo! Ya se dealloc invocado dealloc methods dealloc (por inheritance) y, por lo tanto, el estado interno estará completamente indefinido.

Tenga en count que si su objective es tratar de limpiar algo en los frameworks del sistema, entonces … no lo haga. Por ese path no hay nada de inestabilidad y dolor.


como mencioné en los comentarios a nielsbot, NO estoy tratando de saber cuándo se desasigna un object.

Sería útil saber exactamente qué hay en su implementación dealloc inyectada. A primera vista, la única razón por la que puedo pensar que no se pudo resolver mediante el uso de objects asociados para detectar la desasignación es exactamente porque estás tratando de cambiar el comportamiento de un dealloc la class, lo que es realmente malo para hacer.

Después de algún time, finalmente encontré la causa real del problema: estaba pasando por alto la firma del tipo IMP. Desde la reference de time de ejecución Objective-C de Apple:

IMP Un puntero al inicio de una implementación de método.

id (* IMP) (id, SEL, …)

Particularmente, el IMP tiene un tipo de ID de retorno y, por lo tanto, en ARCLandia, ARC intenta administrar este identificador, causando el locking de objc_retain . Por lo tanto, suponiendo que tenga un IMP a -dealloc , -dealloc explícitamente a un puntero de function C con el tipo de retorno void convierte en donde ARC ya no intentará administrar el valor de retorno:

 void (*deallocImp)(id, SEL) = (void(*)(id, SEL))_originalDeallocIMP; deallocImp(self,NSSelectorFromString(@"dealloc"));