【问题标题】:NSMutableArray of viewControllers not releasing the memory of its objects视图控制器的 NSMutableArray 未释放其对象的内存
【发布时间】:2012-10-31 00:07:28
【问题描述】:

这基本上和我的另一个问题here有关

我正在尝试发布包含 viewControllers 的 NSMutableArray。我愿意:

self.viewControllers = nil;

viewWillDisappear 中,因为我要转到另一个视图。但无论我做什么,视图控制器都不会被释放。我也试过了:

[[scrollView subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)];

其中 scrollview 是拥有 NSMutableArray 的视图。

尽管包含 NSMutableArray 的引用计数为 0,但我看到实时视图控制器(在仪器中)的计数没有改变。

【问题讨论】:

  • 当您说“引用计数”时,我假设您的意思是“保留计数”?

标签: ios memory-management nsmutablearray viewcontroller


【解决方案1】:

几个观察:

  1. 确保通过静态分析器运行非 ARC 代码。这可以找到许多困扰非 ARC 代码的内存管理问题。从 Xcode 的“产品”菜单中选择“分析”,或按 command+shift+B。如果您使用 ARC,很多这些内存管理问题都会消失,但如果您不使用 ARC,静态分析器在检查您的代码时可能非常有用。

  2. 您对removeFromSuperview 的尝试是不必要的,并且不会影响视图控制器本身的retainCount。但是,我是否从这次尝试中推断出您已经创建了视图控制器,然后将它们的视图添加到了滚动视图中?如果是这样,您是否为每一个都做了必要的addChildViewController?如果是这样,您确实需要为其中的每一个执行关联的removeFromParentViewController

  3. 视图控制器的正确释放取决于您如何定义和分配 viewControllers 数组以及如何填充它。

    但是,例如,我有一个属性:

    @property (nonatomic, retain) NSMutableArray *array;
    

    我用下面的代码初始化了它(注意NSMutableArray本身的autorelease(因为我使用了为我保留它的访问器方法),以及@987654331的显式release @对象):

    - (void)makeArray
    {
        // create an array, using the accessor method (thus why I'm using an autorelease object)
    
        self.array = [[[NSMutableArray alloc] init] autorelease];
    
        // just add four random objects to the array.
        // note, adding them to the array increases their retain count, thus I 
        // release them to bring the retain count back to +1 ... I could have 
        // done that via autorelease, too
    
        for (NSInteger i = 1; i < 4; i++)
        {
            Object *obj = [[Object alloc] initWithString:[NSString stringWithFormat:@"Test %d", i]];
            [self.array addObject:obj];
            [obj release];
        }
    }
    

    如果我检查 retainCount 的值,我可以看到所有内容的 retainCount 都是 +1,这是适当的:

    - (void)logArray
    {
        // let's examine the retain counts for the objects in the array
        // should be "1" given there are no other strong references anywhere
    
        for (id obj in self.array)
            NSLog(@"%s %@ (retainCount = %d)", __FUNCTION__, obj, [obj retainCount]);
    
        // let's also examine the retain count for the array, itself
        // this should also be "1"
    
        NSLog(@"%s retainCount = %d", __FUNCTION__, [self.array retainCount]);
    }
    

    当我在以下方法中清除它(以及 Object 类在其 dealloc 方法期间执行 NSLog 的事实证实了这一事实)时,它(以及数组的各个对象)被正确释放:

    - (void)clearArray
    {
        // let's use the accessor method to release the array and make sure
        // the pointer is nil
    
        self.array = nil;
    }
    
  4. 假设数组被定义为retain 属性,这完全是一种冗长的说法,即self.viewControllers = nil; 的语法是释放数组(及其成员对象)的完美方式在上一点。但是,如果数组的成员对象没有被释放,那么这些对象显然不会将它们的retainCount 降为零。我会尝试,在您的self.viewControllers = nil; 之前,不仅记录数组本身的retainCount,还记录数组中各个对象的retainCount,以确认它们的retainCount 设置。

    此时它们都应该有一个 +1 的retainCount(否则会有其他东西保留它们,或者因为它们被过度保留,你有一些保留周期(又名强引用周期)在那些视图中控制器,或其他合法保留它们的东西(例如,在某些时候,您将其中一个视图控制器推送到导航器堆栈中,但您尚未将它们弹出)。

  5. 如果你还在泄漏,我会使用 Instruments 到 find the leak。顺便说一句,当检查调用树是否有泄漏时,我发现“反转调用树”和“隐藏系统库”很有用。


更新:

在上面的第 4 点中,我警告了保留周期的风险。保留周期的一个示例是视图控制器使用NSTimer 以及在释放视图控制器时invalidaterelease 计时器失败。与您离线聊天,这听起来可能是问题所在,您试图在dealloc 中使用release NSTimer,但dealloc 永远不会调用,因为计时器本身正在保留视图控制器.在释放NSMutableArray 之前,您需要为任何具有计时器的视图控制器手动invalidaterelease NSTimer(通过释放对视图控制器的强引用)。 (例如,可能有一个停止计时器的协议,让您的子视图控制器符合该协议。)

【讨论】:

  • @Ali 我在想你为什么要这样做removeFromSuperview 的东西,并且想知道你是否将视图控制器的视图添加到子视图中,如果是这样,如果你做了必要的@987654360 @ 也。如果是这样,您必须在清理时执行关联的removeFromParentViewController。请参阅我更新答案的第 2 点。
  • 1.我确实使用了静态分析器,没有任何问题。
  • 2.是的,我正在创建视图控制器,然后将其视图添加为像这样的子视图[scrollView addSubview:countdownController.view]; 请检查我在上面问题中链接的原始问题。 3. 我这样填充数组:NSMutableArray *controllers = [[NSMutableArray alloc] init]; for (unsigned i = 0; i &lt; kNumberOfPages; i++) { [controllers addObject:[NSNull null]]; } self.viewControllers = controllers; [controllers release]; 4. 我会尝试检查数组对象的保留计数。
  • 4.再次。是的,viewControllers 数组对象的保留计数是每个对象的 2。但我真的不知道这种额外的保留是从哪里形成的。您能否检查我的原始问题并提供一些有用的提示?比你好多了。问题是在 Instruments 中,如果数组有 2 个控制器,我只看到 2 个活着的 viewController 对象。这可能吗?
  • 是的。这是视图控制器中使用的 NSTimer 创建的保留周期。您对问题的更新是正确答案。
【解决方案2】:

你可以做一个快速测试:

- (void) test
{
    NSMutableArray *testArray = [NSMutableArray arrayWithCapacity:0];
    // MyViewController is your view controller class
    MyViewController *vc = [[MyViewController alloc] initWithNibName:@"MyViewController" bundle:nil]; // add autorelease if you aren't using ARC
    [testArray addObject:vc];

    // On exit, testArray will be released, so will vc
}

在 MyViewController 中的 dealloc 上设置断点并检查它是否被调用。如果它被调用,而不是在你的代码中,这意味着你的视图控制器被保留在某个地方,你必须找到它们被保留在哪里。

【讨论】:

  • 我做了测试,autorelease 确实释放了viewController 并调用了dealloc。我没有使用 ARC,但我不想使用 autorelease 我想在我选择的时间释放 te viewController,那就是滚动的“viewWillDisappear”。
  • 如果在将视图控制器添加到 self.viewControllers 之前没有使用 autorelease 创建视图控制器,则视图控制器的保留计数为 +2(alloc/initWithNibName 为 +1,NSMutableArray 中的 addObject 为 +1 )。如果只释放 self.viewControllers = nil 的数组,self.viewControllers 中的视图控制器的保留计数仍然为 +1,因此它们永远不会被释放。为了释放它们,你可以 [self.viewControllers makeObjectsPerformSelector:@selector(release)] before self.viewControllers = nil;
【解决方案3】:

鉴于提供的详细信息,我们很难诊断。但是,您可以通过运行 Instruments(例如 Leaks)自行快速诊断。如果进行了相应的配置,它可以:

  • 指出保留周期
  • 记录所有引用计数操作
  • 和/或您可以使用 heapshot 分析

一旦掌握了这些工具,隔离此类问题所需的时间(通常)应该会缩短到几分钟。

【讨论】:

  • 我做了所有这些。请检查我在上面问题中链接的原始问题。现在归结为释放NSMutableArray保留的viewControllers
猜你喜欢
  • 2013-12-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-18
  • 2013-07-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-27
相关资源
最近更新 更多