iOS Swift5:浅析结构体(struct)与类(class)

一、前言

关于 struct 与 class,相信大家或多或少有些了解,本篇的目的是让大家完全透彻的熟悉,不在是片面了解。

二、基础

2.1、起源

大家学过 C 语言,也应该学过 C++,先帮大家从底层先梳理一下基础知识:

  • struct 与 class 的出现
    • C 语言中是有 struct 结构体的,但是 C 语言是没有 class 的;
    • C++,扩展了 struct,并有了 class 的出现;
  • struct 与 class 的定义
    • C 语言中,struct 只是数据结构,没有函数;
    • C++ 中,struct 与 class 类似,可以有变量,可以有函数;

我们不谈 C 语言,因为它没有 class,struct 也功能单一。

2.2、C++ 中两者的区别

C++ 中 struct 与 class 的区别:

  • 成员:class 中,成员默认是 private 的,而 struct 中,成员默认是 public 的;
  • 继承:class 默认是 private 继承,而 struct 默认是 public 继承;
  • 模板:class 可以使用,而 struct 不能;

2.2.1、继承

关于继承,可能大家都忘记了『基类与子类在继承时,属性的限制』:

public、protected、private 指定继承方式
不同的继承方式会影响基类成员在派生类中的访问权限。

  1. public继承方式
    基类中所有 public 成员在派生类中为 public 属性;
    基类中所有 protected 成员在派生类中为 protected 属性;
    基类中所有 private 成员在派生类中不能使用。

  2. protected继承方式
    基类中的所有 public 成员在派生类中为 protected 属性;
    基类中的所有 protected 成员在派生类中为 protected 属性;
    基类中的所有 private 成员在派生类中不能使用。

  3. private继承方式
    基类中的所有 public 成员在派生类中均为 private 属性;
    基类中的所有 protected 成员在派生类中均为 private 属性;
    基类中的所有 private 成员在派生类中不能使用。

综上,我们可以得出结论:

  • 基类成员在派生类中的访问权限不得高于继承方式中指定的权限;
  • 不管继承方式如何,基类中的 private 成员在派生类中始终不能使用;
  • 如果希望基类的成员能够被派生类继承并且毫无障碍地使用,那么这些成员只能声明为 public 或 protected;
  • 如果希望基类的成员既不向外暴露(不能通过对象访问),还能在派生类中使用,那么只能声明为 protected;

我们用图来方便大家记忆:

inherience.png

2.2.2、模板(template)

说起模板可能大家有点生疏,但我换个词大家就能理解:函数重载!

何谓函数重载?
函数重载针对的是形参(即入参),可以有多个同名函数,但只要满足以下条件中的一个就行:

  1. 函数的参数个数不同;
  2. 函数的参数类型不同;
  3. 函数的不同参数类型顺序不同;

例如我有如下方法:

//交换 int 变量的值
void swap(int *a, int *b){
    int temp = *a;
    *a = *b;
    *b = temp;
}

//交换 float 变量的值
void swap(float *a, float *b){
    float temp = *a;
    *a = *b;
    *b = temp;
}

如果用模板来实现如下:

template<typename T> void Swap(T *a, T *b){
    T temp = *a;
    *a = *b;
    *b = temp;
}

template是声明模板关键字,typename是声明类型关键字(Java中泛型);

2.3、类型区别

  • struct 是值类型(可以简单类比为基本类型),因此,是值拷贝传递,数据的改变不会相互影响;
  • class 是引用类型(可以理解为指针),也叫对象,因此,是引用传递(指针传递),数据的改为会相互影响;

最简单的理解:

  • 内存地址是一致还是发生变化(struct 是值传递,所以是新的变量;而 class 是引用传递,内存地址不变);
  • 内存地址的引用计数是否发生变化(struct 没有引用计数;而 class 会随着引用传递计数不断增加);

三、Swift 中的两者浅析

无论是 OC 还是 Swift,它们的底层都是 C++,所以,struct 与 class 在 C++ 层面的特性是不会发生变化的,我们主要讨论的是两者在 Swift 层面的相同点与不同点。

3.1、相同点

  • 定义属性用于存储值
  • 定义方法用于提供功能
  • 定义下标操作用于通过下标语法访问它们的值
  • 定义构造器(init)用于设置初始值
  • 支持扩展(extension)以增加默认实现之外的功能
  • 遵循协议(protocol)以提供某种标准功能

3.2、不同点

  • 继承允许一个类继承另一个类的特征
  • 类型转换允许在运行时检查和解释一个类实例的类型
  • 析构器允许一个类实例释放任何其所被分配的资源
  • 引用计数允许对一个类的多次引用

以上主要是针对 class 来说的,struct 是没有这些的。

四、总结

Swift 之所有增加了 struct,主要有以下几点考虑:

  • 安全性:值拷贝传递,没有引用计数;
  • 内存:没有引用也就不会因为循环引用导致内存泄漏;
  • 速度:值类型是直接在栈上分配,而不是在堆上,所以比 class 要快很多;
  • 拷贝:值类型是深拷贝,class 默认浅拷贝;
  • 线程安全:自动线程安全的(无论哪个线程去访问都非常简单);


关于速度话题的延伸:

  • “堆”和“栈”并不是数据结构上的Heap跟Stack,而是程序运行中的不同内存空间;
  • 栈是程序启动的时候,系统事先分配的,使用过程中,系统不干预;
  • 堆是用的时候才向系统申请的,用完了需要交还,这个申请和交还的过程开销相对就比较大了;
  • 栈是编译时分配空间,而堆是动态分配(运行时分配空间),所以栈的速度快;

从两方面来考虑:

  • 分配和释放:堆在分配和释放时都要调用函数(MALLOC,FREE),比如分配时会到堆空间去寻找足够大小的空间(因为多次分配释放后会造成空洞),这些都会花费一定的时间,而栈却不需要这些;
  • 访问时间:访问堆的一个具体单元,需要两次访问内存,第一次得取得指针,第二次才是真正得数据,而栈只需访问一次;

关于速度,这里有一个早期 stackoverflow 的帖子:
http://stackoverflow.com/a/24243626/596821

速度的测试用例(Github):
https://github.com/knguyen2708/StructVsClassPerformance


苹果官方也给出了两者如何选择使用:
https://developer.apple.com/documentation/swift/choosing_between_structures_and_classes

说白了就是:

  • 不考虑应用的状态,也不存在对外的数据交互,就用 struct;
  • 需要在多个地方修改 / 更新数据(共享数据),就用 class;
  • 涉及到继承就用 class,没有就用 struct;
  • 官方建议用 struct + protocol 来建模继承与共享;
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,014评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,796评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,484评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,830评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,946评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,114评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,182评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,927评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,369评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,678评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,832评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,533评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,166评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,885评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,128评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,659评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,738评论 2 351

推荐阅读更多精彩内容