2013-02-12 94 views
3

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

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以创建的createPlayer方法在一个ActiveGame类:

[[ActivePlayer alloc] initWithName:name] 

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

顺便说一句:下面的每个项目符号行描述了分配工具的Objects List选项卡中的一行;本网站不会让我发布截图,因此文字说明。所有行都是实时的;我只查看创建和静态分配。

第1场比赛正在进行中,我看到下列分配。

  • 类别= ActivePlayer;大小= 16; Responsible Caller = - [ActiveGame createPlayer:]
  • Category = Malloc 48 Bytes;大小= 48; Responsible Caller = - [ActivePlayer initWithName:]

游戏#1完成后,我看到以下内容。 ActivePlayer对象已被释放,但48个字节仍为Live。

  • 类别= Malloc 48 Bytes;大小= 48; Responsible Caller = - [ActivePlayer initWithName:]

如果我开始游戏#2,在游戏进行时我会看到以下内容。除了第一场比赛之外,还有两个新的分配。

  • 类别= Malloc 48 Bytes;大小= 48; Responsible Caller = - [ActivePlayer initWithName:]
  • Category = ActivePlayer;大小= 16;负责任的来电= - [ActiveGame createPlayer:]
  • Category = Malloc 144 Bytes;大小= 144;负责任的来电者= [ActivePlayer initWithName:]

并且在游戏#2完成后,我看到以下内容。同样,ActivePlayer对象已被释放,但“Malloc X Bytes”分配仍然存在。

  • 类别= Malloc 48 Bytes;大小= 48; Responsible Caller = - [ActivePlayer initWithName:]
  • Category = Malloc 144 Bytes;大小= 144;负责来电= - [ActivePlayer initWithName:]

在那之后,我得到的不寻常的结果 - 如果我玩游戏#3,#4,#5,我从来没有看到在游戏中的行为类别=“的malloc X字节“,只有Category = ActivePlayer的新行,在游戏结束后释放。如上所示,前两个“Malloc”行继续存在。我还看到了其他奇怪的行为 - 在使用iPhone 6.0模拟器进行测试时,仅在游戏#2和#3之后,而没有游戏#1,#4和#5时,活动内存才被留下。所以,尽管内存仍然是分配的,但它发生的时间似乎在我的设备和不同版本的模拟器上有所不同。

而且我的问题:

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

注:

  • 这些截图来自于运行iOS 6.1的iPhone 4上运行我的应用程序。但是我看到类似的行为与iPhone Simulator for 5.1,6.0和6.1一起运行,我在升级之前在运行iOS 6.0的iPhone上看到它。
  • 在ActivePlayer.m中,dealloc方法当前已被注释掉,尽管我已经在未被注释的情况下进行了测试,并且已经验证了它被调用(由系统;我不直接在任何地方调用dealloc)。无论哪种方式,行为都是一样的。
  • 值得一提的是,Leaks剖析工具并未报告任何内容。
  • 虽然这是一个导致192字节的活内存的例子,我相信应该释放,但我看到这与我的许多类,即看起来内存分配随着时间增长,我认为是一个问题。
+1

*这个问题可能在其他地方在你的代码*你能发布更多?有些时候必须持有对ActivePlayer对象的引用。你能看到保留计数增加的位置和哪个对象? – paulmelnikow 2013-02-12 23:58:40

+0

我想到了这一点,并寻找可能引用ActivePlayer对象的其他地方。但是,如果某些其他对象持有对ActivePlayer的引用,那么我会预期“Category = ActivePlayer; Size = 16; Responsible Caller = + [ActivePlayer initWithName:]”行仍然会存在于对象列表(它不)和(2)对象的dealloc方法不会被调用(我已经验证它被调用)。再次,如果我的理解是正确的... – 2013-02-13 00:09:46

+0

让我们使用该dealloc来获得调试优势:在文件中创建一个静态int并将其增加到您的alloc init行旁边。记录在那里。然后递减并记录到dealloc中。 – danh 2013-02-13 00:34:30

回答

3

列出的代码没问题。看起来您仍然在代码中的其他位置保留对原始ActivePlayer的引用。作为一个方面说明,创建ActivePlayer的模式不是常态 - 通常一个类不会从init方法内调用alloc。相反,调用者应该执行:

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

和你的init方法应与

[super init]; 
+0

是的,你是对的。这是我的一个新手错误(以这种方式编写init方法)。我刚刚改变了它,乍一看,似乎我看到的额外的“Malloc X Bytes”行也消失了。要做一些其他代码mods和测试,看看我的不正确实施的init方法是不经意间导致内存问题。 – 2013-02-13 01:25:58

2

返回值工作,我觉得很奇怪,你的构造函数是静态的(+符号)。命名约定规定名称前缀为init的方法将返回受管内存对象。我怀疑内部方法的结果正在评估他们的方法名称。

+0

是的,你和@丹当然是对的。看到我上面的评论。这可能已经解决了这个问题,但是如果没有更多的代码更改和更多的测试就不会知道。 – 2013-02-13 01:28:12

+1

二读时,我高度怀疑这是正确的问题。 ARC之前重要的方法名称 - 以“-init”开头的方法应该给调用方一个+1保留计数对象; '+ activePlayerWithName'将被自动释放,并有一个有效的0保留计数。也许ARC以某种方式带来了一些这种逻辑,并且使用'+ init'方法行为不正确。 – paulmelnikow 2013-02-13 04:21:11

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"]; 
+0

我按照这里的建议进行了代码更改,进行了进一步测试,问题仍然存在。作为这个网站的新手,现在是否更好地更新原始问题以包含已更改的代码+更改我在“分配”工具中看到的内容的具体内容,或将评论发布到其中一个答案或原来的问题?预先感谢任何指导。 – 2013-02-13 19:50:18

+0

我会这样处理:如果这个问题仍然是关于漏水的ActivePlayer对象,坚持这一条,并张贴一些_evidence_,它的泄漏(如泄漏工具截屏的NSLog输出等),如果你想别的东西是错误的(包括泄漏一些其他对象),然后开始一个新的问题。新的问题会得到第一个活动的热潮,但如果有人认为你仍然在问这个问题,那么它将会被推倒。 – danh 2013-02-13 21:14:37

+0

我认为这一点是没有强有力的证据表明它正在泄漏。模拟器代码在某些地方泄漏,并且sdk可能也会泄漏。除非您在繁忙的代码路径中泄漏大量数据,否则我也不会太担心。 – danh 2013-02-13 21:16:18

相关问题