我读过http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/BasicViewControllers/BasicViewControllers.html#//apple_ref/doc/uid/TP40007457-CH101-SW19,但我还不完全清楚这是如何运作的。
@property (nonatomic, retain) UIButton *startButton;
@property (nonatomic, retain) UITextView *infoTextView;
这是显示在标签栏中的视图控制器
- (void)loadView
{
UIView *newView = [[UIView alloc] init];
//self.startButton and addSubivew retains the button obect; retain count = 2
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
//autorelases
self.startButton = button;
[newView addSubview:startButton];
//addSubview retains infoTextView; self.infoTextview retains; retain count: 2
self.infoTextView = [[[UITextView alloc] initWithFrame:CGRectMake(0.0, 0.0, 280.0, 270.0)] autorelease];
//autoreleased
[newView addSubview:infoTextView];
//View controller retains the view hierarchy
self.view = newView;
[newView release];
}
//customization of the button and textview (text, frame, center, target-action etc)
- (void)viewDidLoad
{
[super viewDidLoad];
[self loadStartButton];
[self loadTextView];
}
//because these have retain properties, they are released through nil
- (void)viewDidUnload
{
self.startButton = nil;
self.infoTextView = nil; //retain counts - 1
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc
{
[startButton release];
[infoTextView release]; //retain counts -1
[super dealloc];
}
我的问题是:UIView对象是否应该保留两个?我看到它的方式是视图控制器保留UIView对象,viewController.view也保留UIView对象(通过添加子视图)。这是从概念上看它的正确方法吗?因为我的viewController也管理其.view属性所拥有的对象。
但是,我不确定在内存不足的情况下是否同时调用viewDidUnload和dealloc。我是否正确释放它们或设置内存泄漏?
(任何关于将代码放在错误位置的评论都会有所帮助)
答案 0 :(得分:0)
从概念上讲,我并不太担心实际的保留计数。我尝试更多来平衡我的电话。如果我在一个方法中本地保留,那么我(很可能)需要以相同的方法调用一个版本。
...
UIView *newView = [[UIView alloc] init]; <---- retain count gets +1
// ... do more stuff
[newView release]; newView = nil; <---- retain count gets -1
...
在上面,呼叫是平衡的,所以我们很好。
/*
* This creates an autoreleased button. Therefore I am not taking a retain on
* it so I don't need to release it
*/
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
//autorelases <---- this comment is redundant as the code states this
对于ivars,它的工作方式略有不同,因为通常您会尝试平衡init
和dealloc
方法中的调用,然后使用属性在整个班级中执行正确的操作。
/*
* As we have used sythesized properties the memory management is taken care
* for us if we use dot notation or call the setters/getters
*/
self.startButton = button;
OR
[self setStartButton:button];
/*
* Because this is an ivar and not a local method variable we balance our calls
* in the `init` (if we need to, remember ivars are initialized to nil for us)
* and `dealloc` methods.
*/
- (id)initWithButton:(UIButton *)startButton
{
self = [super init];
if (self) {
/*
* Notice we don't use self. You should try to access ivar
* directly in your init and dealloc methods to avoid side effects
*/
_startButton = [startButton retain];
}
return self;
}
- (void)dealloc
{
// Release any other ivars
/*
* Notice we don't use self. You should try to access ivar
* directly in your init and dealloc methods to avoid side effects
*/
[_startButton release];
[super dealloc];
}
// NOTE
// I have prefixed my ivars to make it more explicit when I am accessing them
// directly as opposed to accessing them through dot notation of normal method
// calls
添加视图通常不是我们关注的问题,因为我们相信框架会正确管理其任务内存
/*
* We don't need to worry about this taking a +1 as our code is doing our
* management correctly
*/
[newView addSubview:startButton];
内存管理摘要
retain
中的init
和release
中的dealloc
保持平衡,然后始终使用属性在整个班级中管理所有其他记忆任务。<强> viewDidUnload 强>
一般来说,您可以将viewDidLoad
和viewDidUnload
视为匹配对。您在viewDidLoad
中创建的任何内容都可以在viewDidUnload
中释放。您不应在viewDidUnload
中发布无法轻松重新创建的内容,请查看UIViewController的文档。
如果视图不是当前可见的屏幕并且出现内存不足的情况,则只会卸载视图。
答案 1 :(得分:0)
在内存不足的情况下,我认为您的视图控制器不会被解除分配。你的观点会。将调用viewDidUnload但不调用dealloc。没关系。您的按钮将被释放。来自视图控制器的引用都将通过viewDidUnload代码和addSubview中的引用(视图的默认dealloc代码(或默认代码中的某个位置))发布。
当您取消分配视图控制器时,如果有的话,两者都会被调用。 dealloc中的release语句将不起作用,因为它们将等同于[nil release],这是一个no-op。没关系。
我认为你可以从dealloc中取出release语句,因为如果没有首先调用viewDidUnload,dealloc就永远不会被调用。
答案 2 :(得分:0)
看起来你的UIView对象的保留计数为2。但这并不一定是坏事。由于内存不足,您的视图可能会出现变化,如果您不想在每次加载视图时重新创建UI元素,那么使用视图控制器是一件合理的事情。
通常,低内存条件会导致视图被卸载,因此将调用viewDidUnload。在大多数情况下,不会调用视图控制器的dealloc方法,除非以某种方式删除了对视图控制器的所有引用。
我通常让viewDidUnload基本上“撤消”viewDidLoad中发生的任何事情。由于出现内存不足的情况,视图将被卸载,并且在加载时完成的工作将被撤消。当视图重新加载时,它基本上会重做。你似乎在做的不仅仅是这里。您的viewDidUnload正在撤消在loadView中执行的操作。在这种情况下,我认为你没问题:当视图重新加载时,它应该(我认为)调用loadView。但作为一般规则,我喜欢匹配viewDidLoad和viewDidUnload。它使得与未直接实现loadView的视图控制器更加一致。
答案 3 :(得分:0)
我认为要问的问题是,是否会出现将UIView
个实例作为子视图删除但后来保存并重新添加为其他视图的子视图的情况?
由于代码的实现方式,似乎并非如此。如果这是真的,那么您的UIViewController
实际上不需要保留它创建的视图。它可以简单地将它们添加为子视图,然后忘记它们。您甚至不需要为它们提供字段/属性。你可以这样做:
- (void)loadView {
UIView *newView = [[UIView alloc] init];
//self.startButton and addSubivew retains the button obect; retain count = 2
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[newView addSubview:startButton]; //the button will be retained as long as it is part of 'newView'
//addSubview retains infoTextView; self.infoTextview retains; retain count: 2
UITextView* infoTextView = [[[UITextView alloc] initWithFrame:CGRectMake(0.0, 0.0, 280.0, 270.0)] autorelease];
[newView addSubview:infoTextView]; //the text-view will be retained as long as it is part of 'newView'
//View controller retains the view hierarchy
self.view = newView; //will be retained until we manually change the view or until we get unloaded/deallocated
[newView release];
}
- (void)viewDidLoad {
[super viewDidLoad];
[self loadView];
}
//could just remove this now
- (void)viewDidUnload {
[super viewDidUnload];
}
//could just remove this now
- (void)dealloc {
[super dealloc];
}
作为一般经验法则,您应该更喜欢能够满足您需求的最简单的解决方案。因此,如果您没有保留对以编程方式添加的子视图的任何明确引用,那么您应该这样做。
仅保留对您打算用于任务的事物的显式引用,而不仅仅是在将来的某个时刻向他们发送release
消息。如果这就是你所做的一切,那么你也可以立即发送release
,而不是首先保留引用。