Joh*_*ith 1 iphone cocoa objective-c nstimer uiview
我在UIView中有一行UILabel文本,它通过NSTimer定期更新.这段代码应该经常在屏幕底部附近写一个状态项.数据来自其控制之外.
我的应用程序内存耗尽非常快,因为似乎UILabel没有被释放.似乎dealloc从未被调用过.
这是我的代码的一个非常压缩的版本(为清楚起见,删除了错误检查等).
文件:SbarLeakAppDelegate.h
#import <UIKit/UIKit.h>
#import "Status.h"
@interface SbarLeakAppDelegate : NSObject
{
UIWindow *window;
Model *model;
}
@end
Run Code Online (Sandbox Code Playgroud)
文件:SbarLeakAppDelegate.m
#import "SbarLeakAppDelegate.h"
@implementation SbarLeakAppDelegate
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
model=[Model sharedModel];
Status * st=[[Status alloc] initWithFrame:CGRectMake(0.0, 420.0, 320.0, 12.0)];
[window addSubview:st];
[st release];
[window makeKeyAndVisible];
}
- (void)dealloc
{
[window release];
[super dealloc];
}
@end
Run Code Online (Sandbox Code Playgroud)
文件:Status.h
#import <UIKit/UIKit.h>
#import "Model.h"
@interface Status : UIView
{
Model *model;
UILabel * title;
}
@end
Run Code Online (Sandbox Code Playgroud)
文件:Status.m这就是问题所在.UILabel似乎没有被释放,并且很可能是字符串.
#import "Status.h"
@implementation Status
- (id)initWithFrame:(CGRect)frame
{
self=[super initWithFrame:frame];
model=[Model sharedModel];
[NSTimer scheduledTimerWithTimeInterval:.200 target:self selector:@selector(setNeedsDisplay) userInfo:nil repeats:YES];
return self;
}
- (void)drawRect:(CGRect)rect
{
title =[[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 12.0f)];
title.text = [NSString stringWithFormat:@"Tick %d", [model n]] ;
[self addSubview:title];
[title release];
}
- (void)dealloc
{
[super dealloc];
}
@end
Run Code Online (Sandbox Code Playgroud)
文件:Model.h(这个和下一个是数据源,因此只包括完整性.)它所做的只是每秒更新一个计数器.
#import <Foundation/Foundation.h>
@interface Model : NSObject
{
int n;
}
@property int n;
+(Model *) sharedModel;
-(void) inc;
@end
Run Code Online (Sandbox Code Playgroud)
文件:Model.m
#import "Model.h"
@implementation Model
static Model * sharedModel = nil;
+ (Model *) sharedModel
{
if (sharedModel == nil)
sharedModel = [[self alloc] init];
return sharedModel;
}
@synthesize n;
-(id) init
{
self=[super init];
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(inc) userInfo:nil repeats:YES];
return self;
}
-(void) inc
{
n++;
}
@end
Run Code Online (Sandbox Code Playgroud)
问题是你永远不会从状态UIView中删除UILabel.我们来看看drawRect中的保留计数:
(void)drawRect:(CGRect)rect {
title =[[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 12.0f)];
Run Code Online (Sandbox Code Playgroud)
在这里,您已经创建了一个带有alloc的UILabel,它创建了一个保留计数为1的对象.
[self addSubview:title];
[title release];
Run Code Online (Sandbox Code Playgroud)
将UILabel添加到Status视图会将title的保留计数增加到2.以下版本导致最终保留计数为1.由于永远不会从其superview中删除该对象,因此永远不会释放该对象.
基本上,每次定时器被触发时,你都会在另一个UILabel之上添加一个UILabel,直到内存耗尽.
如下所示,您应该在视图加载时创建一次UILabel,并使用[model n]更新UILabel的文本.
作为管家备注,您可能还需要确保在dealloc方法中正确释放任何遗留对象.'model'和'title'应该在Status'dealloc中发布,就像'model'应该在SbarLeakAppDelegate中一样.
希望这可以帮助.
编辑[1]:
听起来你在这一点上已经很好地处理了内存问题.我只想建议您使用的两个计时器的另一种替代方案.
您在Status对象中运行的计时器每隔0.2秒触发一次.实际递增"模型"值n的计时器每秒仅触发一次.虽然我相信您这样做是为了确保状态视图更加经常"刷新率",但您可能会在不更改数据的情况下每秒重新绘制视图4或5次.虽然这可能不明显,因为视图相当简单,但您可能需要考虑类似NSNotification的内容.
使用NSNotification,您可以让Status对象"观察"当值"n"发生更改时模型将触发的特定类型的通知.(在这种情况下,大约每秒1次).
您还可以指定回调方法以在收到通知时处理通知.这样,只有在模型数据实际更改时才会调用-setNeedsDisplay.
| 归档时间: |
|
| 查看次数: |
1730 次 |
| 最近记录: |