Swift:什么时候使用结构体和类

发布于 2015 年 8 月 14 日

世界上对swift持续不断的讨论话题中有一个就是什么时候使用结构体什么时候使用类。我想我今天要贡献一些自己的想法。

值 VS 引用

答案其实很简单:当你需要值语义(所谓值语义是一个对象被系统标准的复制方式复制后,与被复制的对象之间毫无关系,可以彼此独立改变互不影响)的时候使用结构体,当你需要引用语义(所谓值语义是一个对象被系统标准的复制方式复制后,与被复制的对象之间毫无关系,可以彼此独立改变互不影响)的时候使用类。就是那样!

欢迎下周再来。。。

等等! 怎么了?

这没有回答这个问题 什么意思?就是这样的啊!

是的,但是。。。 但是什么?

什么是值语义和引用语义呢? 哦,这个啊。也许我接下来应该讲讲这个。

并且他们怎么和结构体和类相关联的呢? 好的。

所有的问题都归结于数据和数据被存储在什么地方。我们通常将数据存在在局部变量、参数、属性以及全局变量中。从根本上说有两种不同的方法将数据存储在所有这些地方。

值语义中,数据直接存在于被存储的位置。引用语义中,数据存在于别的地方,而存储的位置中存储着一个对数据的引用。当你获取数据的时候这种差别可能不那么明显。而当你拷贝那块存储区域时这个不同就会显现出来。值语义中,你会获取一份新的数据的拷贝,而引用语义下,你会获取一份新的对同样的数据的引用的拷贝。

这真的很抽象。让我们来看一个例子,暂时把swift的这个问题从你脑海中移除,让我们来看一个OC的例子:

@interface SomeClass : NSObject@property int number;@end@implementation SomeClass@endstruct SomeStruct {int number;};SomeClass *reference = [[SomeClass alloc] init];reference.number = 42;SomeClass *reference2 = reference;reference.number = 43;NSLog(@”The number in reference2 is %d”, reference2.number);struct SomeStruct value = {};value.number = 42;struct SomeStruct value2 = value;value.number = 43;NSLog(@”The number in value2 is %d”, value2.number);

打印结果:

The number in reference2 is 43The number in value2 is 42

为什么会有这样的差异呢?

代码 SomeClass *reference = [[SomeClass alloc] init]在内存中创建了一个新的SomeClass类型的实例,然后在变量中赋值对那个实例的引用。代码reference2 = reference在一个新的变量中赋值了对同一个对象的引用。现在两个变量都指向了同一个对象,而reference.number = 43修改了存储在那个对象中的number属性的值。所以当打印对象的number属性值时,结果是43。

代码 struct SomeStruct value = {}创建了SomeStruct的一个实例并赋值给变量。代码value2 = value在第二个变量中拷贝了这个实例的副本。每个变量包含了一块独立的数据。代码value.number = 43 只修改了value变量中的数据,然后当打印value2的number时结果仍然是42。

这个例子对应下面Swift的举例:

class SomeClass {var number: Int = 0}struct SomeStruct {var number: Int = 0}var reference = SomeClass()reference.number = 42var reference2 = referencereference.number = 43print(“The number in reference2 is \(reference2.number)”)var value = SomeStruct().number = 43print(“The number in value2 is \(value2.number)”)

和之前的打印结果一样:

The number in reference2 is 43The number in value2 is 42值类型的体验

值类型不是一个新的概念,但是对于很多人来说他们觉得这是新的。为什么呢?

结构体在大多数OC代码中不是很常用。我们通常以CGRect或者CGPoint以及其他类似结构的形式接触他们,但是一般不会创建我们自己的结构体。原因之一是,他们不是那么的实用.想要用OC语言将一个对象的引用正确地存储在一个结构体中真的是一件很困难的事情,尤其是在使用APC的情况下。

很多其他的语言根本没有类似struct这样的类型。很多认为“一切皆对象”的语言如Python、JavaScript等也都只有引用类型。如果你是从那样的语言转而学习Swift的,这个概念对你来说可能会更陌生。

但是别急!有这么一个区域几乎所有的语言都使用值类型:数字!下面的例子连刚开始编程几周的程序员都不会觉得陌生,忽略掉语言:

var x = 42var x2 = xx++print(“x=\(x) x2=\(x2)”)// prints: x=43 x2=42

这对我们来说是那么的明显和自然以至于我们根本没有觉察到他表现得有些不同,但是它就那样展现在我们面前。只要你在编程你就在跟值类型打交道,即使你没有意识到!

很多语言实际上把数字实现为引用类型,因为他们坚持“一切皆对象”的哲学。不管怎样,他们是不可变类型,而值类型和不可变引用类型之间的区别很难察觉。他们表现得跟值类型很像,即使他们不是像值类型那样实现的。

这是关于理解值类型和引用类型的相当大的一部分内容。就语言的语义来说,只有在数据被改变的时候他们的差异会有影响。但是如果你的数据是不可变的,那么值类型和引用类型的差别就不存在了,至少问题就转向性能而不是语法了。

这甚至出现在了OC中的标记指针(tagged pointers)中。就像标记指针中那样,一个对象存储在一个指针的值中,这是个值类型。拷贝存储区域就拷贝了对象。这个差别不明显,因为OC库很小心地只在不可变类型中加入了标记指针。一些NSNumbers对象是引用类型,另外一些则是值类型,但是这并没有什么差别。

做出选择

现在我们知道值类型是怎么工作的了,你怎么选择你自己的数据类型呢?

从根本上讲这两者的区别就是当你在他们身上使用等号的时候发生了什么。值类型被拷贝,而引用类型有了另外一个引用。

因此当决定使用哪种数据类型时根本上要问的问题就是:拷贝这个类型有意义吗?你想方便地使用拷贝操作并且会频繁使用吗?

一个人去旅行,而且是去故乡的山水间徜徉。

Swift:什么时候使用结构体和类

相关文章:

你感兴趣的文章:

标签云: