查找ARC保留对象的位置

hpi*_*que 36 macos xcode ios automatic-ref-counting

我有一个对象被保留超过必要(很可能是由于一个属性strong而不是weak).大代码库,所以很难找到哪里.

使用ARC时,如何找到保留此对象的所有行?

如果我没有使用ARC,我想我可以简单地覆盖retain并检查它的调用位置.我可以用ARC做类似的事吗?

bbu*_*bum 43

为了跟踪应用程序的增长,已经证明Heapshot Analysis非常有效.它将捕获真正的泄漏和内存增加,其中分配不考虑泄漏.

您可以使用Allocations工具查看所有保留/释放事件及其回溯.点击Allocations仪器上的小(i)按钮,然后打开"记录参考计数".打开"仅跟踪活动分配"会减少Instruments收集的数据量,使其更加快速(并且死区分配在此上下文中并不真正有用,但可以在其他情况下使用).

这样,您可以深入了解任何分配(通过单击地址字段中的右箭头),查看所有保留/释放事件并查看它们的确切位置.

在此输入图像描述

  • iOS7/Xcode5的仪器现在有点不同了,但这仍然很多.当然,在弄清楚这一切是如何工作后,我发现我已经以教科书的方式创建了一个保留周期(委托 - 委托人都强烈地引用了彼此).*叹* (4认同)
  • 如今,这在仪器中被称为"世代".并且您可能想要在真实设备上进行配置*,因为模拟器为什么不表示自己的类名. (2认同)

hpi*_*que 24

我设法retain通过执行以下操作找到了违规行为:

  1. 暂时添加-fno-objc-arc到对象类Compiler Flags以禁用该类的ARC.
  2. 暂时覆盖retain(只是调用super)并在其上放置一个断点.
  3. 每次调用时调试并检查调用堆栈retain.

  • 一个非常有效的锤子驱动螺丝... :)这有效 - 我已经在极端情况下使用它 - 但启用了"记录引用计数"的分配工具应该能够显示这一点而无需任何修改您的代码(包括可能引入其他错误的内存模型更改). (4认同)

Sul*_*han 10

上周我帮助一些朋友在他们的ARC项目中调试泄漏.一些技巧:

1 /构建用于分析并启动具有泄漏检测的仪器.然后浏览当前分配的对象,找到所需的对象(可以按名称对它们进行排序)并查看其保留/释放历史记录.请注意,保留计数对ARC没有多大帮助.您必须逐步手动检查它.

尝试评论可能是泄漏源的所有代码,然后逐步取消注释.

2/在创建和销毁对象时NSLog放入init并进入您dealloc的观察区域.

3 /不要只查看属性定义,观察是否手动实现属性设置器.我在朋友的项目中发现了一个问题:

@property (weak, nonatomic) id<...> delegate;
Run Code Online (Sandbox Code Playgroud)

@interface ... {
    id<...> _delegate;
}

@synthesize delegate = _delegate;

- (void)setDelegate(id<...>)delegate {
    _delegate = delegate;  //with ARC this retains the object!
}
Run Code Online (Sandbox Code Playgroud)


小智 5

这个解决方案对我来说有些帮助。它基本上使用方法调配来欺骗 ARC 编译器,让其认为您没有重写保留和释放。

#import <objc/runtime.h>
...
+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class cls = [self class];
        
        // When swizzling a class method, use the following:
        // Class class = object_getClass((id)self);
        
        SEL originalSelector1 = NSSelectorFromString(@"retain");
        SEL swizzledSelector1 = NSSelectorFromString(@"myretain");
        
        SEL originalSelector2 = NSSelectorFromString(@"release");
        SEL swizzledSelector2 = NSSelectorFromString(@"myrelease");
        
        Method originalMethod1 = class_getInstanceMethod(cls, originalSelector1);
        Method swizzledMethod1 = class_getInstanceMethod(cls, swizzledSelector1);
        Method originalMethod2 = class_getInstanceMethod(cls, originalSelector2);
        Method swizzledMethod2 = class_getInstanceMethod(cls, swizzledSelector2);
        
        BOOL didAddMethod1 =
        class_addMethod(cls,
                        originalSelector1,
                        method_getImplementation(swizzledMethod1),
                        method_getTypeEncoding(swizzledMethod1));
    
        if (didAddMethod1) {
            class_replaceMethod(cls,
                                swizzledSelector1,
                                method_getImplementation(originalMethod1),
                                method_getTypeEncoding(originalMethod1));
        } else {
            method_exchangeImplementations(originalMethod1, swizzledMethod1);
        }
    
        BOOL didAddMethod2 =
        class_addMethod(cls,
                        originalSelector2,
                        method_getImplementation(swizzledMethod2),
                        method_getTypeEncoding(swizzledMethod2));

        if (didAddMethod2) {
            class_replaceMethod(cls,
                                swizzledSelector2,
                                method_getImplementation(originalMethod2),
                                method_getTypeEncoding(originalMethod2));
        } else {
            method_exchangeImplementations(originalMethod2, swizzledMethod2);
        }
    });
}

-(id)myretain {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
    NSLog(@"tracking retain now %@",@((uintptr_t)[self performSelector:NSSelectorFromString(@"retainCount")]));
    SEL selector = NSSelectorFromString(@"myretain");
    return [self performSelector:selector withObject:nil];
#pragma clang diagnostic pop
}

-(id)myrelease {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
    NSLog(@"tracking release now %@", @((uintptr_t)[self performSelector:NSSelectorFromString(@"retainCount")]));

    SEL selector = NSSelectorFromString(@"myrelease");
    return [self performSelector:selector withObject:nil];
#pragma clang diagnostic pop
}
Run Code Online (Sandbox Code Playgroud)