美文网首页
iOS Swift5:浅析结构体(struct)与类(class

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

作者: 青叶小小 | 来源:发表于2021-02-26 00:10 被阅读0次

    一、前言

    关于 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 来建模继承与共享;

    相关文章

      网友评论

          本文标题:iOS Swift5:浅析结构体(struct)与类(class

          本文链接:https://www.haomeiwen.com/subject/qtnufltx.html