许多iPhone代码示例(来自Apple等)都包含以下代码:
- (void)viewDidLoad {
CGRect applicationFrame = [[UIScreen mainScreen] applicationFrame];
// add the top-most parent view
UIView *contentView = [[UIView alloc] initWithFrame:applicationFrame];
contentView.backgroundColor = [UIColor blackColor];
self.view = contentView;
[contentView release];
levelView = [[LevelView alloc] initWithFrame:applicationFrame viewController:self];
[self.view addSubview:levelView];
calibrationView = [[CalibrationView alloc] initWithFrame:applicationFrame viewController:self];
}
此片段来自BubbleLevel示例项目。
我的问题:为什么发布消息发送到contentView?我们在self.view中保留对contentView的引用,我们显然希望在应用程序的生命周期中使用它,而不仅仅是在此方法中。不会调用release会导致视图被释放吗?
答案 0 :(得分:13)
Ölbaum说self.view = contentView;
将保留您的观点是正确的,这意味着它不会被取消分配。但是,这种情况并非总是如此:
如果您查看UIViewController的文档(或标题),您会看到view
属性声明如下:
@property(nonatomic, retain) UIView *view;
这意味着只要设置了视图属性(foo.view = anotherView
或[foo setView:anotherView];
),就会保留其他视图。如果view
属性声明为:
@property(nonatomic, assign) UIView *view;
然后视图将不保留。它会将一个简单的指针指定到实例变量中。在这种情况下,您认为后续的release
到contentView
会破坏视图是正确的,这意味着控制器会有一个陈旧的指针,这意味着您的应用可能会崩溃。
换句话说,当您使用属性时,请确保知道它们是retain
还是assign
。 (如果没有说,那就是assign
)。然后相应地处理你的内存管理。
答案 1 :(得分:8)
不,因为self.view = contentView
会保留它。由于您必须平衡保留和释放呼叫,因此您需要释放它以平衡分配。
答案 2 :(得分:6)
不要认为retain
/ release
只是为了平衡。他们所做的是转让所有权。理解所有权的概念,并了解Cocoa中的内存管理。
所有权在范围内设定;方法范围,对象实例范围或全局应用程序范围。方法拥有通过保留局部变量。对象实例拥有具有保留的实例变量。并且应用程序拥有,具有保留的全局变量。
如果您从名为alloc
的方法调用中收到该对象,或者包含单词new或copy (如newSprocket
或mutableCopy
中所述),则当前方法拥有该对象< / em>的。通过其他方式提供给您的所有对象都不归您所有,例如方法的参数或大多数方法调用的结果。
您可以访问您不拥有的对象实例,您可以一直这样做。如果您希望对象实例超过当前方法的执行(或者如果您想要挑剔地运行循环),则只需要明确取得retain
的所有权。
这行代码取得了从局部变量contentView
引用的对象实例的所有权。方法loadView
现在拥有该对象。
UIView *contentView = [[UIView alloc] initWithFrame:applicationFrame];
接下来,对象实例self获取所有权,因为该属性被声明为保留。对象实例现在有两个所有者,包括方法和类。
self.view = contentView;
从此开始,我们永远不会通过此方法中的局部变量访问视图对象。该方法不再需要它,因此重新拥有它。现在,该对象仅由对象实例拥有,并且不再是此方法的问题。
[contentView release];
答案 3 :(得分:5)
如果你已经习惯了其他语言,可能很自然地看self.view = contentView
并想一想,“哦,它正在分配一个实例变量。”但它不是 - 实例变量不能用点分配,因为Objective-C中的对象总是指针。这是一个属性访问者。在大多数情况下,它完全等同于编写[self setView:contentView]
。
在大多数情况下,setter方法将实现如下:
- (void)setView:(UIView *)view {
if (view != _view) {
[_view release]; // where _view is the name of the actual instance variable
_view = [view retain];
}
}
在这种情况下,设置属性时会保留它。所以你必须在那里发布它,否则分配对象造成的初始所有权永远不会被释放。