ARC:什么时候是短暂的视图控制器委托内存回收弱引用

时间:2013-06-06 12:46:02

标签: objective-c automatic-ref-counting

这是来自previous one的后续问题,其中可能包含太多间接细节。请考虑以下代码:

BarViewController.h

#import <UIKit/UIKit.h>
@protocol SomeDelegate
- (void)someCallback; // doesn't matter
@end

@interface BarViewController : UIViewController

@property (weak, nonatomic) id <SomeDelegate> delegate;

@end

BarViewController.m

#import "BarViewController.h"

@interface BarViewController ()

@end

@implementation BarViewController

@end

FooViewController.h

#import <UIKit/UIKit.h>

@interface FooViewController : UIViewController

@end

FooViewController.m

#import "FooViewController.h"
#import "BarViewController.h"

@interface FooViewController () <SomeDelegate>
@end

@implementation FooViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    BarViewController *bar = [[BarViewController alloc] init];
    // does this assignment create a "strong" reference i.e. increase retain count by 1?
    bar.delegate = self;
    // *do some useful stuff with bar.delegate here* //
    bar = nil; // is memory for bar.delegate free'd here,
    // or only after this instance of FooViewController is destroyed?
}

#pragma mark - SomeDelegate
- (void)someCallback {
// doesn't matter
}

@end

想象一下FooViewController是某个小应用程序中的主视图控制器,BarViewController只是一些短暂的事情,可能是为了让用户选择几个按钮之一。 BarViewController报告通过其代表选择的内容。

在上面的代码中,当我在bar中将nil设置为FooViewController.m时,为bar.delegate预留的内存会发生什么变化?一方面,我认为将bar设置为nil可能会导致bar.delegate也被设置为nil,作为被释放的bar对象的一部分。另一方面,我对weak引用的理解是,只有在没有任何人强烈指向它时才释放内存。因此,如果bar.delegate = self创建了对delegate的强引用(是吗?),bar.delegate是否有可能仍以某种方式保存指向FooViewController实例的指针,即使{ {1}}现在是bar?这里是否存在内存泄漏的可能性?

修改
所以我正在考虑的方式是,nil强烈指向我们的UIWindow实例作为根视图控制器,而我们的BarViewController实例将弱点指向我们的{{1}实例作为其代表。因此,即使我们设置FooViewControllerFooViewController弱指向仍然至少有一个来自bar = nil的强指针的对象,因此bar.delegate无法自由基于“当没有其他人强烈指出它时,弱势财产被释放”的前提是什么?

2 个答案:

答案 0 :(得分:2)

除了这里,你的事情几乎是正确的:

  

另一方面,我对弱参考的理解是   内存只有在不再有人指向时才会释放   强烈的。因此,如果bar.delegate = self创建一个强引用   委派(是吗?)

声明weak的属性比您想象的要简单得多。它就像assign,除了弧生成的dealloc之外,它的类将它设置为nil。而且,bar.delegate = self;不会“创建对委托的强引用”,它只是指定一个指向Foo实例的指针,当Bar被释放时,该指针将被清除。

所以这就是发生的事情:

// no bar exists
BarViewController *bar = [[BarViewController alloc] init];

// now a bar exists with only a stack variable referring to it, it will be released
// by arc at the bottom of this method

// YOUR QUESTION: does this assignment create a "strong" reference i.e. increase retain count by 1
bar.delegate = self;

// Answer - No. No change in retain counts.  Bar now has an assigned pointer to the
// Foo instance (self)

// *do some useful stuff with bar.delegate here* //
bar = nil; // is memory for bar.delegate free'd here,

// bar is free'd here, and would have been whether or not you set it to nil
// moreover - bar's delegate is set to nil because it was declared weak.

Here's a decent Apple doc on the subject.

答案 1 :(得分:1)

- (void)viewDidLoad
{
    [super viewDidLoad];
    BarViewController *bar = [[BarViewController alloc] init];
    // does this assignment create a "strong" reference i.e. increase retain count by 1? 

不,您将委托设置为弱,因此在分配bar.delegate时,编译器不会强烈指向任何对象(在本例中为SELF)。

bar是一个局部变量,因此只要函数没有返回它就会自动保留BarViewController对象(当指定函数时将计数增加1并在函数结束时将其删除),或者只要你没有将它设置为NIL。

如果你没有指定委托变量为弱,那么是的,你将保留计数增加到SELF,你可能最终会有保留周期。这就是代表应该总是很弱的方式。

bar.delegate = self; 
//do some useful stuff with bar.delegate here
bar = nil; // is memory for bar.delegate free'd here,
// or only after this instance of FooViewController is destroyed?
}

在这种情况下,将bar设置为nil将释放内存中与变量bar相关的空间,这是唯一一个指向BarViewController对象的空间所以是的,之后发生BarViewController也将被释放,但请记住你不要不得不这样做,因为ARC会在函数结束时自动将局部变量设置为nil。

所有这些都在ARC下。同样,如果您在代码中执行代理指针较弱,这是一个很好的约定,以避免可能的保留周期。