【问题标题】:Factors that affect retain count影响保留计数的因素
【发布时间】:2012-06-23 04:59:57
【问题描述】:

我很难找到内存泄漏。我正在使用 cocos2d。这是两个类的数据区:

@interface Dungeon : CCLayerColor {
    DungeonLevel *aDungeonLevel;
    Player *thePlayer;

    // list of all monster file names
    NSMutableArray *monsterNames;

    // array of how many monsters there are of each monster level
    NSMutableArray *monsterLevels;

    MessageView *theMessageView;

    DungeonDisplay *theDisplay;

    bool processing;

    int currentDungeonLevel;    
}

@interface DungeonDisplay : CCLayerColor {
    NSMutableArray *displayGrid;
    NSMutableArray *displayGrid2;
    NSMutableArray *displayGrid3;
    NSMutableArray *displayGrid4;
    NSMutableArray *dungeonMatrix;
    NSMutableArray *monsterSprites;
    Dungeon *theDungeon;  
    int xdelt;
    int ydelt;
    CGPoint lowerLeft;
    Player *thePlayer;
    CCSprite *playerSprite;
    CCSprite *mSprite1;

    ButtonsLayer *buttonArea;

    double previousTime;
    double currentTime;
    double touchTimePrev;
    bool touchFlag;
    bool processing;
    bool processing2;
    bool animating;
    bool flipSprite;
    bool doIdleAnimation;
    bool isAttacking;
    int firstIteration;
    CGPoint dungeonOriginalPosition;
    CGPoint playerOriginalPosition;
    CGPoint mSprite1Original;
    CGPoint buttonOriginal;
    CCTimer *myTimer;

    // List of Messages
    NSMutableArray *messages;    
    int messageIndex;

    // player transparency level
    int transparency;

    // indicates that walls need to become transparent
    bool needTransparency;

    int pXInc;
    int pYInc;
    int tempx;
    int tempy;

    // debugging variables
    CCLabelTTF *debugLabel1;
    CCLabelTTF *debugLabel2;

    // the Map
    MiniMap *aMap;
}

好的,现在 Dungeon 对象通过与另一个对象 DungeonLevel 交互来创建 DungeonDisplay 对象(我认为这与找出 DungeonDisplay 未释放的原因特别相关)。这是创建“单例”DungeonDisplay 对象的所有代码:

-(void) displayDungeon
{
    if (!theDisplay) {
        theDisplay = [[DungeonDisplay alloc]init];
        [self addChild:theDisplay z:101];
        [theDisplay letTheDungeon:self];    
    }
    else {
        [thePlayer placePC:thePlayer.pCLocation];
        [theDisplay displayStructure];
    }
    theDisplay.visible = true;
    aDungeonLevel.visible = NO;
}

由于某种原因,在 addChild(一个 cocos 方法)之后,保留计数跳到 4(从 1)。 “letTheDungeon”对保留计数没有影响(如预期的那样)。

【问题讨论】:

  • 您能提供一些背景信息吗?这是从什么类型的对象调用的,DungeonDisplay 是什么类型的对象?我假设addChild 是您编写的方法是否正确(在这种情况下,您也可以与我们共享该代码)?当我看到计数像那样跳跃时,这通常是我将它添加到 NSMutableArray/NSMutableDictionary 并忽略从该结构中删除它的结果。但是我们这里没有足够的数据来诊断它。我知道这很痛苦,但是你能给我们更多的上下文和更多的相关代码吗?
  • 静态分析仪是否给您一个干净的健康账单?只需 Shift-command-B 即可分析您的代码。

标签: objective-c ios xcode cocos2d-iphone


【解决方案1】:

问题:“我很难找到内存泄漏的位置。...有没有人列出了增加和减少保留计数的具体事项的完整列表?”

答案:哇,很多东西。只关注增加保留计数的因素,它包括:添加子视图;推送/展示控制器;添加到字典和数组;任何名称以allocnewcopymutableCopy 开头的方法;任何retain 调用;在viewDidLoad 中以非ARC 代码创建对象,而忽略在dealloc 中清理它们;在非 ARC 代码中的一个指针中分配另一个新对象,该指针已经指向尚未发布的项目;名称中带有createcopy 的任何核心基础功能;等等,这可能只是表面上的问题。减少保留计数的列表也一样长。

无意冒犯,这不太可能是追踪泄漏的有效途径。 (这就像说有人在曼哈顿被枪杀,所以让我们列出东海岸持枪的每个人的名单。)我建议你更多地采用 CSI 方法:

  1. 通过Xcode static analyzer 运行您的代码。在您解决所有这些问题之前,没有必要进一步研究。您应该从静态分析中得到个警告。

  2. 使用分析器工具find the leak。一旦您学会了如何使用该工具,它通常可以准确地向您显示导致泄漏的对象和代码行,此时解决问题要容易得多。

  3. 确保您完全阅读并理解Advanced Memory Management。如果您正在做任何与核心基础相关的事情,请查看Memory Management Programming Guide for Core Foundation

  4. 如果您不使用 ARC,请开始输入调试消息,检查您的各种对象的 retainCount

如果您发现有一段代码泄漏,如果您无法弄清楚,请在 StackOverflow 上发布有问题的代码(确保告诉我们它是否是 ARC),我们可以帮助您进一步诊断.

