在面向过程的编程语言(如C语言)中,结构体用得比较多,但是面向对象之后,如在C++和Objective-C中,结构体已经很少使用了。这是因为结构体能够做的事情,类完全可以取而代之。
而Swift语言却非常重视结构体,把结构体作为实现面向对象的重要手段。Swift中的结构体与C++和Objective-C中的结构体有很大的差别,C++和Objective-C中的结构体只能定义一组相关的成员变量,而Swift中的结构体不仅可以定义成员变量(属性),还可以定义成员方法。因此,我们可以把结构体看做是一种轻量级的类。
Swift中的类和结构体非常类似,都具有定义和使用属性、方法、下标和构造器等面向对象特性,但是结构体不具有继承性,也不具备运行时强制类型转换、使用析构器和使用引用计等能力。
一、类和结构体定义
Swift中的类和结构体定义的语法也是非常相似的。我们可以使用class关键词定义类,使用struct关键词定义结构体,它们的语法格式如下:
class 类名 {
    定义类的成员
}
struct 结构体名 {
    定义结构体的成员
}
从语法格式上看,Swift中的类和结构体的定义更类似于Java语法,不需要像C++和Objective-C那样把接口部分和实现部分放到不同的文件中。
类名、结构体名的命名规范与枚举类型的要求是一样的。下面我们来看一个示例:
class Employee { //定义员工类
    var no : Int = 0 //定义员工编号属性
    var name : String = "" //定义员工姓名属性
    var job : String? //定义工作属性
    var salary : Double = 0 //定义薪资属性

var dept : Department?  //定义所在部门属性
}

struct Department { //定义部门结构体
    var no : Int = 0 //定义部门编号属性
    var name : String = "" //定义部门名称属性
}
Employee是我们定义的类,Department是我们定义的结构体。在Employee和Department中我们只定义了一些属性。关于属性的内容我们将在下一章介绍。
Employee和Department是有关联关系的,Employee所在部门的属性dept与Department关联起来,它们的类图如下图所示。
 
我们可以通过下列语句实例化:
var emp = Employee()
var dept = Department()
Employee()和Department()是调用它们的构造器实现实例化,关于构造器我们会在14.1节介绍。
提示 实例化之后会开辟内存空间,emp和dept被称为“实例”,但只有类实例化的“实例”才能被称为“对象”。事实上,不仅仅是结构体和类可以实例化,枚举、函数类型和闭包开辟内存空间的过程也可以称为实例化,结果也可以叫“实例”,但不能叫“对象”。
二、再谈值类型和引用类型
数据类型可以分为:值类型和引用类型,这是由赋值或参数传递方式决定的。值类型就是在赋值或给函数传递参数时候,创建一个副本,把副本传递过去,这样在函数的调用过程中不会影响原始数据。引用类型就是在赋值或给函数传递参数的时候,把本身数据传递过去,这样在函数的调用过程中会影响原始数据。
在众多的数据类型中,我们只需记住:只有类是引用类型,其他类型全部是值类型。即便结构体与类非常相似,它也是值类型。值类型还包括整型、浮点型、布尔型、字符串、元组、集合和枚举。
Swift中的引用类型与Java中的引用类型是一样的,Java中的类也是引用类型。如果你没有Java经验,可以把引用类型理解为C、C++和Objective-C语言中的指针类型,只不过不需要在引用类型变量或常量前面加星号(*)。
下面我们看一个示例:

  1. var dept = Department() ①
  2. dept.no = 10
  3. dept.name = "Sales"         ②
  4. var emp = Employee()        ③
  5. emp.no = 1000
  6. emp.name = "Martin"
  7. emp.job = "Salesman"
  8. emp.salary = 1250
  9. emp.dept = dept             ④
  10. func updateDept (dept : Department) {   ⑤
  11. dept.name = "Research"  ⑥
  12. }
  13. println("Department更新前:\(dept.name)")   ⑦
  14. updateDept(dept)                ⑧
  15. println("Department更新后:\(dept.name)")   ⑨
  16. func updateEmp (emp : Employee) {   ⑩
  17. emp.job = "Clerk"       ⑪
  18. }
  19. println("Employee更新前:\(emp.job)")   ⑫
  20. updateEmp(emp)              ⑬
  21. println("Employee更新后:\(emp.job)")   ⑭

