UIViewController的presentModalViewController:animated:方法会保留模态控制器吗?

11

这对我与我的模态控制器交互的方式有影响。当我刚开始进行iOS开发时,我假设UIViewController不会保留以模态呈现的视图。实际上,更确切地说是我没有理由认为它会保留它们。这使得我在知道它们会完成消失动画时尝试释放它们变得相当笨拙:

_myViewController = [[UIViewController alloc] init];
[self. present modalViewController:_myViewController animated:YES];
/* 
Some stuff, then in a different method all together,
probably as the result of a delegate callback or something...
*/
[self dismissModalViewControllerAnimiated:YES];
[_myViewController performSelector:@selector(release) withObject:nil afterDelay:0.5f];

然后,我看到了UIViewControllermodalViewController属性,并想到,“嗯,我希望在呈现模态视图控制器时它能够保留这个属性。”果然,我尝试记录了几次保留计数并注意到在调用presentModalViewController:animated:后立即增加了一般(我知道,保留计数并不是完全可靠的度量)。所以,在某个地方,我开始使用一种更好的模式,即假设任何我以模态方式呈现的控制器对象都是由呈现控制器保留的。这使我可以编写标准的呈现代码:

UIViewController* myViewController = [[UIViewController alloc] init];
[self presentModalViewController:myViewController animated:YES];
[myViewController release]; // <- Fire and forget!
现在,当然没有任何尴尬:无需等待动画完成,甚至不需要保留对呈现的控制器的引用(如果我不需要它)。我可以以后盲目地解除它,而不必担心泄漏。我喜欢它。 我已经记录了许多在我的模态呈现的控制器中的dealloc,它们总是在我想要的时候被调用,这让我对自己的方法感到自信:UIViewControllerpresentModalViewController:animated: 方法会将呈现的控制器作为 modalViewController 属性进行保留。 但是,问题的关键是,我意识到我不能将此作为文档记录的行为来确认。如果没有文档记录,我就不应该像现在这样感觉非常安全,因为苹果不会对未记录的行为的持续性做出承诺。 modalViewController属性公开是只读的,因此我只能假设后台对其进行保留,并且关于presentModalViewController:animated: 的文档仅说明:

将 modalViewController 属性设置为指定的视图控制器。

“设置”可能是 assignretain。我没有找到确切证据来证实或否认我的观点。由于这是我经常做出的假设,因此我真的希望有人能够指出我在文档的深处错过了什么事实,以使我对这种做法的合法性感到安心。 编辑: 在 iOS SDK 的日常生活中,我发现自己在 UIViewController 的头文件中并开始阅读其中的一些内容。我获得了一些有用的信息,提醒我起笔写下这个问题。在某些未来的用户偶然发现这个问题并想要尽可能多地获取信息以满足他们对非常标准的做法的担忧时,我决定发布它。这个小片段很简单,来自 UIViewController.h 中的 @interface ivar 块:
UIViewController *_childModalViewController;

与其他声明不同的是:

UIViewController *_parentViewController; // Nonretained
NSHashTable      *_childViewControllers; // Nonretained

评论似乎明确说明了什么不被保留。由于在模态视图控制器 ivar 声明上没有评论,因此似乎它是被保留的。


这里有同样的问题。是否有人可以帮忙? - Di Wu
5个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
14
Objective-C的内存管理规则定义了行为,因此不需要明确说明它会保留模态视图控制器。如果一个对象在方法执行完后需要保留一个传递过来的对象,除非另有说明,否则它将保留该对象。 因此,在这种情况下,您只需将视图控制器传递给presentModalViewController:animated:,然后释放它(或使用autorelease)。 这适用于Objective-C中的任何地方。如果一个对象将另一个对象作为方法输入,则您永远不必代表其保留该对象。 根据评论中的要求,如果您阅读苹果公司的内存管理文档,则会找到关于弱引用的部分,其中指出:

重要:在Cocoa中,对 表数据源、大纲视图 项目、通知观察者和 代表都被认为是弱的(例如,NSTableView对象 不保留其数据源,而NSApplication对象不保留 它的代表)。文档仅 描述这个约定的例外。

这实际上说明了这是一个惯例,文档中将会说明异常情况,然而,前往NSTableView的文档并查看setDataSource:方法,我们可以看到:

