0%

OC懒加载与Swift懒加载

OC懒加载与Swift懒加载

OC懒加载

1
2
3
4
5
6
7
8
9
- (UIView *)redView
{
if (_redView == nil) {
_redView = [[UIView alloc] init];
_redView.frame = CGRectMake(0, 0, 100, 200);
_redView.backgroundColor = UIColor.redColor;
}
return _redView;
}

注意:在懒加载的实现中尽量避免懒加载循环.即A懒加载B,B又懒加载A.这样会导致互相调用而死循环或者偏离预期结果.

错误示例1:会导致调用死循环.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- (UIView *)redView
{
if (_redView == nil) {
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.blackView.frame.size.width + 100, 200)];
view.backgroundColor = UIColor.redColor;
_redView = view;
}
return _redView;
}

- (UIView *)blackView
{
if (_blackView == nil) {
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, self.redView.frame.size.height + 100)];
view.backgroundColor = UIColor.blackColor;
_blackView = view;
}
return _blackView;
}

错误示例2:示例2其实和示例1是一回事.也会导致调用死循环.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- (UIView *)redView
{
if (_redView == nil) {
_redView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.blackView.frame.size.width + 100, 200)];
_redView.backgroundColor = UIColor.redColor;
}
return _redView;
}

- (UIView *)blackView
{
if (_blackView == nil) {
_blackView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, self.redView.frame.size.height + 100)];
_blackView.backgroundColor = UIColor.blackColor;
}
return _blackView;
}

错误示例3:程序会在[self.view addSubview:self.redView];这一行崩溃:Thread 1: EXC_BAD_ACCESS (code=1, address=0x0)

原因在于:对于一个OC对象,alloc之后必须调用init方法完成初始化.对象只有在初始化完成之后才能继续使用.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

- (void)viewDidLoad {
[super viewDidLoad];

[self.view addSubview:self.redView];
[self.view addSubview:self.blackView];
}


- (UIView *)redView
{
if (_redView == nil) {
_redView = [UIView alloc];
_redView.frame = CGRectMake(0, 0, self.blackView.frame.size.width + 100, 200);
_redView.backgroundColor = UIColor.redColor;
}
return _redView;
}

- (UIView *)blackView
{
if (_blackView == nil) {
_blackView = [UIView alloc];
_blackView.frame = CGRectMake(0, 0, 200, self.redView.frame.size.height + 100);
_blackView.backgroundColor = UIColor.blackColor;
}
return _blackView;
}

错误示例4:程序虽然不会崩溃,但二者的frame会和你的预期不同,因此不建议这么做.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- (UIView *)redView
{
if (_redView == nil) {
_redView = [[UIView alloc] init];
_redView.frame = CGRectMake(0, 0, self.blackView.frame.size.width + 100, 200);
_redView.backgroundColor = UIColor.redColor;
}
return _redView;
}

- (UIView *)blackView
{
if (_blackView == nil) {
_blackView = [[UIView alloc] init];
_blackView.frame = CGRectMake(0, 0, 200, self.redView.frame.size.height + 100);
_blackView.backgroundColor = UIColor.blackColor;
}
return _blackView;
}

还有一种更隐秘的懒加载调用懒加载:子视图懒加载中使用了self.view,而self.view也是懒加载的.虽然在大多数情况下self.view会先于子视图加载,从而不会出现什么问题.但也有一些特殊情况下会出现子视图先于self.view访问.从而出现一些bug.

eg: 子视图redView使用了self.view的frame信息.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
NS_ASSUME_NONNULL_BEGIN

@interface XQViewController : UIViewController

@property (nonatomic, strong) UIView *redView;

@end

NS_ASSUME_NONNULL_END


@interface XQViewController ()

@end

@implementation XQViewController

- (void)viewDidLoad {
[super viewDidLoad];

self.view.backgroundColor = UIColor.whiteColor;

[self.view addSubview:self.redView];
NSLog(@"添加的view:%@", _redView);
}

- (UIView *)redView
{
if (_redView == nil) {
_redView = [[UIView alloc] initWithFrame:CGRectMake(0, 200, self.view.frame.size.width, 200)];
NSLog(@"%@", _redView);
}
return _redView;
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self dismissViewControllerAnimated:YES completion:nil];
}

@end

...
//在某个地方我们先访问了子控件,并调用了子控件的某个方法.
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
XQViewController *vc = [XQViewController.alloc init];
vc.redView.backgroundColor = UIColor.redColor;
[self presentViewController:vc animated:YES completion:^{

}];
}
...

打印如下:

1
2
3
2019-09-01 12:21:08.512351+0800 OCDemo[70734:2950590] <UIView: 0x7fd353f14610; frame = (0 200; 375 200); layer = <CALayer: 0x600002d6dae0>>
2019-09-01 12:21:08.512753+0800 OCDemo[70734:2950590] 添加的view:<UIView: 0x7fd353f14610; frame = (0 200; 375 200); layer = <CALayer: 0x600002d6dae0>>
2019-09-01 12:21:08.512946+0800 OCDemo[70734:2950590] <UIView: 0x7fd353f0a1f0; frame = (0 200; 375 200); layer = <CALayer: 0x600002d75960>>

这个时候self.redView和self.view上添加的redView其实是两个不同的对象.因此界面上并不会出现红色的视图.

解决办法有两种:

  1. 手动提前self.view的加载.如:[vc loadViewIfNeeded];
  2. 避免在子控件中使用self.view

Swift懒加载

1
2
3
4
5
lazy var blackView: UIView? = {
let view = UIView.init(frame: CGRect.init(x: 0, y: 300, width: 200, height: 100))
view.backgroundColor = .black
return view
}()

Swift的懒加载实现只会执行一次,如果在某个地方将blackView置为nil后,懒加载的实现不会被再次执行.blackView就一直是nil了.

同样:在懒加载的实现中尽量避免相互懒加载.

错误示例:死循环

1
2
3
4
5
6
7
8
9
10
11
lazy var redView: UIView = {
let view = UIView.init(frame: CGRect.init(x: 0, y: 0, width: self.blackView.frame.size.width + 100, height: 200))
view.backgroundColor = .red
return view
}()

lazy var blackView: UIView = {
let view = UIView.init(frame: CGRect.init(x: 0, y: 300, width: 200, height: self.redView.frame.size.height + 100))
view.backgroundColor = .black
return view
}()

总结

相同点:对象在真正使用时才会被初始化.

不同点:OC的懒加载实现可能会被多次调用.当属性的实例变量变为nil时会再次执行.而Swift的懒加载实现只会被执行一次,如果在某个地方将变量置为nil后,懒加载的实现不会被再次执行.变量就一直是nil了.

注意点:在懒加载的实现中尽量避免懒加载循环.即A懒加载B,B又懒加载A.这样会导致互相调用而死循环或者偏离预期结果.

觉得文章有帮助可以打赏一下哦!