未使用ARC在iOS应用程序中释放内存

未使用ARC在iOS应用程序中释放内存,ios,memory-management,automatic-ref-counting,Ios,Memory Management,Automatic Ref Counting,我用ARC构建了一个简单的琐事游戏。在使用Xcode中的分配分析工具分析其内存使用情况时,我发现内存并不总是被释放的。例如,我有一个ActivePlayer对象的类: ActivePlayer.h: @interface ActivePlayer : NSObject @property (nonatomic, strong) NSString * name; @property (nonatomic) NSInteger overallScore; @property (nonatomic)

我用ARC构建了一个简单的琐事游戏。在使用Xcode中的分配分析工具分析其内存使用情况时,我发现内存并不总是被释放的。例如,我有一个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被解除分配)。然后我可以开始另一个游戏并重复这个循环(每个循环都是一个“游戏”,如下所述)。在使用分配分析工具时,我希望看到的是内存已经分配在游戏中间,但在游戏结束后被释放(不管我玩了多少次游戏)。但我发现情况并非总是如此:

顺便说一句:下面的每个项目符号行描述了“分配”工具的“对象列表”选项卡中的一行;这个网站不允许我发布截图,因此有文字描述。所有行都是活动的;我只查看创建的和仍然存在的分配

在游戏1进行期间,我看到了以下分配

  • 类别=活动玩家;尺寸=16;负责呼叫方=-[ActiveGame createPlayer:]
  • 类别=Malloc 48字节;尺寸=48;负责调用方=-[ActivePlayer initWithName:]
游戏1结束后,我看到了以下内容。ActivePlayer对象已解除分配,但48字节仍然有效

  • 类别=Malloc 48字节;尺寸=48;负责调用方=-[ActivePlayer initWithName:]
如果我开始游戏#2,我会在游戏进行过程中看到以下内容。除了游戏#1中的分配外,还有两个新的分配

  • 类别=Malloc 48字节;尺寸=48;负责调用方=-[ActivePlayer initWithName:]
  • 类别=活动玩家;尺寸=16;负责呼叫方=-[ActiveGame createPlayer:]
  • 类别=Malloc 144字节;尺寸=144;负责调用方=-[ActivePlayer initWithName:]
第二场比赛结束后,我看到了以下内容。同样,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。因此,尽管内存仍在分配,但在我的设备和不同版本的模拟器中,内存出现的时间似乎有所不同

我的问题是:

  • 我的理解正确吗?在游戏结束并且ActivePlayer对象被释放后,我不应该从调用initWithPlayer中看到任何活动内存
  • 如果是,原因是什么,如何解除分配
  • 或者我根本不需要担心它
注:

  • 这些截图来自在运行iOS 6.1的iPhone 4上运行我的应用程序。但我在5.1、6.0和6.1版的iPhone模拟器上看到了类似的行为,在升级之前,我在运行iOS 6.0的iPhone上看到了类似的行为
  • 在ActivePlayer.m中,dealloc方法目前已被注释掉,尽管我已经在未注释的情况下进行了测试,并验证了它正在被调用(由系统调用;我不会在任何地方直接调用dealloc)。不管怎样,行为都是一样的
  • 值得一提的是,泄漏分析工具没有报告任何内容
  • 虽然这是一个我认为应该释放192字节活动内存的例子,但我在我的许多类中都看到了这一点,即内存分配似乎随着时间的推移而增加,我认为这是一个问题
您列出的代码很好。看起来您仍然在代码的其他地方维护对原始ActivePlayer的引用

作为补充说明,创建ActivePlayer的模式不是标准模式——通常类不会从init方法中调用alloc。相反,调用方应执行以下操作:

[[ActivePlayer alloc] initWithName:@"Bob"];
您的init方法应该使用

[super init];

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

我认为实例计数测试确定您的代码没有泄漏ActivePlayer。另一方面,构造函数和初始化的更好形式如下:

// .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"];

问题可能出现在代码的其他地方。你能多发一些吗?某些时候,某些东西一定保存着对ActivePlayer对象的引用。你能看到在哪里,通过哪些对象,retain计数增加了吗?我也想到了这一点,并寻找了ActivePlayer对象可能被引用的其他地方。但是,如果是其他对象持有对ActivePlayer的引用,那么我希望行“Category=ActivePlayer;Size=16;Responsible Caller=+[ActivePlayer initWithName:]”仍然存在于对象列表中(它不存在),并且(2)不会调用对象的dealloc方法(我已经验证了它正在被调用)。再次,如果我的理解是正确的……让我们使用dealloc来实现调试优势:在fi中创建一个静态int