0、写在前面

Swift 中 struct、enum 都有了很大的变化,这边从基本概念开始分析,如值类型和引用类型的原理、内存管理机制等等来分析三者在 Swift 语言中的异同。

详解 Swift 中 class 和 struct

详解 Swift 中 class 和 struct - 敏捷大拇指 - 详解 Swift 中 class 和 struct





1、值类型和引用类型

首先提到的是 iOS 中的内存管理机制的问题,在 iOS 中内存对象主要有两类,值类型和引用类型。值类型就是 int、float、struct、enum、tuple 等基本数据类型,引用类型就是继承自 NSObject 的对象,通常是类对象,我们做内存管理的时候就是对这种对象进行管理。

为什么值类型不需要进行内存管理,而引用类型需要进行内存管理呢? 因为两种方式的内存分配机制不一样,值类型存储在栈当中,在内存中连续的内存空间,遵循后进先出(LIFO)的原则;而引用类型存储在堆当中,存储空间是随机的,对象之间留有空白空间,会产生空间碎片,需要手动进行管理。



1.1、有什么区别?

值类型基本特征就是复制数据,不管是在赋值、初始化还是参数传递的过程中,并且为这个数据创建独立的实例。

[Swift] 纯文本查看 复制代码
struct S {
  var value = 0
}
var a = S()
var b = a						// a被复制给了b
a.value = 1					// a改变了, b没有
println("\(a.value), \(b.value)")	// "0, 1"


引用类型在赋值、初始化还是参数传递的过程中其实创建了实例,但是实例的指针指向和原来实例同样的内存地址,所以说修改其中一个示例数据的时候,另一个实例数据也同样会被修改。

[Swift] 纯文本查看 复制代码
class C {
  var value = 0
}
var a = C()
var b = a				    // a被复制给了b
x.value = 1				  // a和b都被修改了
println("\(a.value), \(b.value)")	// "1, 1"




1.2、为什么选择值类型更安全?

选择值类型而不是引用类型的一个主要原因是能让你的代码变得更加简单。你在任何情况下用一个值类型,都能够假设你的其他代码不会使它改变,这通常在多线程环境中很有用,如果一个线程中使用的数据被另一个线程给意外的修改了,这通常会产生非常严重的Bug,且相当难以调试。在 Objective-C 中 NSArray、NSDictionary 都是属于 NS 类,而 Swift 中 Array、Dictionary 都是属于 struct 类型,显然在后者在多线程编程中更加安全。

在 iOS 中通常创建一个类,继承自 NSObject 的时候,这就已经是引用类型,我们发现在 Objective-C 中建立 Model 的时候选择的总是继承自 NSObject 的类,而在 Swift 中通常使用 struct 来进行 Model 来建立,重要的一点原因是因为 Swift 语言中,struct、enum 都支持了定义方法,极大的扩展了可用性,再加上值类型的安全性,成了 Model 最好的选择。




2、struct 和 class

我们接下来会详细分析 struct 和 class 的异同。



2.1、关于引用类型和继承关系

  • struct 是值引用,class 是类型引用;
  • struct 没有继承,class 有继承功能;


关于第一点的区别,上面已经详细讲过原理,不再赘述。第二点区别,struct 没有继承,这是因为 Swift 在本质上来说是面向协议(Protocol Oriented)的语言。



2.2、关于 immutable 变量

我们都知道在 Swift 语言中,通过 let 和 var 就能够来控制不可变和可变,struct 也是遵循该规则。

[Swift] 纯文本查看 复制代码
let s = S()
s.value = 1         // 该方法会抛出错误,Cannot assign to property: 's' is a 'let' constant


而在 class 中,let 规则是无法延续的。

[Swift] 纯文本查看 复制代码
let c = C()
c.value = 1         // 可以直接赋值,且不跑处错误




2.3、关于 mutating function

[Swift] 纯文本查看 复制代码
struct S {
  var value = 0
  mutating func change(_ value: Int){
    self.value = value
  }
}


[Swift] 纯文本查看 复制代码
class C {
  var value = 0
  mutating func change(_ value: Int){
    self.value = value
  }
}


我们发现两者的区别在于 struct 的方法中添加了 mutating 关键字,这是因为在 struct 中 property 是不允许在方法中修改的,如果需要在方法中修改就需要加上关键字。一般这种情况出现较少,最近在写栈数据结构的时候碰巧遇到了,比如实现栈的 pop、push 方法,就需要对栈的 property 做修改。




作者:Ivan