自己实现swift lazy关键字效果

作者: 微微笑的蜗牛 | 来源:发表于2015-12-26 16:47 被阅读816次

Lazy

lazy关键字的作用是在第一次使用属性的时候才去生成,而不是在一开始就初始化好,按需使用。
当计算属性的值比较耗时,或者需要外部值的依赖时,用lazy比较合适。

struct Animal {
    lazy var name: String = {
        // ...
        return ...
    }()
}

lazy必须配合var使用,因为let需要有个初始值。

当使用lazy属性时,struct对象必须声明为var,因为在访问属性时会改变该对象。如果像下面这样,会报错。

let animal = Animal()
print(animal.name)
// error: Cannot use mutating getter on immutable value: 'animal' is a 'let' constant.

global和static修饰的属性都是lazy的。

自己实现lazy

知道了lazy代表的意思,我们可以自己实现lazy。主要思路就是判断该值是否计算过,如果没有,就计算,返回结果,如果已经计算过了,就直接返回结果。下面将会用到enum关联值和简单的模式匹配。

定义

首先来定义enum

private enum LazyValue<T> {
    case NotComputed(() -> T)
    case Computed(T)
}

如果未计算过,就用func计算,并且将result关联到Computed(T)中。

class LazyBox<T> {
    init(computation: () -> T) {
        _value = .NotComputed(computation)
    }

    private var _value: LazyValue<T>

    var value: T {
        switch self._value {
        case .NotComputed(let computation):
            let result = computation()
            self._value = .Computed(result)
            return result
        case .Computed(let result):
            return result
        }
    }
}

这里利用了一个中间值_value来存储其状态,当要真正获取value的时候,判断_value关联属性,然后进行处理。

Test
var counter = 0
let box = LazyBox<Int> {
    counter++
    return counter * 10
}

assert(box.value == 10)
//assertion failed
assert(box.value == 20)
assert(counter == 1)

LazyBox所关联的函数只会执行一次,所以assert(box.value == 20)
不成立。

小优化

每次取值都是box.value,显得有些不太方便,所以我们再提供一个直接属性来获取value。

struct Animal {
    private let _name = LazyBox<String> {
        return "animal"
    }
    
    var name: String {
        return _name.value
    }
}

let animal = Animal()
animal.name
并发

如果在多线程情况下,在计算value时是不安全的。有可能出现计算多次的情况。为了解决这个问题,可以用同步队列,一次只能一个线程访问,保证只会计算一次。

class LazyBox<T> {
    
    private var _value: LazyValue<T>
    
    private let queue = dispatch_queue_create("LazyBox", DISPATCH_QUEUE_SERIAL)
   
    init(computation: () -> T) {
        _value = .NotComputed(computation)
    }
    
    var value: T {
        var returnValue: T? = nil

        dispatch_sync(queue) {
            switch self._value {
                
            case .NotComputed(let computation):
                
                let result = computation()
                self._value = .Computed(result)
                returnValue = result
                
            case .Computed(let result):
                returnValue = result
            }
        }
        
        return returnValue!
    }
}

这可能会有点性能下降,因为在读取的时候同样也是在同步队列中读取,但是影响不会很大。

苹果文档中也说道,lazy进行计算时是线程不安全的。

Note: If a property marked with the lazy modifier is accessed by multiple threads simultaneously and the property has not yet been initialized, there is no guarantee that the property will be initialized only once.

相关文章

  • 自己实现swift lazy关键字效果

    Lazy lazy关键字的作用是在第一次使用属性的时候才去生成,而不是在一开始就初始化好,按需使用。当计算属性的值...

  • swift3.0 - 懒加载

    和OC不同的是swift有专门的关键字来实现懒加载 lazy关键字可以用于定义某一个属性懒加载 格式: lazy ...

  • Swift: lazy 属性的写法

    序言:OC中有懒加载,Swift中用lazy关键字声明属性,也可以实现懒加载。lazy所修饰的属性只有第一次访问时...

  • Swift-懒加载

    在OC中,一般是通过重写getter方法来实现,但是在swift中有专门的关键字lazy来实现某一个属性实现懒加载...

  • swift学习之懒加载

    在移动端开发过程之中,懒加载随处可见。在swift中,苹果推荐使用懒加载,如何实现懒加载呢? 关键字: lazy ...

  • swift学习09(懒加载)

    swift中是通过专门的关键字(lazy)来实现懒加载; 一、直接懒加载赋值 二、懒加载赋一个函数 三、懒加载赋一...

  • lazy

    swift中是通过专门的关键字(lazy)来实现懒加载; 直接懒加载赋值 懒加载赋一个函数 懒加载赋一个闭包 1、...

  • Swfit 懒加载、闭包

    Swift有懒加载这么一说,而且Apple 很推荐我们使用懒加载, 并且还有一个关键字lazy懒加载定义: var...

  • 【kotlin】委托

    在 kotlin 开发中,会遇到懒加载的情形:使用 by lazy 关键字。而这是通过委托来实现的。Kotlin ...

  • slider 设置滑过的和未滑的的图片

    实现效果: 主要代码: swift: 具体实现: //// ViewController.m// Test//...

网友评论

    本文标题:自己实现swift lazy关键字效果

    本文链接:https://www.haomeiwen.com/subject/jmkbhttx.html