IOS8 UI: 由 self.view = nil 引起的思考

首先声明我使用了 ARC, 使用 OC 编码.


有2个 ViewController, 暂且称之为 AController 和 BController.

从 AController切换到(push) BController,  然后再从 BController 返回到 AController 中.


这是很基本的操作.


我手动的释放当前 AController 的 view, 在 AController 的 viewDidDisappear 方法中.

- (void)viewDidDisappear:(BOOL)animated
{
    [super viewDidDisappear:animated];
    [self dump:@"viewDidDisappear"];
    
    self.view = nil;
}

这个时候, 再从 BController 返回到 AController 中, AController 的 viewDidload 又被调用了一次.

如果去掉

self.view = nil;

从 BController 返回到 AController 中, AController 的 viewDidload 不会再次的被调用.


Apple 建议在

didReceiveMemoryWarning
方法中, 可以释放 view, 即

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
    
    [self dump:@"didReceiveMemoryWarning"];
    self.view = nil;
}
官方的文档: Resource Management in View Controllers

示例代码
- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Add code to clean up any of your own resources that are no longer necessary.
    if ([self.view window] == nil)
    {
        // Add code to preserve data stored in the views that might be
        // needed later.
 
        // Add code to clean up other strong references to the view in
        // the view hierarchy.
        self.view = nil;
}

那么, self.view = nil 究竟意味什么?看 stackoverflow 的一个回答.

About self.view = nil in ARC

解释一下这个问题.

提问者的原话是这样的

I'm just wondering about self.view = nil in ARC.

when set self.view = nil, self.view will be automatically released?
When set self.view = nil, all the subviews will be automatically nil and released?

第一个问题是, self.view = nil, 那么ViewController 的 view 是否可以自动的被释放?

第二个问题是, self.view = nil, 那么添加到 ViewController 的 view 的子视图是否可以被自动释放?

对于这这两个问题, 回答者是这样回答的, 当然也被最终采纳.

1) Yes, assuming no other strong references exist to it (this is a very big if, and this can come up in unexpected places). The only thing guaranteed to happen is that its retain count will decrease by 1.

2) If your view is set to be deallocated by meeting the above conditions, then the subviews will automatically be released (their retain count will be decremented by 1). Assuming no other references exist to them (see point 1) they will be deallocated as well.

Everything depends on the number of strong references to an object. It is absolutely critical to understand that. An object will be deallocated if and only if its retain count reaches zero. You don't directly control this as of ARC, but it is still very relevant.

大概意思解释一下.

1. 如果没有强引用self.view(strong)的话(其他对象对他引用计数+1), self.view = nil 就相当于是计数减一, 此时自动引用计数就是0, 也就是被 release 了.

如果有被强引用的话, self.view = nil 就是相当于计数减1, 并没有被 release.

2.  视图的子 views 没有被强引用的话, 也会计数-1, 最终计数是0, 也被 release. 否则也只是计数-1.


结合前面给大家的例子中, 可以得出一个总结:

self.view = nil, 被自动释放的话, viewDidload 会重新执行.


下面给出具体的例子, 便于大家更好地认识这个问题.

ViewController.h

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

@property (strong, nonatomic) UILabel *strongLabel;

@property (weak, nonatomic) UILabel *weakLabel;

@end

ViewController.m

#import "ViewController.h"
#import "MyViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)dump:(NSString *)msg
{
    NSLog(@"ViewController | %@", msg);
}

- (void)loadView {
    [super loadView];
    [self dump:@"loadView"];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    [self dump:@"viewDidLoad"];
    
    NSLog(@"----------------------------");
    NSLog(@"strong UILabel = %@", _strongLabel);
    NSLog(@"----------------------------");
    
    NSLog(@"----------------------------");
    NSLog(@"weak UILabel = %@", _weakLabel);
    NSLog(@"----------------------------");
    
    UILabel *localLabel = [[UILabel alloc] init];
    localLabel.frame = CGRectMake(100, 150, 200, 60);
    [self.view addSubview:localLabel];
    localLabel.backgroundColor = [UIColor redColor];
    localLabel.text = @"---ViewController";
    
    NSLog(@"local label = %@", localLabel);
    
    _weakLabel = localLabel;
    _strongLabel = localLabel;
    
    [self performSelector:@selector(enterOtherController) withObject:nil afterDelay:3];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
    
    [self dump:@"didReceiveMemoryWarning"];
    self.view = nil;
}

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    [self dump:@"viewDidAppear"];
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [self dump:@"viewWillAppear"];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    [self dump:@"viewWillDisappear"];
}

- (void)viewDidDisappear:(BOOL)animated
{
    [super viewDidDisappear:animated];
    [self dump:@"viewDidDisappear"];
    
    self.view = nil;
}

- (void)dealloc
{
    [self dump:@"dealloc"];
}

- (void)enterOtherController
{
    MyViewController *mv = [[MyViewController alloc] init];
    [self.navigationController pushViewController:mv animated:YES];
}

@end

第一次进入 ViewController, 可以看到
ViewController | loadView
ViewController | viewDidLoad
----------------------------
strong UILabel = (null)
----------------------------
----------------------------
weak UILabel = (null)
----------------------------
local label = <UILabel: 0x7ff1ba73c830; frame = (100 150; 200 60); text = '---ViewController'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x7ff1ba73bf00>>
ViewController | viewWillAppear
ViewController | viewDidAppear

然后, 从另外一个 Controller 返回到 ViewController 的时候, 可以看到

ViewController | loadView
ViewController | viewDidLoad
 ----------------------------
 strong UILabel = <UILabel: 0x7ff1ba73c830; frame = (100 150; 200 60); text = '---ViewController'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x7ffd31785b10>>
 ----------------------------
 ----------------------------
 weak UILabel = <UILabel: 0x7ff1ba73c830; frame = (100 150; 200 60); text = '---ViewController'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x7ffd31785b10>>
 ----------------------------
 local label = <UILabel: 0x7ffd31776e90; frame = (100 150; 200 60); text = '---ViewController'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x7ffd31771d10>>
 MyViewController | viewWillDisappear...
 ViewController | viewWillAppear
 MyViewController | viewDidDisappear...
 ViewController | viewDidAppear
对比你会发现, strongLabel 和 weakLabel 的地址和上次 localLabel 的地址是一致的, 这就说明, 之前的 localLabel 并没有随着 self.view = nil 被自动释放掉.

原因很简单, 使用了对 localLabel 的强引用. 假如你去掉ViewController 中的这句代码:

_strongLabel = localLabel;

再次返回的时候, localLabel 就被释放了, 当然 weakLabel 也是 nil.

也验证了 stackoverflow 上面那个问题.


这里也给大家提个醒:

weak
ARC引入, 类似于assign, 主要用于对象和基本数据类型间的赋值, assign 用于基本数据类型的赋值.
另外, weak 的对象消失后把指针置为nil,避免了野指.

strong
ARC引入, 等同于retain.

推荐: http://blog.csdn.net/ndscoahz/article/details/44647221


另外, ARC 不会去管理你的内存, 他只是在合适的地方自动给你 retain 或者 release.

所以, 内存还是需要你自己管理.



©️2020 CSDN 皮肤主题: 酷酷鲨 设计师:CSDN官方博客 返回首页