转载

写代码千万不要滥用懒加载

  • 本文为CocoaChina网友xiubin0314投稿

写代码是一种习惯的养成,一种生活的态度。

有一次同事看着我写的代码说,你为什么要这么写啊?

我看了一下,原来是在 ViewController 和 Cell 里初始化视图,还有数据模型数组的时候,我都是用的懒加载(Lazy-Load)。

是啊,我为什么这么喜欢用懒加载来实例化一个属性呢?

以前学 iOS 开发的时候,

1. 觉得懒加载可以延迟加载,需要的时候才去加载数据;

2. 数组和字典等集合类型还可以防止为初始化或者使用中被置为 `nil`;

3. 类的属性多了这么写看着更舒服、清晰

 。。。

基本上每个属性我都希望去懒加载实现它,这会给我一种错觉:这样写更好,性能更高!

其实,这是一个不好的习惯,随着编程时间越发的长,越是觉得之前有些偏激。

iOS 中懒加载的写法一般为重写 `getter` 方法,判断属性是否为 `nil` ,是的话去初始化,否就直接返回:

``` objc
- (UIView *)layerView {
    if (_layerView) {
        _layerView = [UIView new];
    }
    return _layerView;
}
```

**但是我们有更清晰、简洁的写法**

一般来说,如果 `layerView` 是控制器的属性,我们一般都会在 `viewDidLoad` 方法中去加载视图;如果是一个视图,我们一般会在 `initWithFrame:` 加载子视图,我们只需要安安静静的用以下代码来初始化即可:

``` objc
 _layerView = [UIView new];
```

 根本无需使用懒加载,因为如果你不是一个人在开发的话,你*永远不会知道你的队友会在 `get` 方法里面做什么*。

 而且这样写更简洁,更清晰。当属性很多的时候也可以使用以下方式来初始化:

``` objc
self.layerView = ({
    [UIView new];
});
```

用懒加载至少六行代码,现在只需要一行或者三行就可以做到。

**我们不能使用懒加载来防止那些可能出现的错误**

很大一部分人用懒加载是为了保证数组和字典等集合类型在使用中永远不会是空值,这是错误的做法,因为可变集合类型被初始化之后,在正确的使用中如果不会被置 `nil`,那么也无需使用懒加载。如果因此而引发的问题,也可以帮我们提前找到原因。

**对于耗时或性能很大的操作,我们可以使用惰性计算而不是懒加载**

比如,我重构项目遇到的一个需求:请求股票列表返回的数据会告诉我总共会有上千条数据,并且不做分页,就是全部展示,滑到第几条就去请求第几条的数据。

上千条数据不做分页,我们也不可能全部请求回来,即便能全部请求回来也不可能在一个方法里去做这样的操作:

``` objc
NSMutableArray *dataArray = [NSMutableArray array];
for (int i = 0; i < 100000; ++i) {
  [dataArray addObject:data];
}
```

但是,对于 TableView 来讲,上千条数据,当然需要 Array 的 count 返回是一千。这个时候我们可以用[惰性计算](https://zh.wikipedia.org/zh-hant/%E6%83%B0%E6%80%A7%E6%B1%82%E5%80%BC)来解决这个问题:有多少条数据,我们就让数组返回的 count 是多少,但是只有真正的向数组取这个下标的对象的时候,我们才去处理!

那我们继承 NSArray 来写(真正的写一个 NSArray 还需要重写其他几个方法,在此不细说):

``` objc
typedef id(^HTLazyArrayItemBlock)(NSInteger index);
@interface HTLazyArray : NSArray
- (instancetype)initWithItemBlock:(HTLazyArrayItemBlock)block count:(NSInteger)count;
@end
#import "HTLazyArray.h"
@interface HTLazyArray()
@property (nonatomic, copy) HTLazyArrayItemBlock block;
@end
@implementation HTLazyArray {
    NSInteger _ct;
}
- (instancetype)initWithItemBlock:(HTLazyArrayItemBlock)block count:(NSInteger)count {
    if (self = [super init]) {
        _ct = count;
        self.block = block;
    }
    return self;
}
#pragma mark - override
- (NSUInteger)count {
    return _ct;
}
- (id)objectAtIndex:(NSUInteger)index {
    return self.block(index);
}
@end
```

我们初始化的时候传入一个 count,被 TableView 的代理方法访问的时候,有则返回数据模型,没有就先返回 `nil`,待到网络请求到数据再进行刷新。这样做的性能损耗微乎其微。

``` objc
self.lazyArray = [[HTLazyArray alloc]initWithItemBlock:^id(NSInteger index) {
       HTQuoteAHCellModel *model = weakSefl.cache[@(index)];
       return model;
   } count:dataTotalCount];
```

**那么,我们到底什么时候该用懒加载呢?**

懒加载的使用需要看具体的场景,比如一个很可能不会被使用的属性,使用懒加载确实可以避免无所谓的性能损耗;

还有就是 `null_resettable` 修饰的属性,该属性意为:setter nullable,但是 getter nonnull,典型的就是控制器的 `view` 属性:“你可以不要我,把我置空;但只要你需要我,我就是在的”。诸如此类都可以使用懒加载。

正文到此结束
Loading...