我真的不是要刻薄,但这个问题就目前而言,范围太广,我们无法为您提供帮助(即使理论上有人可以为您提供全面的答案,我也无法想象会是对你有帮助)。不过,希望上述一些技巧能为您指明正确的方向。

我真的很理解你的挫败感。第一个你决定认真追踪泄漏的项目是一个痛苦的练习。你必须掌握 Objective-C 内存管理的重要世界,并学习一些相当复杂的工具(尤其是分析器)。但是一旦你在一个大项目中完成了一次练习并掌握了工具,你就会有那种“啊哈”的感觉,追踪内存泄漏将成为一个简单(或至少有条理)的过程。

【讨论】:

  • 我检查了静态分析器,没有问题(除了cocos库)。泄漏工具没有发现重大泄漏,但这与分配工具相反,分配工具表明内存正在泄漏(导致我的崩溃)。我已经确定了哪些 dealloc 没有被调用(使用 NSLogs),但无法弄清楚为什么保留计数没有正确下降,因此可以调用它。
  • 我不太了解“除了可可库”参考。您很少会在现有库中发现小泄漏,但我从未见过与我的代码没有直接关系的静态分析器警告。
  • 另外,您说您遇到了导致崩溃的泄漏。泄漏导致崩溃的情况很少见(除非您泄漏很多并且未来的内存分配失败)。大多数泄漏表现为可用内存的缓慢、烦人的减少。崩溃通常是过度释放项目或错误指针的结果。打开zombies 可以帮助解决这个问题。
  • 对于第一条评论:静态分析器显示哪些特定的 .m 文件(以及确切的位置)有什么问题。报告问题的唯一文件(除了一个不是真正错误的“逻辑”错误)是 CCMenuItem.m、CCSprite.m 等。显示错误如“死代码”和其他一些我无法理解的奇怪错误,但是不在我专门编写的任何 .m 文件或代码中。
  • 第二个:我相信是内存分配本身导致了崩溃:虽然从程序开始就有一些小的内存泄漏,包括在“简单音频引擎”中,我认为一个 cocos2d 模块,它们一点也不麻烦。我的程序获得了 72MB 的内存并且运行良好,直到我到达某个部分,当我从“城镇”到“地牢”来回移动时,2-3MB 被泄露。在大约 25 到 35 次嬉戏之后,程序就会崩溃,当然,如果玩家愿意的话,当然可以在一次会议上进行这么多的尝试。
【解决方案2】:

可能导致泄漏的第一个原因是您没有释放创建的显示实例。将您的代码更改为

if (!theDisplay) 
{
    theDisplay = [[DungeonDisplay alloc]init];
    [self addChild:theDisplay z:101];
    [theDisplay release];  // add this line
    [theDisplay letTheDungeon:self];    
}

if (!theDisplay) 
{
    theDisplay = [[[DungeonDisplay alloc] init] autorelease];  // create autoreleased object
    [self addChild:theDisplay z:101];
    [theDisplay letTheDungeon:self];    
}

它将解决至少一个内存问题。

【讨论】:

  • 谢谢。实际上我所做的是,在同一个类的另一个函数中,当准备离开时,我 [self removeChild:theDisplay] 然后 [theDisplay release];我认为这是正确的,因为 alloc 和 addchild 都会增加保留计数。
  • @user1437403 虽然我怀疑这与您的原始问题无关,但我同意 Morion 的观点,即在 addChild 之后立即释放(即减少保留计数)是一种好习惯(明确或通过自动释放)。没有理由推迟这个版本,在很多情况下(虽然不是这个)它可以让你头疼。但我从你的其他回复中看到你解决了你的问题。太好了!
【解决方案3】:

感谢您的所有回答。问题解决了,我再次有微不足道的泄漏。问题在于子类 DungeonDisplay 中的 CCTouchDispatcher。我将处理触摸的代码更改为地牢类,并进行了一些其他小的调整,每个人都被调用了 dealloc。

无论如何,它再次坚如磐石。我来回移动了一百多次,分配的内存没有变化。事实上,我现在的浏览量低于 70 MB,比以前少了。

再次感谢您的鼓励和支持。

【讨论】:

    【解决方案4】:

    好吧,这可能不是“科学”正确的,但有时你必须做你必须做的事情。按如下方式使用 Instruments Zombies 工具。在你知道你已经泄露了一个对象的代码中的某个地方,发出 [theLeakedObject_release] 的次数与它僵尸出的次数一样多。然后在仪器中,您将能够获得保留计数的踪迹,哪个类增加它,哪个类减少它,按照它发生的顺序,直到很明显你僵死了。您应该能够“发现”一个不应该存在的保持器,然后从那里取出。

    ps。作为个人实践,在创建从 CCNode 派生的对象时,我遵循 Morion 的建议实践并坚持使用自动释放分配模式。保持东西整洁,清理:是的过程很好地在我之后擦拭:)。对于任何其他商务舱,我特别保留/释放,以便在自动释放池中为 cocos 留出尽可能多的空间。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-10-07
      • 2012-01-03
      • 2011-04-05
      • 1970-01-01
      • 1970-01-01
      • 2020-08-09
      • 2013-09-01
      • 1970-01-01
      相关资源
      最近更新 更多