了解iOS上的仪器内存分配日志

时间:2013-12-09 13:35:34

标签: ios memory-management instruments

我已经构建了一个几乎已经完成的iOS应用程序,但是,我最近经历过由于“内存压力”导致它崩溃的情况。所以我开始分析仪器中的内存分配,当然,应用程序确实使用了相当多的内存,而且在使用过程中似乎只会增加。

然而,相对较新的仪器内存分配,我无法破译52%的分配,如下面的屏幕截图所示:

enter image description here

它显然与Core Animation有关,但究竟是什么对我来说难以确定,所以我认为一些聪明的头脑可能知道答案。

面包屑:

我的应用程序使用自定义segue,在视图控制器之间移动时,正在进行大量动画制作。这是一个例子:

@interface AreaToKeyFiguresSegue : UIStoryboardSegue

@end

...

@implementation AreaToKeyFiguresSegue

- (void)perform
{
    [self sourceControllerOut];
}

- (void)sourceControllerOut
{
    AreaChooserViewController *sourceViewController = (AreaChooserViewController *) [self sourceViewController];
    KeyFigureViewController *destinationController = (KeyFigureViewController *) [self destinationViewController];

    double ratio = 22.0/sourceViewController.titleLabel.font.pointSize;

    sourceViewController.titleLabel.adjustsFontSizeToFitWidth = YES;

    [UIView animateWithDuration:TRANSITION_DURATION delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{

        // Animate areaChooser
        sourceViewController.areaChooserScrollView.alpha = 0;
        sourceViewController.areaScrollViewVerticalSpaceConstraint.constant = -300;

        sourceViewController.backButtonVerticalConstraint.constant = 20;
        sourceViewController.backButton.transform = CGAffineTransformScale(sourceViewController.backButton.transform, ratio, ratio);
        sourceViewController.backButton.titleLabel.textColor = [UIColor redKombitColor];

        sourceViewController.backArrowPlaceholderVerticalConstraint.constant = 14;
        sourceViewController.backArrowPlaceholder.alpha = 1;

        sourceViewController.areaLabelVerticalConstraint.constant = 50;
        sourceViewController.areaLabel.alpha = 1;

        [sourceViewController.view layoutIfNeeded];

    } completion:^(BOOL finished) {
        [destinationController view]; // Make sure destionation view is initialized before animating it
        [sourceViewController.navigationController pushViewController:destinationController animated:NO]; // Push new viewController without animating it

        [self destinationControllerIn]; // Now animate destination controller
    }];
}

- (void)destinationControllerIn
{
    AreaChooserViewController *sourceViewController = (AreaChooserViewController *) [self sourceViewController];
    KeyFigureViewController *destinationController = (KeyFigureViewController *) [self destinationViewController];

    destinationController.keyFigureTableViewVerticalConstraint.constant = 600;
    destinationController.keyFigureTableView.alpha = 0.0;
    destinationController.allFavoritesSegmentedControl.alpha = 0.0;
    [destinationController.view layoutIfNeeded];
    [sourceViewController.segueProgress setHidden:YES];
} 

@end

每当要弹出一个视图控制器时,我只是做相反的事情:

- (IBAction)goBack:(id)sender
{
    [UIView animateWithDuration:TRANSITION_DURATION delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{

        [self.keyFigureTableView setAlpha:0];
        self.keyFigureTableViewVerticalConstraint.constant = 700;
        [self.allFavoritesSegmentedControl setAlpha:0];
        [self.view layoutIfNeeded];
    } completion:^(BOOL finished) {
        [self.navigationController popViewControllerAnimated:NO]; // Pop viewController without animating it
    }];
}

编辑:

大多数内存分配在推送视图控制器时发生,即使它之前已经显示过。即从

开始

A - > B - > ç

B < - C

B - &gt; ç

其中“ - &gt;” = push和“&lt; - ”= pop,每个“ - &gt;”分配更多内存,“&lt; - ”永远不会释放任何内存。

更多细节

根据乐器,我有没有僵尸没有泄漏。静态分析也没有给出什么。我的应用程序只是继续分配内存,直到它最终崩溃。

大约70%的内存分配发生在以下调用堆栈中,这与我的代码(反向调用树)无关:

enter image description here

4 个答案:

答案 0 :(得分:6)

以下是我调试这些内容的方法。

  1. 在仪器中,使用分配工具并启用“记录参考计数”
  2. enter image description here

    1. 将您的应用运行至“稳定状态”,包括执行您认为会泄漏几次的操作。

    2. 在仪器中,使用输入/输出标记设置基线记忆水平。

    3. 执行您认为泄漏几次的操作。 (比如说7)

    4. 在Instruments中,切换到显示所有分配的视图,并查找已分配但未解除分配的对象与您刚刚执行的操作相同的次数(也可能是7次)。您首先要尝试查找特定于您的程序的对象...所以更喜欢MyNetworkOperation个实例而不是像NSData这样的通用基础类。 click arrow next to class you are interested in click arrow next to an allocated object

    5. 选择一个尚未解除分配的对象,并查看其分配历史记录。您将能够看到相关对象的每个alloc / retain / release / autorelease的调用堆栈。可能其中一个调用看起来很可疑。 history of retain/release for selected object

    6. 我认为这些步骤在非ARC环境中的应用更多。在ARC下你可能正在寻找一个保留周期的东西。

      通常,您可以通过确保强引用仅朝一个方向来避免保留周期...例如,视图具有对其子视图的强引用,并且每个子视图必须仅使用弱引用来引用任何父视图。或者您的视图控制器可能会强烈引用您的视图。您的视图必须仅对其视图控制器具有弱引用。另一种说法是在每个关系中决定哪个对象“拥有”另一个对象。

答案 1 :(得分:2)

乐器说你的 UIViews正在泄漏(或者说没有准确地解除分配)。

每次推送都会创建一个新的destinationControllerdestinationController's视图将由CALayer支持,CALayer会杀死内存。

为了证明这一点,您可以为dealloc实施destinationController并在那里设置一个断点,看看它是否被调用。

如果调用dealloc,则可以对其他对象执行此操作以找出哪一个。 (如destinationController.view

为什么要泄漏?

1,您可能有保留捕获destinationController的周期。调试很麻烦,您可能需要检查所有相关的代码。

2,每个destinationController可能会被一些长生命物体保留。 (重复的NSTimer没有失效,Singleton,RootViewController,CADisplayLink ...)

3,虚假缓存。您缓存某些内容并尝试重用它。但是,缓存逻辑存在错误,这些对象永远不会被重用,并且不断插入新对象。

答案 2 :(得分:0)

您的动画主要包含更改的Alpha值或颜色。为了减少自定义segue中的动画代码,我建议您将目标视图控制器的动画代码(即方法destinationControllerIn)移动到目标视图控制器的viewDidLoad:

答案 3 :(得分:-2)

首先检查(方案诊断)是否已打开僵尸。僵尸意味着什么都没有被删除。鉴于你的记忆图表永远不会下降,这将是我第一次检查。