Swift中Structure和Class,我们应该pick哪一个?

Swift中Structure的概念与以往OC中的概念很不一样,Apple为Swift中的Structure新增了许多功能,比如可以封装函数,支持下标访问,支持协议,支持扩展等,这不仅使得Structure变得更加好用,而且也让Swift中的Structure与Class在功能上变得更加相似。那么在Swift编程中,我们应该选择Structure和Class中的哪一个来封装数据呢?本文接下来的内容将从底层类型和性能表现两个方面来对Swift中的Structure和Class进行比较,从而为这个问题寻找答案。

类型之战

Structure是值类型(Value Type),在对常量/变量赋值后,持有者所保存的是数据本身。
在进行值传递时,不论是传递给别的常量/变量,还是作为参数传递给某个函数,程序都是通过拷贝的方式将值(Value)传递过去。当接收方对这个值中的属性进行修改时,并不会影响传递方所持有的这个值,反之亦然。从程序表现的结果来看,在传递过后,传递方和接受方所持有的值是两个看起来一样但实际上相互独立的值,它们在拷贝过后就已经分道扬镳,变得毫无瓜葛。
Structure不支持继承,也做不到多态,但是它支持协议,在面向协议编程的理念下,Structure可以表现得很好。

Class是引用类型(Reference Type),在对常量/变量赋值后,持有者保存的是数据在内存的存储地址。
在进行值传递时,传递的是数据在内存的存储地址。当接收方对这个值中的属性进行修改时,会直接影响传递方所持有的这个值,反之亦然。在传递过后,传递方和接受方所指向的是同一个数据。所以Class支持对同一个特定数据添加多个不同的持有者。
Class支持继承,也可以实现魔法般的多态效果,同时它也支持协议,堪称全能选手。

Structure的优势
Structure采用拷贝的方式进行值传递,这样做可以帮助我们更加容易地对bug进行定位和分析。比如当某个Structure类型数据的值出现了异常,不符合原本的设计时,我们可以很容易地追踪到每一次对这个数据进行的修改,从而找到有问题的代码。
此外,Structure还有一个更加吸引人的闪光点,那就是它帮我们成功地避开了循环引用这个看不见的陷阱。使用Structure类型数据的时候,我们可以稍微放松神经,在block中放心大胆地调用外面的Structure类型数据,不再需要动不动就来一套先“弱引用”再“强引用”的组合拳来保护自己,吓退敌人。

Class的优势
尽管在使用Class时,我们要非常小心才可以避免触发“循环引用”这个烦人的陷阱,但对于有一定编程经验的开发者来说,“循环引用”已经不是一个很难应对的问题。相较于此,Class在一些特定场景中能为程序提供的支持,才是我们更需要关注的地方。这些场景包括读取文件,连接网络和连接数据库等,它们都要求在程序的不同位置对同一个特定数据进行操作,而这个要求是值类型所不能满足的,在这个方面,Structure无法取代Class。
此外,Class比Structure多了对继承和多态的支持,Class在我的印象里一直是一个品学兼优的好孩子,它伴随着“面向对象编程”而出现,在很长的一段时间里都是我封装数据时的首选,但是在如今“面向协议编程”发展地如火如荼的时代,我们进入了更加五彩斑斓的新的世界,“继承和多态”已经不再那么有魅力了。

结论
Structure的值类型和Class的引用类型各有千秋,Apple的建议是默认使用Structure来封装数据,当需要根据程序的不同操作状态和运行状态,对某个特定数据进行反复操作时,再使用Class类型,或者是需要跟Objective-C的API进行交互时,再使用Class类型。

性能之战

如果Structure类型数据存储在栈上,Class类型数据存储在堆上,那么在这场性能之战中Structure是毫无疑问的胜利者,因为相对于存储在栈上的数据,存储在堆上的数据,无论是在创建时,读取时,还是删除时都会更加耗时。

堆:创建数据时,系统需要在堆中找到足够大小的一块空闲区域分配给该数据,同时将该区域的地址存储在栈中;访问数据时,系统需要先去栈上读取地址,然后再去堆上寻找到该地址上的数据并将它读取出来;删除数据时系统需要先去栈上读取地址,然后再去堆上寻找到该地址上的数据并将它清除掉。
栈:创建/访问/删除数据时,系统直接在栈中操作即可,不需要再拐弯去堆中走一走。创建时不需要找寻空闲区域,访问/删除数据时不需要寻址操作。

但是Structure类型数据真的是存储在栈上的吗?答案是“不一定”。
Swift中的Structure不同于其他语言,它是支持协议的,对于遵从了协议的Structure类型数据,它将不再是从前那个普普通通的少年。Swift发明了一个新玩意儿 - Existential Container来存储遵从了协议的Structure类型数据。
Existential Container由三部分组成:三个字节的内存空间、协议查找表以及值查找表。当Structure封装的数据所需空间不超过三个字节时,它将被存储在栈上。当所需空间超过三个字节时,令人意外的事情就发生了,它将被存储在堆上,那么接下来的事情我们就可以想象了,它并不会比Class快了。
那么,没有遵从任何协议的Structure类型数据,是不是就可以在栈中自由穿梭,毫无压力了呢?答案同样是“不一定“,当Structure封装的数据需要很大的存储空间时,系统还是会安排在堆中,让它有个宽敞的地方可以好好发挥。

结论
单就性能方面而言,Structure和Class其实差别不大,并且随着设备的不断更新,设备的性能也在不断提升,Structure和Class在性能方面的差异也变得更小,封装数据时选哪一个都可以。

你可能感兴趣的:(Swift中Structure和Class,我们应该pick哪一个?)