Swift中的值类型和引用类型
在Swift中,初学者会混淆Class和Struct之间的区别以及何时实现而不是实现。 例如,这是每种类型的实现方式:
乍一看,这两种类型没有什么不同。 两者的声明方式相同,但其类型除外。 实际上, 类和结构是灵活的构造,它们成为代码中的构建块。 根据Apple文档:
您可以使用类和结构来定义要在代码中实现的自定义数据类型。 而且,它们的属性和方法也以完全相同的方式提供功能。 那么,什么使类和结构与众不同? 您什么时候使用另一个?
类和结构之间的主要区别在于它们自己的类型。 换句话说, 类是引用类型 , 结构是值类型 。 让我们进一步打破它们的定义:
通过作为引用类型, 类不保存该特定类的实际值或实际实例,而是保留对该特定实例的引用。 结果,您可以更改或变异先前声明为常量的特定实例上的存储属性或值,如以下示例所示:
在此,在将此类的实例创建并声明为常量之后,更改了其存储属性。 但是,该类的特定实例本身并未被突变, 只有引用存储属性才被突变。 不仅如此,一个类的几个即时变量或常量可以使用相同的引用:
换句话说,您还可以具有一个类型的多个实例,这些实例引用相同的现有实例或点引用,从而在某些情况下,在程序中使用类的选择不太安全。 可能危及代码安全性的另一个问题是,全局函数还可以更改存储的属性或类类型的特定实例的引用。 您可以在程序中的任何位置访问引用类实例及其存储的属性,这一事实使使用类成为程序的较不理想的选择。
另一方面, 结构或结构实例始终按值传递。 据说它们具有值类型并具有值语义。 这意味着此类型的每个实例本身就是特定的值。 因此,无法更改常量或其存储的属性,如以下示例所示:
在这里,程序在抱怨名为flare1的实例被声明为“ let”,它是一个常量,因此不能更改其存储的属性。 为了更改它,“ let”需要更改为“ var”以使其可变。 同样,没有全局函数可以更改结构的存储属性。 为此,需要在该结构的范围内声明一个变异函数,并将该结构的实例声明为“ var”变量。
同样,通过复制值而不是像类这样的引用来传递结构。 通过复制,将使用相同的值制作或“复制”该类型的新实例。 在下面的示例中,结构的两个实例变量具有可以具有相同的值,但是如果将变量值之一更改为另一个值,则另一个实例值不会更改,因为它的值已被实际复制:
结果,可以将结构视为更安全,因为除非声明为可变变量,否则实例的存储属性不会更改,它的方法需要声明为变异函数,而实例在传递给代码时按值复制。
在研究Swift时,您会发现许多基本数据类型(例如String,Array和Dictionary)都作为结构实现。 这意味着当将它们的数据分配给新的常量或变量,或者将其传递给函数或方法时,将复制它们。 因此,如果要对其进行突变,则需要将其实例声明为“ var”,如果要声明一个常量而不是对其进行突变,则需要将其实例声明为“ let”。 最好的习惯是养成在程序中计划代码以最好地声明实例类型的习惯。
除了类和结构之间在引用类型和值类型方面的差异之外,它们各自还具有更多差异。 例如:
类:1)可以从其他类继承,2)类型转换以解释实例,3)反初始化器使类的实例释放已分配的任何资源,4)引用计数允许对一个类实例进行多个引用。
结构:1)可以封装一些相对简单的数据值,2)在分配或传递该结构的实例时,将复制而不是引用封装的值,3)该结构存储的任何属性本身都是值类型, 4)该结构无需从另一个现有类型继承属性或行为。 5)提供成员级的初始化器或不需要声明的“免费”初始化器。
这意味着它们适合于各种任务的类和结构 。 在考虑项目或程序所需的数据结构和功能时,需要确定是将每个数据结构定义为类还是结构,并考虑最适合自己的目的。 最后,归结为它们的区别,即将类定义为引用类型 ,将结构定义为值类型 。
有关更多信息,请参阅Apple Swift 3.0文档:https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/ClassesAndStructures.html#//apple_ref/doc/uid/TP40014097- CH13-ID82