发送消息objc_msgSend(class,@selector(dealloc))释放对象,为什么访问对象指针错误?

Send the message objc_msgSend(class,@selector(dealloc)) to release the object, why is it wrong to access the object pointer?

代码在ARC下。当我删除代码 NSObject* objc = (NSObject*)object; 时,程序运行正常,但我无法访问指针 objc。当我保留代码 NSObject* objc = (NSObject*)object; 时,系统会提示我 EXC_BAD_ACCESS (code=1, address=0x20)。系统是否在块函数体结束后访问objc指针?

-(void)resetDeallocMethodWithInstance:(NSObject*)obj
{
    Class targetClass = obj.class;
    @synchronized (swizzledClasses()) {
        NSString *className = NSStringFromClass(obj.class);
        if ([swizzledClasses() containsObject:className]) return;
        SEL deallocSel = sel_registerName("dealloc");
        __block void (*deallocBlock)(__unsafe_unretained id, SEL) = NULL;
        id block = ^(__unsafe_unretained id object){
            NSObject* objc = (NSObject*)object;
            NSUInteger hash = ((NSObject*)object).hash;
            [self removeAllTargetWitSuffixKey:[NSString stringWithFormat:@"%lu",(unsigned long)hash]];
            if (deallocBlock == NULL) {
                struct objc_super superInfo = {
                    .receiver = object,
                    .super_class = class_getSuperclass(targetClass)
                };
                void (*msgSend)(struct objc_super *, SEL) = (__typeof__(msgSend))objc_msgSendSuper;
                msgSend(&superInfo, deallocSel);
            } else {
                deallocBlock(object, deallocSel);
            }
        };
        IMP blockImp = imp_implementationWithBlock(block);
        if (!class_addMethod(obj.class, deallocSel, blockImp, "v@:")) {
            Method deallocMethod = class_getInstanceMethod(obj.class, deallocSel);
            deallocBlock = (__typeof__(deallocBlock))method_getImplementation(deallocMethod);
            deallocBlock = (__typeof__(deallocBlock))method_setImplementation(deallocMethod, blockImp);
        }
        [swizzledClasses() addObject:className];
    }
    return;
}

enter image description here

注意:这个答案是直接输入的,你的代码没有测试过,确实没有代码已经过测试。因此,正在推断以下问题导致了您的问题。

您的设计存在问题的区域数量:

  • 不推荐混合 deallocdealloc 方法在销毁对象的过程中由系统自动调用,因此不恰当地使用部分销毁的对象(无论可能是什么)可能会导致问题 - 正如您所发现的!
  • 您正在使用 "an implementation of dealloc, [should] not invoke the superclass’s implementation" 下的 ARC。然而,您的街区会这样做。
  • 变量objc未使用。但是默认情况下,局部变量具有 strong 属性,因此您在销毁过程中创建了对对象的强引用。块以这种方式进行的任何强引用都将在块完成时由 ARC 释放,这几乎肯定不是你的错误指示的那样好。

当特定对象被销毁时,您似乎试图调用 removeAllTargetWithSuffixKey: 方法(出现 当您调配 [并且只能调配] class 但正在使用特定对象的 hash)。避免混合的更好方法是使用 关联对象 .

运行时函数 objc_setassociatedobject() 允许您将一个对象附加到另一个对象的特定实例,并在其宿主被销毁时自动销毁该对象(使用 objc_AssociationPolicy of OBJC_ASSOCIATION_RETAIN).

设计一个 class,它有一个实例 属性 您需要的 hash 值和一个 dealloc 方法,它调用您的 removeAllTargetWithSuffixKey: 而不是 swizzle class 只需创建一个 class 的实例并将其与目标对象相关联。

HTH

是的,它是在方法结束后访问指针。如果这是在 ARC 下编译的,那么 objc 是一个 "strong" 引用。但是,您正在伪造 dealloc 方法的实现,因此在对象已经被释放时保留该对象,因此对它进行强引用为时已晚。你的实现将调用 super,它实际上应该释放对象,然后 ARC 将释放 objc 值,但它已经消失了,因为它是接收者,即 "self" 如果你正在编写一个普通的 dealloc 方法.

ARC 永远不会在常规的 dealloc 方法中保留 self ,但这是您正在有效地做的事情。 "object" 值是同一个指针,但明确地是 __unsafe_unretained,因此您应该直接使用它。如果有帮助,您可以将块键入为 NSObject* 而不是 id,但这无关紧要。或者你也可以让你的 objc 值 __unsafe_unretained 这样 ARC 就不用管它了。您不希望 ARC 以任何方式触及块内的 "self" 值,因为在这种情况下您要绕过 ARC 的背面。

无论如何,一旦您处于对象的 dealloc 方法中,就永远不要 retain/release/autorelease 自指针——它最终会导致崩溃。例如,从 dealloc 调用方法并将引用传递给 self 是不行的。如果您正在玩这些类型的运行时游戏,您需要对此非常小心,并准确了解 ARC 在做什么。