ARC: how to add a custom dealloc IMP to an object, which in turn calls the original dealloc without a malloc error

I am trying to do the following:

  • access the class'dealloc IMP
  • introduce a custom IMP into the mentioned class, which essentially calls the original dealloc IMP
  • When an instance of the specified class is freed, both IMPs must start.

This is my attempt:

@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 

Used like this:

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

But the results are:

disabled zombie objects:

 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 with zombies are included (line 11 is EXC_BAD_ACCESS in the picture)

 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 

Any thoughts on what I'm doing wrong?

Line 11 is a EXC_BAD_ACCESS

+8
ios objective-c cocoa-touch cocoa
source share
2 answers

After a while, I finally found the real cause of the problem: I did not notice a signature like IMP. From the Apple Objective-C Runtime Reference:

IMP Pointer to the beginning of the implementation of the method.

id (* IMP) (id, SEL, ...)

In particular, IMP has a return identifier type, so in ARCLandia, ARC tries to manage that identifier, causing objc_retain fail. So, suppose you have an IMP for -dealloc , explicitly -dealloc it to a C function pointer with a return type of void , doing this where ARC will no longer try to control the return value:

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

If you really want to know when an object is freed, use related objects.

In particular, associate the object with the object that you want to observe in such a way that the observed object has the only strong link - the only link to the object object. You can then override dealloc and know that when it is called, the observed object has been (or should be) freed.

Do not confuse with the freed object, though !! He would already use all his dealloc methods (by inheritance), and thus the internal state would be completely undefined.

Please note that if your goal is to try to clear something in the system framework, then ... no. Down this path there is nothing instability and pain.


as I mentioned in the comments on nielsbot, I am NOT trying to know when an object is freed.

It would be helpful to know exactly what is in your embedded dealloc implementation. At first glance, the only reason I can think about it cannot be solved with the help of related objects to detect the release - this is precisely because you are trying to change the behavior of the dealloc class, which is a very bad thing to do.

+9
source share

All Articles