【问题标题】:Programmatically-created subviews and viewDidUnload以编程方式创建的子视图和 viewDidUnload
【发布时间】:2011-09-20 12:54:19
【问题描述】:

对于视图控制器,您在 Interface Builder 中设置的任何出口都必须释放 并在 viewDidUnload 中设置为 nil,并且还必须在 dealloc 中释放。

(见:When should I release objects in viewDidUnload rather than in dealloc?

实现 [viewDidUnload] 的最重要原因之一是 UIViewController 子类通常还包含对视图层次结构中各种子视图的拥有引用。例如,这些属性可以在从 nib 加载时通过 IBOutlets 设置,或者在 loadView [强调添加] 内以编程方式设置。

我的问题是,我们真的需要为在 loadView 中以编程方式创建的视图层次结构中的子视图实现 viewDidUnload(没有 Interface Builder)吗?

【问题讨论】:

    标签: iphone objective-c ios memory-management


    【解决方案1】:

    这取决于您如何创建它们以及是否需要在其他地方引用它们。

    例如:

    - (void)loadView
    {
        [super loadView];
    
        UIButton *someButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
        someButton.frame = CGRectMake(0, 0, 50, 50);
        [self.view addSubview: someButton];
    }
    

    在上述情况下,您不需要实现 viewDidUnload,因为 someButton 是在 loadView 中自动释放的。

    另一个例子:

    - (void)loadView
    {
        [super loadView];
    
        self.someButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
        someButton.frame = CGRectMake(0, 0, 50, 50);
        [self.view addSubview: someButton];
    }
    

    在此示例中,您可能希望使用 viewDidUnload,因为您有另一个对 someButton 的引用。您希望 viewDidUnload 释放该按钮并重置引用,这样您就不会不正确地使用它并释放内存。在这种情况下,您还需要释放 dealloc 方法中的按钮,以防永远不会调用 viewDidUnload。

    【讨论】:

    • 完全正确!这个想法是你的视图控制器应该能够通过许多 loadView(+viewDidLoad)/viewDidUnload-Cycles 而不会泄漏内存或访问僵尸。最好的例子是标签栏控制器中的 VC - 它们被分配一个,然后在您单击标签时通过 loadView/viewDidUnload
    • 不应在自定义loadView方法中调用[super loadView]。如下代码: self.view = [[[UIView alloc]initWithFrame:[UIScreen mainScreen].applicationFrame]autorelease];
    • 想解释为什么五宝?我从来没有遇到过调用 [super loadView] 的问题。
    【解决方案2】:

    你当然应该。但是,如果您查看 Apple 示例,您会发现有时它们只使用 dealloc。如果您知道您的对象会在使用后的某个合理时间被释放,我认为这是完全合理的。但是,我遵循这种模式,因为在某些特殊情况下可能不会调用 viewDidUnload。我其实并没有把release方法叫这么长的名字:

    -(void)releaseRetainedXibAndViewDidLoadObjects
    {
        self.myLabel = nil;
        self.myImage = nil;
    }
    
    -(void)viewDidUnload
    {
        [super viewDidUnload];
        [self releaseRetainedXibAndViewDidLoadObjects];
    }
    
    -(void)dealloc
    {
        self.myObject = nil;
        [self releaseRetainedXibAndViewDidLoadObjects];
        [super dealloc];
    }
    

    您甚至可以从您的类子类化的应用程序特定视图控制器对象执行此操作,以简化问题。

    【讨论】:

    • viewDidUnloaddealloc* 中调用相同的“释放方法”是否明智?通常,我们将@property 设置为“(非原子,保留)”,因此为您创建的设置器释放当前对象,然后保留参数。也就是说,viewDidUnload 中的self.myProperty = nil; 执行类似于[myProperty release]; myProperty = [nil retain];... 然后,在dealloc 中,我们所做的就是[myProperty release]
    • 使用我的模式是非常安全的,并且在dealloc中不需要与在viewDidUnload中不同。对于一个保留的属性 self.property = nil 等价于 [property release], property = nil;尽管在 dealloc 中将其设置为 nil 并不重要,但如果您以后不小心尝试访问它,这仍然是一个好主意。您应该减少冗余和必须定义相同功能的位置。
    【解决方案3】:

    在您的 dealloc 方法中执行此操作。不保证会调用viewDidUnload。它通常仅在控制器需要卸载视图时调用,例如当有内存警告时。另一方面,init/dealloc 配对将始终被调用。

    【讨论】:

    • 我将在 dealloc 中发布我的子视图。我是否必须释放我的子视图并在 viewDidUnload 中将它们设置为 nil?
    • @Seth Lowenstein:这取决于。从文档中:“您应该只对以后可以轻松重新创建的对象执行此操作,无论是在您的 viewDidLoad 方法中还是从应用程序的其他部分。您不应该使用此方法来发布用户数据或任何其他不容易的信息重新创建”
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-14
    • 1970-01-01
    • 2012-10-21
    • 2019-10-10
    • 2017-05-01
    • 1970-01-01
    相关资源
    最近更新 更多