iOS值类型和引用类型的区别

本文主要引自huxinguang002写的Swift 值类型和引用类型以及# Yarn_
写的【Swift学习】值类型、引用类型 & 堆、栈

一个是值类型(Value types) 一个是引用类型(Reference types)。

  • 引用类型(Reference types): 每个实例共享一份数据来源,一般以类(class)的形式出现。
    创建一个Person类,里面有一个name属性:
    class Person {
        var name: String
        init(name: String) {
            self.name = name
        }
    }
    var var1 = Person(name: "John")

此时,var1在内存中是这样的:


图1.png

创建一个新的变量var2让它等于var1:

var var2 = var1

此时,var1 和 var2 指向了内存中的同一个地方:


图2.jpg

上文我们说class是引用类型,顾名思义,"引用"就是C语言中的指针,指针是一个地址,指向某个内存,因此类的变量实际上是存储了实例的地址,当我们声明一个新的变量var2 = var1时,并没有创建一个新的实例,而是把var1实例的地址赋值给var2,于是就有了图2。
知道了类实例的存储机制,下面这种现象就非常容易理解了:

var1.name = "Jane"

print(var1.name)   // Jane
print(var2.name)   //Jane

我们只改变了变量var1的name属性,发现var2的name属性也发生了变化。原因非常明显,因为var1和var2是共享同一个实例,当使用变量var1修改了其所指向的实例之后,var2对应的实例自然也就被修改了。

  • 值类型(Value types): 每个实例都保留一份独有的数据拷贝,一般以结构体 (struct) 、 枚举(enum) 或者元组(tuple)的形式出现。
    同样的,我们以struct为例来说明值类型:
    创建一个Person结构体,里面有一个name属性:
struct Person {
    var name: String
}
var var1 = Person(name: "John")

此时,var1中存储的不是指向内存的指针,而是值:


图3.png

创建一个新的实例var2,并赋值为var1:

var var2 = var1

此时,系统将会创建一个新的实例并将var1中的值拷贝一份,如下图所示:


图4.png

因此,当修改var1中的属性时,var2的值是不会发生改变的:

    var1.name = "Jane"
    print(var1.name)   //Jane
    print(var2.name)   //John

可以看到var2还是保持原来的值,因为在执行var2 = var1时,系统拷贝了一个新的实例给var2,那么var1与var2之间是不会相互影响的。
值类型和引用类型有各自的优缺点,我们应该根据具体的需求来选择。

  • 堆(heap) & 栈(stack)

堆和栈是内存中不同的分区,它们与引用类型和值类型有很多的联系。

  • 栈(Stack):
    当我们创建一个值类型,如结构体,系统将其存储在一个被称为栈的内存区域中。系统使用栈来存储在紧急线程上的任何东西,是由CPU直接管理和优化的。当一个函数声明一个变量,变量将存储在栈中,当函数调用完毕后栈会自动释放该变量。因此栈是非常易于管理的、有效的,由于是CPU直接控制,速度非常快。
  • 堆(Heap):
    当我们创建了一个引用类型,如类,系统将把类实例存储在一个被称为堆的内存区域中。系统使用堆来存储其他对象引用的数据。堆是一个大的内存池,系统可以从该池中请求并动态分配内存块。堆不会像栈一样自动释放对象,需要额外的工作来完成。这使得在堆中创建和删除数据比栈慢。
    下图展示了类的实例在堆和栈中的存储:


    图5.png

    当我们创建一个类的实例时,系统会在堆中申请一个内存块用于存储实例本身,如图中右边Heap中的两个实例。同时将把存储该实例的变量和堆中的地址存储在栈中,如图的左边所示。而当创建一个结构体时,将会把变量和值都存储在栈中。

类和结构体定义语法

struct Location {
    let x: Double
    let y: Double
 
    func distance() -> Double {
        return sqrt((x * x) + (y * y))
    }
}
 
class Person {
    var firstName: String
    var lastName: String
 
    init(firstName: String, lastName: String){
        self.firstName = firstName
        self.lastName = lastName
    }
 
    func fullName() -> String{
        return "\(firstName) \(lastName)"
    }
}

此处分别定义了一个Location结构体以及一个Person类,可以看到它们除了关键字的差别,其他的定义方式基本是一样的。结构体和类都可以有属性和方法。

  • 构造器(initializers)

可以看到上文代码比较明显的差别就是:class含有一个init(::)方法,而struct没有。因为所有结构体会自动生成一个成员逐一构造器,用于初始化新结构体实例中的成员属性,因此我们可以直接声明一个实例并设置其各个属性的初始值:

let location = Location(x: 2, y: 4)

而类是没有默认的成员逐一构造器的,因此,若删除class中的init(::)方法,Xcode将会报错:"Class Person has no initailizers",即Person类中没有构造器。因此我们在创建类的时候,必须创建一个及以上的构造器。当然我们也可以对构造器进行重载(overload),系统将会根据参数的类型和数据自动地调用对应的构造器。当在结构体中手动添加了init(::)方法后,默认的成员逐一构造器将会失效。

结构体与类的异同

类和结构体有很多共同点。共同处在于:

  • 定义属性用于存储值
  • 定义方法用于提供功能
  • 定义下标操作使得可以通过下标语法来访问实例所包含的值
  • 定义构造器用于生成初始化值
  • 通过扩展以增加默认实现的功能
  • 实现协议以提供某种标准功能

与结构体相比,类还有如下的附加功能:

  • 继承
  • 类型转换:运行时检查和解释一个类实例的类型
  • 析构器:释放一个实例的所有资源
  • 引用计数

类和结构体在内存中的实现机制的不同:

  • 类存储在堆(heap)中,结构体存储在栈(stack)中
  • 类是引用类型,而结构体是值类型

类和结构体的使用场景

按照通用的准则,当符合一条或多条以下条件时,请考虑构建结构体:

  • 该数据结构的主要用来封装少量相关简单数据值。
  • 希望数据结构的实例被赋值给另一个实例时是拷贝而不是引用,封装的数据及其中存储的值也是拷贝而不是引用。
  • 该数据结构不需要使用继承。

举例来说,以下情境中适合使用结构体:

  • 几何形状的大小,封装一个 width 属性和 height 属性,两者均为 Double 类型。
  • 三维坐标系内一点,封装 x , y 和 z 属性,三者均为 Double 类型。

类似于上述情形,我们将使用结构体,在其他多数案例中,我们将定义类,生成它的实例,并通过引用来管理和传递。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,185评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,652评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,524评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,339评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,387评论 6 391
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,287评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,130评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,985评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,420评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,617评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,779评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,477评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,088评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,716评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,857评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,876评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,700评论 2 354

推荐阅读更多精彩内容