一.懒加载的概念
我们使用的iOS设备其内存都有一定的限度,如果在程序启动时就将程序内的所有资源(数据,图片,视频)都加载完,那么有可能耗尽我的iOS设备的内存。
所以iOS出现了懒加载,懒加载也称延迟加载(比如控制器的view的创建),就是在开发中,当程序启动时不一次性加载所有的资源,而是在需要一些资源的时候程序才去加载这些资源(效率低,占用内存小),所谓的懒加载,其实就是对实例的getter 方法的重写。
用更清晰的说法懒加载就是,一个view 的子控件 ,只有当这个view被显示的时候才去加载。一个tableViewCell 中,给他设置了图片,他的contentView 里面才包含imageView 的图片,只有设置了textLabel 的内容,才会加载这个textLabel 。
二.懒加载的使用
@property (nonatomic, strong) NSMutableArray *dataArray;
@property (nonatomic, strong) UILabel *titleLabel;
至于为什么必须使用strong的原因,strong会使修饰对象的引用计数加一,而weak不会,如果一个对象的引用计数为0的话系统就会自动将其销毁。如果你想让一个控件的生命周期随着你的控制器被销毁才去释放,那就使用strong ;如果你仅仅是想让它在被移除视图之后就被销毁,那就使用weak 。当你使用懒加载将实例变量初始化后若使用weak修饰并且没有在其getter 方法中添加到视图上时,即使你懒加载写的再好,它所返回的都是nil,即你的对象被销毁了,你的懒加载就如同空有一样。
- 因为懒加载就是重写这个属性对应的
getter 方法,所以我们将要实现的逻辑放在getter 中。
-(NSMutableArray *)dataArray {
if (_dataArray == nil) {
_dataArray = [[NSMutableArray alloc] init];
}
return _dataArray;
}
-(UILabel *)titleLabel {
if (!titleLabel) {
_titleLabel = [[UILabel alloc] init];
_titleLabel.textAlignment = NSTextAlignmentCenter;
_titleLabel.textColor = [UIColor blueColor];
_titleLabel.text = @"lazyLoad";
[self.view addSubview:_titleLabel];
}
return _titleLabel;
}
必须完全按照这种格式,因为如果将括号中任意一个_titleLabel 换做self.titleLabel ,所谓的点语法,就相当于调这个方法,在外部调用点语法时就形成无限循环,具体原因看下面注意事项的说明。
- 懒加载的使用
调用的时候要用自己重写的getter 方法。
- (void)viewDidLoad {
[super viewDidLoad];
[self titleLabel];
}
- (UILabel*)titleLabel {
if (!_titleLabel) {
_titleLabel = [[UILabel alloc] init];
_titleLabel.textAlignment = NSTextAlignmentCenter;
_titleLabel.textColor = [UIColor blueColor];
_titleLabel.text = @"lazyLoad";
_titleLabel.frame = CGRectMake(100, 100, 100, 30);
[self.view sizeToFit];
[self.view addSubview:_titleLabel];
}
return _titleLabel;
}
或者
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addSubview:self.titleLabel];
}
- (UILabel*)titleLabel {
if (!_titleLabel) {
_titleLabel = [[UILabel alloc] init];
_titleLabel.textAlignment = NSTextAlignmentCenter;
_titleLabel.textColor = [UIColor blueColor];
_titleLabel.text = @"lazyLoad";
_titleLabel.frame = CGRectMake(100, 100, 100, 30);
[self.view sizeToFit];
}
return _titleLabel;
}
三.懒加载的注意事项
注意一:指针循环引用问题
if (_dataArray == nil) 不可以写成 if (self.dataArray == nil) ,不然会造成循环引用指针
return _dataArray 不可以写成return self.dataArray 不然会形成循环引用
_titleLabel 为成员变量,是由属性附加生成的成员变量,而self.titleLabel 则为调用属性titleLabel 的点语法,在没有重写titleLabel 的点语法之前两者返回的是同一个东西,例如:
当调用titleLabel 的setter 方法调用的是下面的方法:
- (void)settitleLabel:(UILabel *)titleLabel{
_titleLabel = titleLabel;
}
再调用titleLabel 的点语法self.titleLabel 调用的是下面这个方法:
- (UILabel *)titleLabel{
return _titleLabel;
}
这就使调用self.titleLabel 和直接调用_titleLabel 的结果是相同的。 重写之后self.titleLabel 返回的是懒加载初始化后的对象,而直接调用_titleLabel 则返回的是未初始化的对象,所以我们在使用懒加载时一定要注意自己的书写,防止出现循环引用的问题。
注意二:关于布局的问题
如果用Masonry 布局页面的话,关于frame 的代码一定不能放在重写的getter 方法里,不然会报找不到父视图的错误,要放在addSubview 后面。 Masonry 是一种非常流行的第三方布局框架,它是一个轻量级的布局框架,拥有自己的描述语法,采用更优雅的链式语法封装自动布局,简洁明了,并具有高可读性,而且同时支持 iOS 和 Max OS X。 想要详细了解Masonry 可以看看这两篇文章:iOS自动布局——Masonry详解和iOS自动布局框架-Masonry详解
注意三:不会出现死循环的方法:
当然,还有另外一种只要你记住, 就随便你怎么写,并且万无一失、永远也不会造成死循环的方法,那就是懒加载的方法名不要和懒加载内部的实例变量同名,这样Xcode 就可以准确识别出你调用的是属性还是懒加载方法,比如:
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) UILabel *label;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self lazyMethod];
}
- (UILabel *)lazyMethod {
if (self.titleLabel) {
_titleLabel = [[UILabel alloc] init];
_titleLabel.textAlignment = NSTextAlignmentCenter;
_titleLabel.textColor = [UIColor blueColor];
_titleLabel.text = @"lazyLoad";
[self.view addSubview:_titleLabel];
}
return self.titleLabel;
}
@end
四.懒加载的优点
从上述的代码可以看出,懒加载具有一下优点:
- 如果代码量不多,可读性略强。
- 防止了实例为 nil 的情况,减少了后续使用程序时对实例为 nil 的检查,即不用担心实例为 nil 的状况导致程序崩溃。
- 适当使用懒加载可以节省内存资源。
- 每个属性的 getter 方法中分别负责各自的实例化方法,代码彼此之间的独立性强,降低耦合性。
- 一定程度上节省了某一期间内的时间。
- 得当使用可以优化程序性能,提高用户体验。
五.懒加载的缺点
每个方法的出现都不会完美,有优点就有缺点,从上述的程序我们难想出懒加载相应的缺点:
- 使用泛滥会导致可读性变差,每使用一次懒加载,代码就会增加几行,那么当你程序使用大量的懒加载它的代码量就会比普通方法的代码量多,看起来反而更加的繁琐,可读性自然也就没有那么强了。
- 使用不当会导致死循环,导致crash,程序无法分清调用的是 getter 方法还是实例变量,会造成死循环。
- 代码量增加(每增加一个懒加载,代码会平均多出3-4行)。
|