上述代码第①~②行创建Department结构体实例,并设置它的属性。代码第③~④行创建Employee类实例,并设置它的属性。
为了测试结构体是否是值类型,我们在第⑤行代码定义了updateDept函数,它的参数是Department结构体实例。第⑥行代码dept.name = "Research"是改变dept实例。然后在第⑦行打印更新前的部门名称属性,在第⑧行进行更新,在第⑨行打印更新后的部门名称属性。如果更新前和更新后的结果一致,则说明结构体是值类型,反之则为引用类型。事实上第⑥行代码会有编译错误,错误信息如下。
Playground execution failed: error: <REPL>:34:15: error: cannot assign to 'name' in 'dept'
    dept.name = "Research"
    ~~~~~~~~~ ^
这个错误提示dept.name = "Research"是不能赋值的,这说明了dept结构体不能修改,因为它是值类型。其实有另外一种办法可以使值类型参数能够以引用类型传递,我们在第9章介绍过使用inout声明的输入输出类型参数,这里需要修改一下代码:
func updateDept (inout dept : Department) {
    dept.name = "Research"
}

println("Department更新前:\(dept.name)")
updateDept(&dept)
println("Department更新后:\(dept.name)")
我们不仅要将参数声明为inout,而且要在使用实例前加上&符号。这样修改后输出结果如下:
Department更新前:Sales
Department更新后:Research
相比之下,第⑩行代码是定义updateEmp函数,它的参数是Employee类的实例,我们不需要将参数声明为inout类型。在第⑪行修改emp没有编译错误,这说明Employee类是引用类型,在调用的时候不用在变量前面添加&符号,见代码第 行。输出结果如下:
Employee更新前:Salesman
Employee更新后:Clerk
这个结果再次说明了类是引用类。
三、引用类型的比较
我们在第4章介绍了基本运算符,提到了恒等于(===)和不恒等于(!===)关系运算符。===用于比较两个引用是否为同一个实例,!===则恰恰相反,它只能用于引用类型,也就是类的实例。
下面我们看一个示例:

  1. var emp1 = Employee()       ①
  2. emp1.no = 1000
  3. emp1.name = "Martin"
  4. emp1.job = "Salesman"
  5. emp1.salary = 1250
  6. var emp2 = Employee()       ②
  7. emp2.no = 1000
  8. emp2.name = "Martin"
  9. emp2.job = "Salesman"
  10. emp2.salary = 1250
  11. if emp1 === emp2                ③
  12. {
  13. println("emp1 === emp2")
  14. }
  15. if emp1 === emp1                ④
  16. {
  17. println("emp1 === emp1")
  18. }
  19. var dept1 = Department()    ⑤
  20. dept1.no = 10
  21. dept1.name = "Sales"
  22. var dept2 = Department()    ⑥
  23. dept2.no = 10
  24. dept2.name = "Sales"
  25. if dept1 == dept2   //编译失败  ⑦
  26. {
  27. println("dept1 === dept2")
  28. }

上述代码第①行和第②行分别创建了emp1和emp2两个Employee实例。在代码第③行比较emp1和emp2两个引用是否为一个实例。可以看到,比较结果为False,也就是emp1和emp2两个引用不是一个实例,即便是它们内容完全一样,结果也是False,而第④行的比较结果为True。如果我们采用==比较,结果会如何呢?代码如下:
if emp1 == emp2 
{
    println("emp1 === emp2")
}
答案是有如下编译错误。==比较要求两个实例的类型(类、结构体、枚举等)必须要在该类型中重写==运算符,定义相等规则。同样的错误也会发生在第⑦行代码。
Playground execution failed: error: <REPL>:42:9: error: could not find an overload for '==' that accepts the supplied arguments
if emp1 == emp2
   ~~~~~^~~~~~~
代码第⑤行和第⑥行分别创建了dept1和dept2两个Department实例。在代码第⑦行使用==比较dept1和dept2两个值是否相等,不仅不能比较,而且还会发生编译错误,这在上面已经解释过了。
如果我们采用恒等于===比较dept1和dept2,结果会如何呢?代码如下:
if dept1 === dept2
{
    println("dept1 === dept2")
}

我们发现会有编译错误。===不能比较值类型,而Department结构体是值类型,因此不能使用===比较。

更多内容请关注国内第一本Swift图书《Swift开发指南》
本书交流讨论网站:http://www.51work6.com/swift.php
欢迎加入Swift技术讨论群:362298485

欢迎关注智捷iOS课堂微信公共平台

