闭包(Closures)
使用过其他语言的应该对代码块并不陌生,Swift中的闭包与C,OC中的Block相似。
表示自包含的函数代码块,可以在代码中传递和使用。
并且可以捕获和存储上下文的变量以及常量值,Swift会为你进行捕获相关的内存操作。
上一篇文章提到的函数,也是一种特殊的闭包,具体在:
全局函数是有名字但是不会捕获任何值的闭包。
嵌套函数是有名字且可以捕获域内值的闭包。
闭包表达式是利用轻量级语法写的可以捕获上下文值的匿名闭包。
基本语法
表达式的一般语法以及简化过程
上面罗列了同一个行为的五种写法,也标明了简化方法适用的情景。
尾随闭包
上面那个闭包若使用尾随闭包的方式来写,则表现为:
reversed = sort(names) {$0 > $1}
通常我们会把具有多行的闭包表达式写入尾随闭包。
例如Array的map方法可以对数组内的每一个元素调用一次闭包,返回一个新的数组。
这里捕获了numbers数组中的每个值并将output映射到一个新的strings数组中。
值捕获
闭包可以在其上下文捕获一些值,即使原域已经不存在。上面提到嵌套函数是有名字可以捕获域内值的闭包。Swift中最简单的闭包形式即是嵌套函数。
这里定义了一个嵌套函数makeIncrementor,里面记录了一个变量runningTotal,嵌套在里面的函数可以捕获这个值并将其持有保留住。
但是如果新声明了一个相同函数的变量/常量引用,则其runningTotal并不共享
闭包是引用类型,上面声明的常量是指向闭包内容的一个引用,而并非闭包内容本身。
枚举
Swift的枚举十分灵活,case中的值可以是字符串,字符,整型或者浮点型。并且可以指定任何类型的相关值存储到枚举成员中。
在Swift里,枚举是一等类型(first-class),它支持类所支持的特性(除了继承),你可以为它定义一些函数让其富有行为等等。
基本语法
当我们已经确定了其是CompassPoint的时候,可以直接使用.Value省略枚举名的形式赋值。
switch匹配
相关值
前面提到过,我们可以为其指定任意类型的相关值存储到枚举成员中。
来看下面这个例子
我们给BarCode枚举存储了一些相应case的相关值,并且可以利用绑定值的方式去访问它,如在switch中定义了个常量identifier来获取Barcode中QRCode的相关值。
原始值
在Swift中,枚举并不会像c,objective-c中的枚举在你未声明枚举值的情况下隐式的赋值0,1,2...只有我们给其赋值时才会猜测其值,但是也仅限于Int。
Swift为其定义了方法来访问原始值toRaw()或者通过fromRaw(value)访问其枚举成员。
由于我们的枚举中并没有5对应的成员,所以其为nil。但如果换为3,则是Green。
那如果枚举成员是String呢?会不会也会猜测值?当然是不太可能了- -
编译器会报错
非integer必须要给其赋值才可以。
类与结构体
定义,实例声明与属性
由于这里并没有设置自定义的指定构造器,所以可以直接使用默认的方式()来初始化一个实例。
访问设置属性则使用点语法,不过与Ojbective--C不同的是,Swift允许对结构体的子属性直接赋值。另外一个不同则是Swift类不需要.h接口文件。
在OC中我们想修改Rect.Origin的x值经常需要赋一个新的origin给rect,而Swift中则可以直接修改了。
在Swift中结构体,枚举和类都可以通过写一些成员函数来为其添加行为。
构造时则稍有不同,结构体可以为其提供逐一成员构造器。
这是系统自动提供的,而类则没有提供。
值类型和引用类型
结构体,枚举和类另外主要不同的一点是类型。
结构体和枚举为值类型,类则是引用类型。
值类型指在赋值传递的时候,系统会自动为其拷贝一个新值并传递给新声明的变量或常量。
我们注意在将hd赋值给cinema后立即cinema的width进行了修改,但是再访问hd.width还是原来的值1920.说明只是将hd的拷贝赋给了cinema而并不是hd本身。
引用类型这是将新的变量或常量指向了原来的对象,而并非拷贝对象值之后进行赋值。
这里修改了frameRate,再访问原来变量的frameRate发现也发生了变化。原因在于他们都指向了同一块堆内存。
在Swift中,判定引用相等引人了一个恒等符号"===",来判定是否引用同一实例,不引用同一实例则为"!=="(不等价于)
那么"==="和"=="的不同在于哪里呢?
等价于("===")表示两个类类型的常量或变量引用同一个实例。
而"=="表示两个实例的值是否相等,判定需要遵照类设计者定义的判定标准。
由于结构体是值类型,我们不使用等价于进行判断。
类和结构体的异同和选择
首先来看类和结构体的相同点:
定义属性用于存储值
定义方法用于提供功能
定义附属脚本(subscript)用于访问值
定义构造器用于初始化值和配置
通过扩展以增加默认实现的功能
符合协议以对某类提供标准功能
不同点在于:(类)
继承允许一个类继承另一个类的特征
类型转换允许在运行时检查和解释一个类实例的类型
解构器(析构过程会提到)允许一个类实例释放任何其所被分配的资源
引用计数允许对一个类的多次使用
则我们通常在下列情况下考虑结构体:
用来封装少量相关简单数据值
预计数据传递会被拷贝而不是引用
结构体中的值类型属性也会被拷贝
不需要继承或被继承特征和行为
集合类型的赋值和拷贝行为
上面说到了值类型和引用类型。在Swift中,集合类型字典和数组在后台均以结构体实现,但是他们的情况稍微有些特殊。
虽然通常值类型的赋值行为都是通过拷贝实现的,我们也不需要害怕过多使用内存而带来的性能问题,Swift会管理所有的值拷贝以确保性能最优化。
字典的赋值和拷贝行为
字典的key/value中,若其为值类型则拷贝值,引用类型则拷贝引用。
数组的赋值和拷贝行为
相比字典数组要复杂的多,当操作数组内容时,数组能提供接近C语言的性能,并且保证拷贝只有在必要时发生。
那么必要时是什么时候呢?是改变数组长度的时候,例如调用了append(), insert(), remove()等函数,数组的拷贝才会发生,不改变时则属于引用传递。
有的时候我们想确保数组的唯一性,这时可以调用unshare方法使其变成唯一拷贝。强制拷贝则使用copy方法。但是copy在任何情况下都会创建新的拷贝,而unshare则会确保唯一引用。
在判定数组是否相同时我们也可以使用"==="来判定两个数组是否共用相同的空间或者元素。
因为他们都非共用空间,所以返回都是false.
有关闭包,枚举,结构体和类的基础知识都讲解完毕。欢迎勘误和讨论。