内存未在使用ARC的iOS应用程序中发布

时间:2013-02-12 23:51:33

标签: ios memory-management automatic-ref-counting

我使用ARC构建了一个简单的琐事游戏。在使用Xcode中的Allocations分析工具分析其内存使用情况时,我发现内存并不总是被释放。对于问题的一个示例,我有一个ActivePlayer对象的类:

ActivePlayer.h:

@interface ActivePlayer : NSObject

@property (nonatomic, strong) NSString * name;
@property (nonatomic) NSInteger overallScore;
@property (nonatomic) NSInteger questionScore;

- (id) initWithName:(NSString *)name;

@end

ActivePlayer.m:

#import "ActivePlayer.h"

@interface ActivePlayer ()

@end


@implementation ActivePlayer

- (id) initWithName:(NSString *)name
{
    self = [self init];
    if (self) {
        self.name = name;
        self.overallScore = 0;
    }
    return self;
}

/*
- (void)dealloc
{
    self.name = nil;
}
*/
@end

ActivePlayer是在ActiveGame类的createPlayer方法中创建的:

[[ActivePlayer alloc] initWithName:name]

我正在执行以下测试用例:我开始一个新游戏(分配一个ActivePlayer),我回答一个问题,然后游戏结束(此时ActivePlayer被解除分配)。然后我可以开始另一个游戏并重复这个循环(每个循环是一个“游戏”,如下所述)。在使用Allocations性能分析工具时,我期望看到的是内存已经在游戏中分配,但在游戏结束后已经解除分配(无论我玩多少次游戏)。但我发现情况并非总是如此:

BTW:下面的每个项目符号行描述了“分配”工具的“对象列表”选项卡中的一行;这个网站不会让我发布截图,因此文字描述。所有行都是Live;我只查看创建和静止分配。

在游戏#1正在进行中时,我看到以下分配。

  • 类别= ActivePlayer;大小= 16;负责的来电= - [ActiveGame createPlayer:]
  • 类别= Malloc 48字节;大小= 48;负责的来电= - [ActivePlayer initWithName:]

比赛#1完成后,我看到以下内容。 ActivePlayer对象已被释放,但48字节仍然是Live。

  • 类别= Malloc 48字节;大小= 48;负责的来电= - [ActivePlayer initWithName:]

如果我开始游戏#2,我会在游戏进行过程中看到以下内容。除了来自游戏#1的那个之外,还有两个新的分配。

  • 类别= Malloc 48字节;大小= 48;负责的来电= - [ActivePlayer initWithName:]
  • 类别= ActivePlayer;大小= 16;负责的来电= - [ActiveGame createPlayer:]
  • 类别= Malloc 144字节;大小= 144;负责的来电= - [ActivePlayer initWithName:]

在第2场比赛结束后,我看到以下内容。同样,ActivePlayer对象已被解除分配,但“Malloc X字节”分配仍然存在。

  • 类别= Malloc 48字节;大小= 48;负责的来电= - [ActivePlayer initWithName:]
  • 类别= Malloc 144字节;大小= 144;负责的来电= - [ActivePlayer initWithName:]

之后,我得到了不寻常的结果 - 如果我玩游戏#3,#4和#5,我从未在Category =“Malloc X Bytes”中看到游戏内行,只有Category = ActivePlayer的新行,游戏结束后释放。如上所示,前两个“Malloc”行继续存在。我还看到了其他奇怪的行为 - 在昨天使用iPhone 6.0模拟器进行测试时,实时内存仅在游戏#2和#3之后留下,但不是游戏#1,#4和#5。因此,虽然内存仍然是分配的,但它出现的时间似乎因我的设备和不同版本的模拟器而异。

我的问题:

  • 我的理解是否正确,我不应该在游戏结束后调用initWithPlayer并且释放ActivePlayer对象时看到任何实时内存?
  • 如果是,导致它的原因是什么,以及如何解除分配?
  • 或者我根本不需要担心它?

注意:

  • 这些截图来自在运行iOS 6.1的iPhone 4上运行我的应用程序。但我看到类似的行为与iPhone模拟器5.1,6.0和6.1一起运行,我在升级之前在运行iOS 6.0的iPhone上看到了它。
  • 在ActivePlayer.m中,dealloc方法目前被注释掉了,虽然我已经测试了,但是它已被取消注释并且已经验证它被调用(由系统调用;我不直接在任何地方调用dealloc)。无论哪种方式,行为都是一样的。
  • 对于它的价值,Leaks概要分析工具没有报告任何内容。
  • 虽然这是一个导致192字节的实时内存的例子,我相信应该被释放,但是我的许多类都看到了这一点,即看起来内存分配随着时间的推移而增长,我认为这是一个问题。

3 个答案:

答案 0 :(得分:3)

您列出的代码很好。看起来您仍然在代码中的其他位置维护对原始ActivePlayer的引用。

作为旁注,您创建ActivePlayer的模式不是常态 - 通常一个类不会在init方法中调用alloc。相反,调用者应该执行:

[[ActivePlayer alloc] initWithName:@"Bob"];

并且您的init方法应该使用

的返回值
[super init];

答案 1 :(得分:2)

我觉得很奇怪你的构造函数是静态的(+符号)。命名约定要求名称前缀为init的方法将返回托管内存对象。我怀疑内部方法的结果是根据其方法名称进行评估的。

答案 2 :(得分:1)

我认为实例计数测试确定您的代码没有泄漏ActivePlayers。顺便说一句,构造函数和inits上的更好的形式是这样的:

// .h

@interface ActivePlayer : NSObject

+ (id)activePlayerWithName:(NSString *)name;

@end

// .m

+ (id)activePlayerWithName:(NSString *)name {
    return [[self alloc] initWithName:name];
}

// if you want to make this public (include in .h interface) you can
// the callers will have the choice of alloc init pattern, or the factory
//
- (id)initWithName:(NSString *)name {
    self = [self init];
    if (self) {
        _name = name;
    }
    return self;
}

然后来电者这样做:

ActivePlayer *activePlayer = [ActivePlayer activePlayerWithName:@"Charlie"];