Swift结构体与类的更多相关文章

  1. Swift 结构体和类

    //*---------------------结构体-------------*/ //swift结构体也具有封装的概念 //swift结构体比OC C语言里面的结构体更加进了一步,里面可以有方法, ...

  2. Swift结构体和类

    Swift结构体 在Swift标准中,绝大多数的公开的类型都是结构体,而枚举和类只占很小的一部分 比如Bool.Int.Double.String.Array.Dictionary等常见的类型都是结构 ...

  3. iOS Swift结构体与类的方法调度

    前言 hello,小伙伴们:在忙碌中闲暇之余给大家聊聊swift的知识点,今天给大家带来的是swift中结构体与类的方法调度详细区别,希望对你有所帮助,好了废话不用多说,接下来步入主题! 1.普通方法 ...

  4. 学习swift从青铜到王者之swift结构体和类08

    定义 // 定义类 class StudentC{ } // 定义结构体 struct StudentS{ } 定义存储属性 // 定义类 class StudentC{ var name:Strin ...

  5. Swift具体解释之六----------------枚举、结构体、类

    枚举.结构体.类 注:本文为作者自己总结.过于基础的就不再赘述 ,都是亲自測试的结果.如有错误或者遗漏的地方.欢迎指正,一起学习. 1.枚举 枚举是用来定义一组通用类型的一组相关值 ,关键字enum ...

  6. 10 结构体和类 - —— 《Swift3.0 从入门到出家》

    Swift中的面向对象5个要素:枚举.结构体.类.协议.扩展 面向对象研究的是对象,完成一件事情需要多个对象参与,是生活的映射 Swift中结构体和类非常相似,也就是结构体能完成类的所有功能.结构体是 ...

  7. C#中结构体和类的区别

    结构体和类同样能够定义字段,方法和构造函数,都能实例化对象,这样看来结构体和类的功能好像是一样的了,但是他们在数据的存储上是不一样的 C#结构体和类的区别问题:这两种数据类型的本质区别主要是各自指向的 ...

  8. C C++ 中结构体与类

    先来说说C和C++中结构体的不同 a) C语言中的结构体不能为空,否则会报错 1>d:\myproject\visual studio 2013\projects\myc++\main.c(71 ...

  9. Swift 结构体的使用

    Swift 结构体是构建代码所用的一种通用且灵活的构造体. 我们可以为结构体定义属性(常量.变量)和添加方法,从而扩展结构体的功能. 与 C 和 Objective C 不同的是: 结构体不需要包含实 ...

随机推荐

  1. [刷题codeforces]650A.637A

    650A Watchmen 637A Voting for Photos 点击查看原题 650A又是一个排序去重的问题,一定要注意数据范围用long long ,而且在写计算组合函数的时候注意也要用l ...

  2. Unity3D - 关于Dynamic和Static

    含有Collider和RigidBody的GameObject, Unity视之为Dynamic 含有Collider的GameObject, Unity视之为Static 如果Static的物体发生 ...

  3. SqlServer刷新所有视图

    CREATE PROCEDURE RefreshAllView AS DECLARE MyCursor CURSOR FOR select Name from dbo.sysobjects where ...

  4. C++ C++ 控制台程序 设置图标

    . 实现过程 创建1个控制台程序. 新建1个 Resource Script文件 #include "stdio.h" #include <windows.h> #in ...

  5. Office 365 Certificate Exam Resources

    70-321 Deploying Office 365 Deploying Office 365 Jump Start (01): Infrastructure Planning Deploying ...

  6. grunt自动化工具

    Grunt和 Grunt 插件是通过 npm 安装并管理的,npm是 Node.js 的包管理器. 安装 CLI 首先,需要先将Grunt命令行(CLI)安装到全局环境中. npm install - ...

  7. MySQL并发复制系列三:MySQL和MariaDB实现对比

    http://blog.itpub.net/28218939/viewspace-1975856/ 并发复制(Parallel Replication) 系列三:MySQL 5.7 和MariaDB ...

  8. 修改范围PHP_INI_SYSTEM与PHP_INI_ALL的区别

    PHP_INI_USER 可在用户脚本(例如 ini_set() )或 Windows 注册表(自 PHP 5.3 起)以及 .user.ini 中设定 PHP_INI_PERDIR 可在 php.i ...

  9. How to Diagnose Audi Vehicles via Tuirel S777

    Here share how to diagnose Audi cars via Tuirel S777. 1.Well connect Tuirel S777 to your Audi car, a ...

  10. [Visual Studio] 开启Visual Studio 2012通过右键菜单创建单元测试(Unit Test)

    Visual Studio 2012可以说是迄今为止微软VS开发工具中用户体验最好的产品,无论是速度还是体验以及功能,都非常出色,但是,使用了一段时间后发现有一个之前版本VS都有的功能却在Visual ...