Swift 3.0 从 ++ 的实现到 inout 和 defer 的小细节

本文是一个在 Swift 3.0 中自加和自减的实现
项目在我的「Playground」中开源

引言

Swift 3.0 中删去了原 C Style 的自加和自减写法,转而推荐使用+=-=写法。有时我们会在数组下标处直接修改某些计数值,而+=写法是个表达式,本身是不返回值的,因此无法代替原来的功能。

本文将通过重载运算符来找回自加和自减,顺便提及一下inoutdefer的小知识。

正文

自加和自减分别包含了两个运算符,共四个运算符。即:++前置/后置、–前置/后置。

前置运算符很简单,因为是先自加,再返回自加后的值:

测试时直接前置自加是可以实现功能的,这里会打印出2,即数组第二个元素:

但后置运算符不同,是先使用本身的值,使用完以后再进行自加,因此它似乎应该长这样:

但事实上,可以使用defer关键字推迟加法运算,可以写成这样:

defer代码块会被压入栈中,待函数结束时弹出栈运行。

代码看上去怪怪的,这样真的没问题吗?deferreturn哪个会被先执行呢?x 是inout的,不论是哪个先被执行,结果返回的不都是应该返回已经 +1 的 x 吗?

其实这里包含了两个问题,一是inout究竟怎样工作的,二是defer究竟是怎样工作的。

inout

inout在写法上与C语言传递地址的写法十分类似,在调用函数传入参数是带有前缀&,就好像取地址传进去了一样,实则不然。

Swift 中 struct 是值类型的。

何为值,值是不能改变的,是immutable的,任何对值的修改其实就是新构造了一个来替换原来的。这里的inout也是如此,并不是传了地址进来,而是在这里构造了一个新的结构体,当函数结束时会复制回去替换原来的结构体。

当然,这个复制并不一定会真的复制。Swift 的copy on write也会分情况,当值类型的引用只有一个时是不会复制的,这段在猫神的书里有提到。

defer

这里defer代码块会被压入栈中,函数结束时执行。到底啥时候执行呢?是在return后执行,如果return调用了其他函数,这个函数会在defer代码块执行之前被执行。这个简单测试一下即可,也可以直接看汇编,上面代码的汇编长这样:

回到上面自加的代码中。

这里defer代码块会被压入栈中,return 时还没有被执行,因此这里的值是正是我们想要的自加以前的值。按照inout的原理,这个 x 指向的不是函数外面的 x,这只是个临时变量,在defer代码块执时值被修改了也不会影响返回值,因为至始至终都是值,不是引用,在函数结束之后会把这个临时变量拷贝回去,改变函数外的 x 值。

因此整个流程都是在操作和复制值,都是immutable的,不存在互相影响的可能,因此这段代码能像想象中那样工作。

按照这个思路完成自减代码即可。完整代码在我的「Playground」中开源。几句简单的代码基本能够代替C语言的自加自减功能了。

Swift 中测试结果为:

11538901-7f0005d81542500d

对于最后一个比较复杂的表达式,可以看出 Swift 跟C语言一样也是从右往左计算的,计算结果跟C语言中的是一致的:

12538901-9a60fae4cd1a121f

结语

其实 Swift 3.0 去除自加和自减也是有原因的,在非必要的情况下还是尽量使用推荐的写法为好,这习惯确实在对代码可读性没什么好处。养成良好编码习惯从我做起,这段代码也仅供特殊情况下使用。

1 收藏 评论

相关文章

可能感兴趣的话题



直接登录
跳到底部
返回顶部