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其实是两个不同的对象.因此界面上并不会出现红色的视图.
解决办法有两种:
- 手动提前self.view的加载.如:
[vc loadViewIfNeeded];
- 避免在子控件中使用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.这样会导致互相调用而死循环或者偏离预期结果.