- [值类型和引用类型]
- [什么是值类型(value type)和引用类型(reference type)]
- [两者有什么不同]
- [请在保证安全的情况下修改变量值]
- [如何选择]
- [c++类型行为的不一致性]
- [ 参考文章]
变量类型可以帮助开发者理清程序的数据流,以写出更健壮的代码。本文通过对swift语言中的两种变量类型的介绍,来说明哪些情况下适合使用value/reference type。阅读本文不需要对swift有深入了解,当然熟悉swift编程对理解本文会有帮助。
什么是值类型(value type)和引用类型(reference type)
在swift中,一个类型要么是value type, 要么是reference type。value type指每一个变量在内存中都有单独的一份数据拷贝。reference type指数据在内存中只有一份拷贝,多个变量共享它的值。和c++中类型的使用方式不同,swift中类型的属性(value/reference)是由类型本身决定的,作为语言标准给出,稍后本文会再做介绍。
比如c++中,
#include <iostream>
int main() {
int a = 1;
int &b = a; //c++中的引用可以使用在所有的类型上
b = 2;
printf("a = %d, b = %d\n", a, b); //输出结果为:a = 2, b = 2
}
两者有什么不同
辨别value type,最基本和最有效的方法是观察它的拷贝行为--赋值、初始化、参数传递等带来的影响,即通过内存数据拷贝 创建 一个独立的实例(instance)。
// Value type example
struct S { var data: Int = -1 }
var a = S()
var b = a // a is copied to b
a.data = 42 // Changes a, not b
println("\(a.data), \(b.data)") // prints "42, -1"
拷贝引用则不同,它的行为类似于创建了一个指针,原来在内存中的数据依然保持一份拷贝。因而改变其中某一个变量的值也会影响其他变量,即产生副作用。
// Reference type example
class C { var data: Int = -1 }
var x = C() // 建立一个对象的引用x
var y = x // 建立对象的另一个引用y
x.data = 42 // changes the instance referred to by x (and y)
println("\(x.data), \(y.data)") // prints "42, 42"
可以看出这个例子和之前c++引用产生了同样的效果。
请在保证安全的情况下修改变量值
开发软件,其中最重要的要求(之一)是程序的正确性,一种保证是来自于编程语言本身,另一种当然是来自程序员。那么程序员在写代码的过程中如何选择使用的类型呢?value type和reference type相比较,选择使用value type可以使你的程序直观上更加符合逻辑。如果所有的变量在内存中都有自己单独的一块区域,你就大可不必担心程序的其他部分在无意中修改这些变量的值。这在多线程环境中尤其重要,因为不同的线程会同时修改变量的值。如果变量是reference类型就十分危险,这使你的程序出现不可预期的结果,调试起来也非常困难。
更理想的一种情况是,你声明和使用的变量是只读(不可写)的,就不会出现数据不一致的情况发生。实际上,在没有变量值改动的程序中,变量是type的或者reference的对程序的行为没有任何影响。这时候reference内存占用少,当然是优先使用reference。
如何选择
使用value type,当:
- 你想要变量之间的赋值隐含数据拷贝,使变量之间的状态完全独立。
- 代码中的数据操作分布在多个进程。
使用reference type,当:
- 创建共享和允许修改的状态。
很多人其实有一种对编程语言的误解,认为在语言层面约束越少的语言越好,这样就可以很容易实现想要的功能。但是灵活性带来的坏处更加容易被人忽视。交给程序员处理的事情越多,导致写出来的代码更加容易产生bug,代码可阅读性大大降低,同时增加重构的难度。
而swift在这方面给出了比较严格的限制,在swift中,struct
, Array
, String
, 和 Dictionary
都是value type,它们的行为就像c语言中的int
,你几乎不用做任何额外的工作,比如为了防止代码的其余部分对数据进行修改而显式地为变量分配额外的内存空间,这些繁琐的过程都会因为swift中变量的value type的属性,而交给编译器去做。更加重要的是,你可以很放心的将变量的数据拷贝交给其他进程进行操作而不用担心同步问题。这些语言层面的约束都是为了程序员可以写出可预测的代码。作为对比,下文介绍c++是怎么做的。
c++类型行为的不一致性
碰巧在微信中看到的一篇文章:C++ 之 stl::string 写时拷贝导致的问题。c++在语言标准上允许不同的数据拷贝机制,联系到本文的概念,即c++中的类型是value type还是reference type取决于编译器的实现。当语言标准容忍实现的灵活性时,虽然多数时候是为了优化性能,但是带来的问题似乎更加严重--导致程序的运行结果不同于预期。
网友评论