讨论 在托管内存环境中,接收者对数据源保持弱引用(即不保留数据源,请参见与对象通信)。在设置数据源后,此方法调用tile。

如果anObject没有响应numberOfRowsInTableView:或tableView:objectValueForTableColumn:row:,则此方法会引发NSInternalInconsistencyException。


委托/弱引用存储方法怎么样?它们将对象作为方法输入,保留它们,并且您必须代表它们保留对象。如果您可以向我展示文档中明确说明所有弱引用存储方法都将声明缺少保留的位置,那我会很高兴。 - Matt Wilding
你的第一个引用似乎建立了一组具体实例,其中弱引用是默认的,而强引用将被标记为异常。它并没有告诉我强引用是默认的,而弱引用会被标记为异常,这是你在这个声明中所暗示的: "如果一个对象需要在方法执行完毕后保留传递的对象,则除非另有规定,否则它将保留该对象。" - Matt Wilding
另外,我意识到我在这里非常苛刻。感谢您的想法,并且我同意您的看法,在惯例方面它确实是很有道理的。 - Matt Wilding

2
嗯,好问题!我不完全确定您的假设。所讨论的属性在文档中被定义为:
@property(nonatomic, readonly) UIViewController *modalViewController
正如您所指出的,它没有指定如何存储。 这个文档: http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocProperties.html 实际上指出,如果没有指定retain/assign/copy,那么assign是默认行为。
我也读了d11wtq的回复,但不确定我完全同意。我同意关于Obj-C内存管理的惯例,但有很多情况下框架方法不会保留属性(考虑弱引用、委托等)。希望有人能确认你的假设。

我同意你关于弱引用的观点。理论上,我可以看到这两种方式的可能性,这就是为什么我希望有一个明确的结论,这样我会感觉更加安心。 - Matt Wilding
如果一个对象不保留另一个对象,但需要它保持存在,则应将其行为记录在文档中。显然,对于第三方框架,我无法保证这一点,因为框架的开发人员需要记录它,但在苹果产品范围内,100%的规则例外应该被记录在文档中。 - d11wtq
1
你提供的那个文档是在讨论声明属性,与这个问题无关。用户询问的是 presentModalViewController:animated: 方法。那只是一个方法...不是一个已声明的属性(顺便说一下,如果你认为是我给它点了踩,那不是我;)对于只读属性,属性上的保留/分配操作只适用于设置器,没有任何作用。 - d11wtq

2

当然有文档记录这种方法。顺便说一句,我也不知道其他方法:D 我从1.5年前开始iOS编程时就使用了分配-展示-释放的方法。

如果您启动Xcode并创建“实用程序应用程序”,则可以找到示例代码。在(由苹果提供的)代码中应该包含:

FlipsideViewController *controller = [[FlipsideViewController alloc] initWithNibName:@"FlipsideView" bundle:nil];
controller.delegate = self;

controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:controller animated:YES];

[controller release];

1

我猜iPortable的回答给了你想要的 - 当你以那种方式释放视图控制器时,让你心里踏实。

顺便说一下,打印retainCount不是一个好的调试策略的部分原因是因为你放NSLog或断点的位置可能不是查看retain count的正确位置。一些保留或释放可能会在你检查其保留计数之后发生,这是由你感兴趣的事件引起的。

因此,如果你真的想知道自事件以来保留计数会是多少,只需重写-(id) release和-(void) release方法并在那里打印日志并计算。当然,你必须在你的方法末尾调用超类的retain/release。例如,通过这种方式,你可以清楚地回答你最初的问题。


0

如果您在呈现模态视图控制器时进行动画

[viewController presentViewController:modalViewController animated:YES completion:^{
    NSLog(@"presentViewController completion");
}];
/*DON'T RELEASE IF THERE IS ANIMATION*/
//[modalViewController release];

释放对象并不是一个好主意。相反,应该在调用类中放置一个回调函数,从而可以释放该类。

如果您没有使用动画,那么您可以在模态显示后立即释放它,如下所示。

   [viewController presentViewController:modalViewController animated:NO completion:^{
    NSLog(@"presentViewController completion");
}];

[modalViewController release];/*YOU MAY RELEASE MODAL VIEW CONTROLLER IMMEDIATELY*/

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,