36

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

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

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

4

5 回答 5

43

为了跟踪应用程序的增长,Heapshot Analysis已被证明非常有效。它将捕获真正的泄漏和内存的增加,其中分配不是由泄漏引起的。

您可以使用分配工具查看所有保留/释放事件及其回溯。点击分配工具上的小 (i) 按钮并打开“记录引用计数”。启用“仅跟踪活动分配”会减少 Instruments 收集的数据量,使其更快速(并且死分配在这种情况下并不是真正有用,但在其他情况下可能有用)。

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

在此处输入图像描述

于 2012-09-05T17:27:14.247 回答
24

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

  1. 临时添加-fno-objc-arc到对象类 Compiler Flags 以禁用该类的 ARC。
  2. 暂时覆盖retain(只需调用super)并在其上放置断点。
  3. 每次调用时调试并检查调用堆栈retain
于 2012-09-04T13:34:49.940 回答
11

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

1/ 构建分析并启动带有泄漏检测的仪器。然后探索当前分配的对象,找到您想要的对象(您可以按名称对其进行排序)并查看其保留/释放历史记录。请注意,保留计数对 ARC 不是很有帮助。您必须逐步手动检查它。

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

2/ 在对象被创建和销毁时,将 aNSLog放入您的init和您dealloc的观察中。

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

@property (weak, nonatomic) id<...> delegate;

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

@synthesize delegate = _delegate;

- (void)setDelegate(id<...>)delegate {
    _delegate = delegate;  //with ARC this retains the object!
}
于 2012-09-04T12:30:32.540 回答
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
}
于 2015-07-10T18:43:51.237 回答
-3

如果您使用 ARC,您将永远无法选择添加保留,

截图1

如果您已使用以下选项将项目转换为 ARC,则会提示您出错

截图2

如果您将属性设置为strong,那么您应该在整个项目中分配一次对象,例如self.yourobject = [[NSMutableArray alloc]init];。这没有捷径可走。

于 2012-09-04T12:19:22.660 回答