tom程序员iOS Developer

Swift 中变量的使用细节

2016-01-29  本文已影响676人  铜锣烧的梦想

变量(variable)是任何一门语言的构成基础, 对 Swift 也不例外. 大家可能会想, 这东西还有什么"细节"可言, 正常使用不就行了.
其实, 在 Swift 中, 变量使用起来还是有一些细节问题需要注意以及提防的, 作者就根据自己的实际经验总结了如下一些东西, 仅供大家参考.
由于这里探讨的是使用细节问题, 因此基本的变量声明、操作以及相关概念等此文便不再赘述.
本文将分成如下 3 部分进行说明.

1. stored variable 和 computed variable

2. Observer(观察器)

3. 懒加载(重点说明!!!)


1. stored variable 和 computed variable

Swift 中的变量分为 stored variable 和 computed variable.
stored variable 顾名思义, 就是我们一般意义上理解的可以进行赋值和取值的变量.

var number: Int = 10
上面的 number 就是一个 stored variable, 对其进行正常的读(取值)写(赋值)操作即可, 因为 stored variable 是可以存储值的.

computed variable, 字面意思为"计算型的变量", 虽然也叫"变量", 但要注意, 这个家伙是根本没法存储值的.
来看下面的例子, 这里定义了一个名为 number 的 computed variable:

var number: Int {
    get {
        return 10
    }
    set {
        print("number 被设置为了 \(newValue)")
    }
}

下面对 number 进行我们"想当然"的读写操作:

number = 100        // 输出结果为 " number 被设置为了 100 "
print(number)      // 输出结果为 " 10 "

What? 明明给 number 赋了新值 100, 并且输出结果也明确指出 number 被设置为了 100, 为什么再次打印 number 的值时, 却还是 10 ???

切记, computed variable 根本不会存储我们主观上认为的"值"!!! 它实质上是一些待执行的代码!!!
number 被赋值时, set 中的代码被执行. 同理, 当执行读取 number 的操作时, get 中的代码被执行.

再次重申, computed variable 只是一些待执行的代码!!!

在对 computer variable 赋值时, 我们能够取到赋给其的新值, 那就是 set 中的 newValue, 至于这个值要怎么用, 那就看实际的需要了. 但要注意, newValue 不会存储至 computed variable中! 毕竟, computed variable 只是一些待执行的代码! 重要的事情说三遍!!!

可能有人会想, 毕竟 computed variable 叫做"变量", 如果不能像 stored variable 那样进行很直观的读写操作, 总觉得很奇怪, 我下面给出了一种用 computed variable 实现 stored variable 效果的思路.

这里首先定义一个 PositiveNumber 类, 用来表示正数:

class PositiveNumber {
    private var _value: Double = 0.0
    var value: Double {
        get {
            return _value
        }
        set {
            _value = newValue
        }
    }
}

我为 PositiveNumber 类定义了两个属性, 分别为私有的store property _value 和可供外部访问的接口: 一个 computed property value.
是不是很面熟? 没错, 这跟 OC 中定义一个 @property 是完全相同的!

let number = PositiveNumber()
number.value = 10       // 利用 computed property 的 set 方法为 _value 赋值
print(number.value)   // 利用 computed property 的 get 方法读取 _value 的值

看到这里, 你可能会想, 如此大费周折, 只做了一件 stored property 很容易就能做到的事儿, 这不是闲的吗?

我们仔细思考一下上例, number 是一个 PositiveNumber 的实例, 在上述实现方式下, 我们可以为 number.value 赋任意值, 如: number.value = -10
这在逻辑上明显是不合适的, 因为 number 是一个"正数", 所以我们要对赋给 number.value 的值进行一定的处理. 这种逻辑交给 computed property 就再合适不过了.
修改上述代码如下:

class PositiveNumber {
    private var _value: Double = 0.0
    var value: Double {
        get {
            return _value
        }
        set {
            if (newValue <= 0) {
                print("赋值不合理!")
            } else {
                _value = newValue
            }
        }
    }
}

