Swift中结构体-Struct和类-Class的区别

今天这篇文章要來介紹 Swift 中 struct 和 class 有什麼不一样的地方

讨论Struct与Class之前,我们先来看一个概念:Value Type(值类型),Reference Type(引用类型):

  • 值类型的变量直接包含他们的数据,对于值类型都有他们自己的数据副本,因此对一个变量操作不可能影响另一个变量;

  • 引用类型的变量存储对他们的数据引用,因此后者称为对象,因此对一个变量操作可能影响另一个变量所引用的对象。

这就是深拷贝与浅拷贝,深拷贝就是内容拷贝,浅拷贝就是指针拷贝。两者的区别为:

  1. 是否开启新的内存地址

  2. 是否影响内存地址的引用计数

讨论Struct与Class之前,我们先做好准备工作,首先分别创建一个Struct:SNode 与 Class:CNode

struct SNode {
    var Data: Int?
}
class CNode {
    var Data: Int?
}

下面我们通过代码来理解两者都有哪些异同:

1.变量初始化的不同

//struct默认就有两种初始化方法 
let snode1 = SNode() 
//struct可直接在构造函数中初始化变量
let snode2 = SNode(Data: 4) 

// class默认只有一种初始化方法,不可直接在构造函数中初始化变量 
let cnode = CNode()  

他们初始化时的主要的差別为: class 在初始化时不能直接把 property 放在 默认的constructor 的参数里,如果想在init时直接给class的变量赋值需要自己创建一个带参数的constructor

class CNode {
    var Data: Int?

    init(data: Int) {
        self.Data = data
    }
}
//此时可以在初始化class时给变量赋值
let cnode = CNode.init(data: 5)

2.immutable 变量

Swift 语言的特色之一就是可变动内容和不可变内容用 var 和 let 來甄别,如果初始为let的变量再去修改会发生编译错误。

 struct也遵循这一特性
 class不存在这样的问题

//struct 这样写会造成错误 
let snode = SNode() 
snode.Data = 500 
//必须将let改为var

//class可以直接更改
let cnode = CNode.init()
cnode.Data = 500 

3.变量赋值方式不同(深浅copy)

// struct
let snode = SNode(Data: 4)
var snode1 = snode
snode1.Data = 5
print("snode.data:\(String(describing: snode.Data))\n snode1.data:\(String(describing: snode1.Data))")


// class
let cnode = CNode()
cnode.Data = 5
let cnode1 = cnode
cnode1.Data = 6
print("cnode.data:\(String(describing: cnode.Data))\n cnode1.data:\(String(describing: cnode.Data))")

打印结果:

 snode.data:Optional(4)
 snode1.data:Optional(5)
 cnode.data:Optional(6)
 cnode1.data:Optional(6)
说明:
以上是 value type 和 reference type 最大的不同,value type 赋值时是开辟新的内存地址,并将当前的內容复制, reference type 会保有一份内存地址重复利用。
即:class 赋值的时候,不会copy一份完整的内容给另一個变量,只是增加了原变量内存地址的引用而已,没有开辟新的内存地址。
struct 赋值的时候,会开辟新的内存地址并将当前的內容复制。

4.mutating function

在不改变原有代码的前提下,为 struct 和 class 新增一个名为changeData()的方法。

extension SNode {
    mutating func changeData(vaule: Int) {
        self.Data = vaule
    }
}

extension CNode {
    func changeData(vaule: Int) {
        self.Data = vaule
    }
}

都是使用extension来新增方法,struct 和 class 的差別是 struct 的 function 要去改变 property 的值的时候要加上 mutating,而 class 不用。

5.继承
struct不能继承,class可以继承。

6.struct比class更“轻量级”
struct分配在栈中,class分配在堆中。

TIPS:

Swift 把 Struct 作为数据模型的注意事项:

  • 优点:

1、安全性:

因为 Struct 是用值类型传递的,它们没有引用计数。

2、内存:

由于他们没有引用数,他们不会因为循环引用导致内存泄漏。

3、速度:
值类型通常来说是以栈的形式分配的,而不是用堆。因此他们比 Class 要快很多!

这里解释下为什么访问struct比class快?
“堆”和“栈”并不是数据结构上的Heap跟Stack,而是程序运行中的不同内存空间。栈是程序启动的时候,系统事先分配的,使用过程中,系统不干预;堆是用的时候才向系统申请的,用完了需要交还,这个申请和交还的过程开销相对就比较大了。

栈是编译时分配空间,而堆是动态分配(运行时分配空间),所以栈的速度快。
从两方面来考虑:

  1.分配和释放:堆在分配和释放时都要调用函数(MALLOC,FREE),比如分配时会到堆空间去寻找足够大小的空间(因为多次分配释放后会造成空洞),这些都会花费一定的时间,而栈却不需要这些。

  2.访问时间:访问堆的一个具体单元,需要两次访问内存,第一次得取得指针,第二次才是真正得数据,而栈只需访问一次。

(http://stackoverflow.com/a/24243626/596821)

4、拷贝:
Objective-C 里拷贝一个对象,你必须选用正确的拷贝类型(深拷贝、浅拷贝),而值类型的拷贝则非常轻松!

5、线程安全
值类型是自动线程安全的。无论你从哪个线程去访问你的 Struct ,都非常简单。

  • 缺点:

1、Objective-C不能直接调用

当你的项目的代码是 Swift 和 Objective-C 混合开发时,你会发现在 Objective-C 的代码里无法调用 Swift 的 Struct。因为要在 Objective-C 里调用 Swift 代码的话,对象需要继承于 NSObject。
Struct 不是 Objective-C 的好朋友。

2、继承
Struct 不能相互继承。

3、NSUserDefaults
Struct 不能被序列化成 NSData 对象。

所以:如果模型较小,并且无需继承、无需储存到 NSUserDefault 或者无需 Objective-C 使用时,建议使用 Struct。

原文地址 :
(https://www.cnblogs.com/beckwang0912/p/8508299.html)

猜你喜欢

转载自blog.csdn.net/philm_ios/article/details/81078682