所以我对 objC 编程比较陌生。但不是 C。在一个更复杂的应用程序中,我认为我有内存泄漏。我编写了这个程序只是为了进行一些测试。该应用程序非常简单:它将一系列整数存储在 MutableArray 中,这些整数表示已安排的计时器。该应用程序在当前运行循环中有一个 NSTimer,它每秒检查一次是否是将计数器与 MutableArray 的正确元素进行比较的正确时间。一切正常,但调试面板中的内存增长,增长,增长...... 我已经尝试了一些变体,但我仍然缺少关于 ARC 的一些东西。我只是不明白,因为 ARC 不是垃圾收集器,为什么内存会增长以及我做错了什么。 这是代码:
-(id)initWithLabelUILabel *)label {
self = [super init];
self.list = [[mtAllarmList alloc]init];
self.label = label;
return self;
}
我的类初始化函数。我将标签引用(弱,因为它是 View Controller 拥有的)传递给我的类(class)。我还分配和初始化包含 MutableArray 和其他信息(在原始应用程序中,要播放的文件,卷,eccetera)的类 mtAllarmList。
-(void)ClockRun {
NSMethodSignature * signature = [mtClockController instanceMethodSignatureForSelectorselector(check)];
NSInvocation * selector = [NSInvocation invocationWithMethodSignature: signature];
[selector setTarget:self];
[selector setSelectorselector(check)];
[[NSRunLoop currentRunLoop] addTimer: self.time = [NSTimer scheduledTimerWithTimeInterval:1
invocation:selector
repeats:YES]
forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] runUntilDate:[[NSDate alloc]initWithTimeIntervalSinceNow: 30]];
}
ClockRun:是应用程序调用以启动一切的方法。它只是启动每秒触发的计时器来检查:
-(void)check {
self.counter++;
int i = [self.list check:self.counter];
if(i == 1) {
[self writeAllarmToLabel:self.label isPlayingAllarmNumber:self.counter];
}
else if (i == 2) {
[self writeAllarmToLabel:self.label theString: @"Stop"];
[self.time invalidate];
self.counter = 0;
}
else {
[self writeAllarmToLabel:self.label theString:[[NSString alloc]initWithFormat"controllo, %d", self.counter]];
}
NSLog(@"controllo %d", self.counter);
}
检查:简单地对 mtAllarmList 的 [list check: int] 方法的返回值使用react。如果定时器必须振铃,则返回 1,否则返回 0,如果序列结束则返回 2。在这种情况下 self.counter 将被设置为 0 并且 NSTimer 将无效。
-(id)init {
self = [super init];
self.arrayOfAllarms = [[NSMutableArray alloc]initWithCapacity:0];
int i;
for(i=1;i<=30;++i) {
[self.arrayOfAllarms addObject: [[NSNumber alloc]initWithInt:i*1]];
}
for(NSNumber * elemento in self.arrayOfAllarms)
NSLog(@"ho creato un array con elemento %d", [elemento intValue]);
return self;
}
在 mtAllarmList init 方法中模拟构造一个数组(我尝试了多种模式)并记录所有元素。
-(int)checkint)second {
int maxValue = [[self.arrayOfAllarms lastObject] intValue];
if(maxValue == second){
self.index = 0;
return 2;
} else {
if ([[self.arrayOfAllarms objectAtIndex:self.index] intValue] == second) {
self.index++;
return 1;
} else {
return 0;
}
}
}
检查方法是非常基本的,我认为不需要解释。
那么,为什么这个简单非常愚蠢的应用程序会泄漏?
Best Answer-推荐答案 strong>
由于您是在主运行循环中执行此操作,因此您可以(并且应该)简化 ClockRun 方法:
- (void)ClockRun {
self.time = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selectorselector(check) userInfo:nil repeats:YES];
}
那个NSInvocation 代码是不必要的,NSRunLoop 代码只能引入问题。
话虽如此,这不太可能是您内存消耗的来源。提供的代码片段中没有任何其他内容看起来像是明显的内存问题。如果您 100% 确信计时器已失效,则计时器不是问题。我想知道这个 mtClockController 处的 View Controller 之间的对象图.或者 View Controller 中的一些循环引用(例如,从 A 推送到 B 并再次推送到 A)。根据迄今为止提供的内容,很难说。
可悲的是,除了常规诊断之外,我们没有太多其他建议。首先,我将通过静态分析器运行应用程序(通过在 Xcode 中按 shift+command+B,或从 Xcode“产品”菜单中选择“配置文件”)。
其次,您应该通过 Leaks 和 Allocations 工具运行您的应用程序,以识别每次迭代中确切泄漏的内容。你有额外的 View Controller 实例吗?或者只是 mtClockController ?
除非您确定未释放的内容,否则很难对其进行补救。仪器是识别未发布内容的最佳工具。在 WWDC 2012 视频中 iOS App Performance: Memory视频的演示部分给出了使用 Instruments 的实用演示(以及大量关于内存管理的良好背景信息)。
第三,当我不确定事情是否在应该被释放的时候被释放时,我有时会包含 dealloc 告诉我对象何时被释放的方法,例如:
- (void)dealloc {
NSLog(@"%s", __PRETTY_FUNCTION__);
}
我建议这不仅适用于您的关键模型对象,也适用于您的 View Controller 。 (有时我们为我们的模型对象苦恼,只是为了意识到它是 View Controller 本身,它被其他东西保留。)
显然,Instruments 是一个更丰富的工具,但这可用于快速识别解除分配的失败(并向您展示什么是维护强引用)。
我通过 Instruments 运行你的应用程序,观察你的自定义对象,一切都被正确释放。下面,我标记了 A 代,按下按钮,让计时器到期,标记 B 代,再次按下按钮,等等。我做了四次,然后我模拟了一个内存警告,最后做了一次。一切看起来都很好(这是六个屏幕快照合二为一的汇编,显示了六代中每一代的总分配):
我检查了你的世代,以及分配本身,没有你的对象在那里。一切都得到很好的释放。唯一与 UIKit 相关联的 Cocoa 内部对象和 NSString . Cocoa Touch 会在幕后缓存我们无法控制的各种内容。我做最后的“模拟器内存警告”的原因是让 Cocoa 有机会清除它所能清除的东西(你会看到,尽管 Generations 报告了什么,总分配量下降了一点)。
最重要的是,您的代码很好,这里没有什么可担心的。将来,不要担心在几代人中偶然出现的东西,而是专注于(a)您的类(class); (b) 任何相当大的东西。但这些都不适用于这里。
事实上,如果您使用 mt 限制 Instruments 仅记录您的类的信息。前缀(您可以通过停止记录仪器并点击分配图上的“i”按钮并配置“记录类型”来完成此操作),您将看到您期望的图表/世代类型:
关于ios - 为什么这个带有 ARC 的简单应用程序会泄漏?,我们在Stack Overflow上找到一个类似的问题:
https://stackoverflow.com/questions/23202727/
|