这样, 若再执行 number.value = -10 则会执行判断的逻辑, 从而使对 _value 的赋值操作更加严谨.

当然, computed property 的作用不仅如此, 下面说到懒加载时还会提到.


2. Observer(观察器)

Observer(即 willSet 和 didSet 方法) 是用在 stored variable 上的, 毕竟只有能存储值的东西才有被观察的价值!
基本的使用方法这里就不赘述了, 只对使用时的一些细节做一下说明.

参考下面的代码

class PositiveNumber {
    var value: Double {
        willSet {
            print("willSet 方法被调用")
            print("在 willSet 中, value = \(value), newValue = \(newValue)")
        }
        didSet {
            print("didSet 方法被调用")
            print("在 didSet 中, value = \(value), oldValue = \(oldValue)")
        }
    }
    
    init(value: Double) {
        self.value = value
    }
}

此处测试一下
let number = PositiveNumber(value: 10.0)
此时, 控制台未输出任何信息! 可见这个初始化的操作并没有调用观察器方法.

再来执行 number.value = 20

控制台输出信息
willSet 方法被调用
在 willSet 中, value = 10.0, newValue = 20.0
didSet 方法被调用
在 didSet 中, value = 20.0, oldValue = 10.0

由上例可以看到观察器调用时的一些细节, 大家在使用时注意一下就好.


3. 懒加载

对于开发者来说,懒加载最被人熟知的优点就在于只在需要某个 variable 时, 才去进行加载.其实, 懒加载还能处理一些普通的初始化方法处理不了的情况.
以初始化一个类的实例为例, 在普通的初始化方法中, 若该实例的初始化过程还未结束, 那么开发者是无法在该初始化方法中引用该实例的属性和方法的. 而懒加载则可以解决这个问题, 因为当需要懒加载某个属性时, 该实例变量已经初始化完毕, 因此开发者可以在懒加载的流程中去任意使用该实例的属性以及方法!

下面说明一下懒加载的使用细节.

虽然 Swift 已经提供给了我们一种超级给力的实现懒加载的方式, 即只要一句 lazy 就搞定了, 但上述最后一条细节还是留给了我们很多遗憾!

考虑这样一种需求: 某个实例属性一定要采用懒加载的方式, 而且要对其实现观察器的功能, 即可观测其值的变化并进行一定的逻辑处理.
再考虑另一种需求: 某个实例属性一定要采用懒加载的方式, 并且加载完成后是只读的, 不能对其进行修改.

乍一看, 这些需求明显是在给 Swift 原生的懒加载方式找茬嘛! 上述需求, 原生的 lazy var 一条都实现不了!!! 首先, 懒加载的实例属性本身就无法实现观察器方法, 同时 lazy var 这种形式的声明又导致该实例变量可以被修改, 此时会想, 如果有个 lazy let 就好了...

但是, 上述需求并不过分, 开发中也会碰到, 怎么办? 只能手写一个满足上述需求的懒加载了. 下面只提供了一种思路, 仅供大家参考

class PositiveNumber {
    private var token: dispatch_once_t = 0
    private var _value: Double = 0.0
    var value: Double {
        get {
            dispatch_once(&token) {
                // 一些非常耗时的初始化 _value 的流程
            }
            return _value
        }
        set {
            _value = value
        }
    }
}

其实仔细想想, 懒加载在某种程度上同 computed variable 是相同的, 即只有需要时才调用! 那么就可以利用 computed variable 的这些特点来人造一个懒加载方式.

上段代码采用了本文中第 1 点提到的方式, 实现了通过一个 computed variable 来操作一个 stored variable 的功能, 然后利用 GCD 的一次性代码实现了懒加载的"只加载一次"操作. 至此, 我们手写了一个与使用 lazy 来实现懒加载操作相同的方法.

但是, 要注意! 上段代码的灵活性更强一些, 包括:

上一篇 下一篇

猜你喜欢

热点阅读