惰性计算

惰性计算的前提:

    创建非常大的属性、或者创建对象的时候有一些必要的副作用不能提前创建之外,几乎不应该使用惰性求值来处理类似逻辑。

一种简单的惰性计算:

 - (id)someBigProperty
{
    if (_someBigProperty == nil) {
        NSMutableArray *someBigProperty = [NSMutableArray array];
        for (int i = 0; i < 100000; ++i) {
            [someBigProperty addObject:@(i)];
        }
        _someBigProperty = [someBigProperty copy];
    }

    return _someBigProperty;
}

除了

缺点:
  • 如果真的是很大的属性,一般它比较重要,几乎一定会被访问,所以加上这个不如直接在 init 的时候创建。
  • @property 的 atomic、nonatomic、copy、strong 等描述在有 getter 方法的属性上会失效,后人修改代码的时候可能只改了 @property 声明,并不会记得改 getter,于是隐患就这样埋下了。
  • 代码含有了隐私操作,尤其 getter 中再混杂了各种逻辑,使得程序出现问题非常不好排查。
  • 很多人的 getter 写得并不是完全标准,例如上述代码会导致多线程访问的时候,出现很多神奇的问题。
  • 代码多,本来代码只需要在init方法中创建用上一两行,结果用了至少 7 行的一个 getter 方法才能写出来。另外代码格式几乎完全一样,不符合 DRY 原则。
  • 性能损耗,对于属性取值可能会非常的频繁,如果所有的属性取值之前都经过一个if判断,这不是平白浪费的性能?
改进:

观察上面的代码,你会发现 _someBigProperty 是一个非常规则的 NSArray,它的 item 内容与下标相等。我们可以看出 item 的结果与 index 存在如下关系:

f(x) = x

那我们现在就基于NSArray这个类簇,实现一个特殊的类吧!
关于类簇,相信很多同学都有所了解,大概的说法是不可以直接继承一个NSArray、NSNumber、NSString这样的类。如果要继承需要实现全部的必要方法,在NSArray这个类簇来说,就是如下的方法:

@interface NSArray<__covariant ObjectType> : NSObject <NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration>

@property (readonly) NSUInteger count;
- (ObjectType)objectAtIndex:(NSUInteger)index;
- (instancetype)init NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithObjects:(const ObjectType [])objects count:(NSUInteger)cnt NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;

@end

当然除了NSArray类的基本方法,还有NSCopying、NSMutableCopying、NSSecureCoding这些协议需要实现,另外NSFastEnumberation协议已经默认实现完成,不需要额外处理。与惰性计算无关的细节大家可以自己填补,对于本例,我们只需要关心这几个方法的实现:

typedef id(^ItemBlock)(NSUInteger index);

@interface ZDynamicArray : NSArray

- (instancetype)initWithItemBlock:(ItemBlock)block count:(NSUInteger)cnt;
- (id)objectAtIndex:(NSUInteger)index;
- (NSUInteger)count;
@end

按照上文的说法,对于这样一个特殊的NSArray,我们真正要储存的数据只有一个 count 值外加一个函数,所以我们用这两个作为init参数。实现也很简单:

@interface ZDynamicArray()

@property (nonatomic, readonly) ItemBlock block;
@property (nonatomic, readonly) NSUInteger cnt;
@end

@implementation ZDynamicArray

- (instancetype)initWithItemBlock:(ItemBlock)block count:(NSUInteger)cnt
{
    if (self = [super init]) {
        _block = block;
        _cnt = cnt;
    }
    return self;
}

- (NSUInteger)count
{
    return self.cnt;
}

- (id)objectAtIndex:(NSUInteger)index
{
    if (self.block) {
        return self.block(index);
    } else {
        return nil;
    }
}

@end

瞧,就这么简单的写好了。让我们试一下吧!

ZDynamicArray *array = [[ZDynamicArray alloc] initWithItemBlock:^id(NSUInteger index) {
    return @(index);
} count:100000];

for (id v in array) {
    NSLog(@"%@", v);
}

NSLog(@"%@", array[15]);

一个看似 10w 数据的数组,其实占用空间微乎其微,但是作用和最开始那样的代码效果一样。很不错吧。大家也可以动手实践,写一些自己需要用到的惰性计算代码,例如一个Model的数组,并非所有的Model都需要用到,我们也可以做成这样的一个数组,等用到的时候再从NSDicitonary转换成Model。就像这样:

NSArray *downloadData = @[@{}, @{}, @{}, @{}];
NSArray *modelArray = [[ZDynamicArray alloc]initWithItemBlock:^id(NSUInteger index) {
    return [SomeModel modelFromDictionary:downloadData[index]];
} count:downloadData.count];
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值