Swift-类和结构体(Class and Structures)(八)

前言

与其他编程语言不通,Swift并不要求我们为自定义的类和结构体去创建独立的接口和实现文件。我们所要做的就是在一个单一文件中定义一个类或者结构体,系统将会自动生成面向其他代码的外部接口。
小节包含内内容:

  • 列表内容
  • 类和结构体的对比
  • 结构体和枚举是值类型
  • 类是引用类型
  • 类和结构体的选择
  • 字符串、数组、字典类型的赋值与复制行为

分条详述

  1. 类和结构体对比

    首先,Swift中类和结构体有很多共同点,共同之处在于(PS:关于属性、方法、下标脚本、构造过程、扩展和协议,其实后面都会说到,教材中提供了书中的具体位置链接):

    • 定义属性用于存储值
    • 定义方法用于提供功能
    • 定义附属脚本用于访问值
    • 定义构造器用于生成初始值
    • 定义扩展用以增加默认实现的功能
    • 事先协议以提供某种标准功能

    与结构体相比,类还有如下的附加功能(PS:关于继承、类型转换、析构过程和自动引用计数,都是后面要说的内容):

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

    结构体总是通过被复制的方式在代码中传递,不使用引用计数

    类和结构体有着类似的定义方式。通过关键字class和struct来分别表示类和结构体,并在一对大括号内定义他们的具体内容:

    class SomeClass {
    // 类内部实现
    }
    struct SomeStructure {
    // Structure definition goes here
    }

    每次定义一个新类,实际上都是定义了一个新的Swift类型,所以命名的时候注意规范,即命名时首字母大写,枚举也是如此。属性和方法名字首字母小写。命名时都要遵循驼峰命名法,并且起名字的时候尽量词能达意。
    下面根据具体的例子来说明问题:

    // 结构体,描述一个显示器的分辨率
    struct Resolution {
    var width = 0
    var height = 0
    }
    // 类,描述一个视频显示器的特定模式
    class VideoMode {
    var resolution = Resolution()
    var interfaced = false
    var frameRate = 0.0
    var name: String?
    }

    结构体和类都可以通过构造器语法来创建实例进行使用,构造器语法最简单的就是在类或者结构体名称后面添加小括号,构造时属性均会被初始化默认值。如:

    // 构造结构体和类的实例
    let someResolution = Resolution()
    let someVideoMode = VideoMode()

    可以通过点语法访问结构体或者类的属性或方法,并且当属性可变,即用var修饰时,可以改变属性值。相比于OC,Swift可以改变结构体或类的属性的子属性的值,而不需要为整个属性赋新值。

    // 构造结构体和类的实例
    var someResolution = Resolution()
    let someVideoMode = VideoMode()
    // 点语法访问属性并修改值
    someResolution.height = 200
    someVideoMode.frameRate = 12.12
    someVideoMode.resolution.width = 300

    注意,所有的结构体都有一个自动生成的成员逐一构造器,就是建立的时候为每个属性赋值。然而类并没有这个特性。只需在构造时输入左括号,自然会有提示。

  2. 结构体和枚举是值类型

    值类型赋值给一个变量、常量或者被传递给一个函数时,其值会被拷贝,新拷贝的值和原来的值是完全隔离的,即他们的改变是独立的。在Swift中,整数、浮点数、布尔值、字符串、数组和字典,都是值类型,并且在底层都是以结构体的形式所实现。显然,结构体和枚举类型都是值类型。下面简单地代码很能说明问题:

    // 值传递,对象以及所有属性都发生了拷贝
    let someResolution = Resolution(width: 1920, height: 1080)
    var anotherResolution = someResolution
    anotherResolution.width = 2880
    anotherResolution.height = 1600
    // 通过输出结果,可以清晰体会到值传递是复制了另外一份数据
    print(someResolution.height,someResolution.width)      // 1080 1920
    print(anotherResolution.height, anotherResolution.width)   // 1600 2880
  3. 类是引用类型

    与值类型不同,引用类型再被赋予到一个变量、常量或者被传递到一个函数时,其值不会被拷贝,因此引用是引的已存在的示例本身而不是拷贝。可以理解为多个指针指向同一个内存区域,任何一个指针对数据进行操作时,其他指针取到数据也是已经被修改的数据。如:

    //  类是引用类型
    let someVideoMode = VideoMode()
    someVideoMode.frameRate = 2.05
    someVideoMode.resolution.height = 300
    let anotherVideoMode = someVideoMode
    anotherVideoMode.frameRate = 99.99
    anotherVideoMode.resolution.height = 2222
    //  展示现在两个类属性的值
    someVideoMode.frameRate        //   99.9999
    someVideoMode.resolution.height       //   2222
    anotherVideoMode.frameRate        //   99.999
    anotherVideoMode.resolution.height    //   2222

    观察上面代码会看到一个比较迷惑的地方,那就是已经使用 let 设置类实例,为什么还能修改它的值呢?如果结构体使用 let 修饰,是无法更改里面的值的。为什么呢?为什么呢??原因很简单,因为类是引用类型,也就是说 someVideoMode 和 anotherVideoMode 本身并没有存储值,改变的仅仅是被引用的属性的值,他们本身的值并没有改变,所以可以用 let 修饰为常量来使用。

    因为类是引用类型,有可能多个常量和变量幕后同时引用一个类实例。如果能够判断两个常量或者变量是否引用同一个类实例,将会很有帮助。为了达到这种目的,Swift内建了两个恒等运算符:

    • 等价于(===)
    • 不等价于(!==)

    注意,”等价于“(用三个等号表示 ===) 与 等于(用两个等号表示 ==)的不同:

    • “等价于”表示两个类类型(class type)的常量或者变量引用同一个类类型。
    • “等于”表示两个实例的值“相等”或“相同”,判断时要遵照设计者定义的评判标准,因此,相对于“相等”来说,这事一种更合适的叫法。
  4. 类和结构体的选择

    当考虑使用结构体合适使用类的时候,如果能用结构体,就果断使用结构体吧,尽量少的使用类。我在看atSwift第一届Swift开发者大会的时候,有好几个演讲人员都强调了这一点,能用结构体就不用类。在教材中,也是提供了什么情况下使用结构体效果更好。
    按照通用的准则,当符合一条或多条以下条件时,请考虑使用结构体:

    • 该数据结构主要目的是用来封装少量相关简书数据值。
    • 有理由预计该数据结构的实例再被赋值或传递时,封装的数据将会被拷贝而不是被引用。
    • 该数据结构图中存储的值类型属性,也应该被拷贝,而不是被引用。
    • 该数据结构不需要去继承另一个既有类型的属性或者行为。

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

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

    教程中的话:在所有其他案例中,定义一个类,生成它的实例,并通过引用来管理和传递。实际上,这意味着绝大部分的自定义数据构造都应该是类,而非结构体。

    其实,当教材和现实有了点冲突,我选择相信多用结构体。。。

  5. 关于Swift中的值传递和OC中的常用数据类型

    在Swift中,所有值传递都是以拷贝的形式发生的,他们的低层都是由结构体实现的。这点和和OC中是不一样的。在Object-C中,NSString、NArray和NSDictionary中低层均以类的形式实现,它们在被赋值或者被传入参数时,不会发生值拷贝,而是传递现有实例的引用。

    注意,以上对字符串、数组、字典的“拷贝”行为的描述,在代码中,好像拷贝行为看起来总会发生。然而,Swift在幕后只在绝对必要时才执行实际的拷贝。Swift管理所有值拷贝以确保性能最优化,所以没必要去回避赋值来保证性能最优化。

结束语

为啥现在写的这么慢了呢?是因为工作忙了么,毛线,就是过了新鲜劲,懒了。。。。。吾日三省吾身,早上吃什么,中午吃什么,晚上吃什么,就是忘了自省下为啥今天没坚持写点东西~
昨天晚上把权利的游戏第六季的前三集一次性看完了,也是过瘾,红巫女威武,囧哥威武!另外,感觉这张图的尺度好大~滑稽.jpg

另外,点击查看前几篇文章

Swift-类和结构体(Class and Structures)(八)_第1张图片

你可能感兴趣的:(类,Class,swift,结构体,structure)