8

我正在尝试执行以下操作:

  1. 获得一个class'dealloc IMP
  2. 向所述类中注入一个自定义 IMP,它本质上调用原始的 dealloc IMP
  3. 当所述类的实例被释放时,两个 IMP 都应该运行。

这是我的尝试:

@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

像这样使用:

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

但结果是:

僵尸对象禁用:

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

启用僵尸对象(第 11 行是图中的 EXC_BAD_ACCESS)

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

关于我做错了什么有什么想法吗?

第 11 行是 EXC_BAD_ACCESS

4

2 回答 2

9

如果您真的想知道何时释放对象,请使用关联对象。

具体来说,将对象与您要观察的对象相关联,以便被观察的对象具有对该对象的唯一强引用——唯一保留的引用。然后,您可以覆盖dealloc并知道当它被调用时,被观察的对象已经(或即将)被释放。

不过,不要弄乱被释放的对象!! 它已经dealloc调用了它的所有方法(通过继承),因此,内部状态将完全未定义。

请注意,如果您的目标是尝试清理系统框架中的某些内容,那么......不要。沿着这条路走下去没有任何不稳定和痛苦。


就像我在对 nielsbot 的评论中提到的那样,我并不是想知道对象何时被释放。

准确了解注入的dealloc实现中的内容会很有帮助。从表面上看,我能想到无法通过使用关联对象来检测释放来解决的唯一原因正是因为你试图改变一个类的行为dealloc,这是一件非常糟糕的事情.

于 2013-07-03T21:12:30.230 回答
2

一段时间后,我终于找到了问题的真正原因:我忽略了 IMP 类型的签名。来自 Apple 的 Objective-C 运行时参考:

IMP 指向方法实现开始的指针。

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

特别是,IMP 的返回类型为 id,因此在 ARCLandia 中,ARC 尝试管理此 id,从而导致objc_retain崩溃。因此,假设您有一个 IMP to -dealloc,将其显式转换为具有返回类型的 C 函数指针,void使得 ARC 不再尝试管理返回值:

void (*deallocImp)(id, SEL) = (void(*)(id, SEL))_originalDeallocIMP;
deallocImp(self,NSSelectorFromString(@"dealloc"));
于 2013-07-03T21:17:38.747 回答