初见:
使用 func 来声明一个函数,使用名字和参数来调用函数。使用->来指定函数返回值。
使用一个元组来返回多个值。
函数的参数数量是可变的,用一个数组来获取它们:func sumOf(numbers: Int...) -> Int
函数可以嵌套。被嵌套的函数可以访问外侧函数的变量,你可以使用嵌套函数来重构一个太 长或者太复杂的函数。
函数是一等公民,这意味着函数可以作为另一个函数的返回值,函数也可以当做参数传入另一个函数。
函数实际上是一种特殊的闭包,你可以使用{}来创建一个匿名闭包。使用 in 来分割参数并返 回类型。
有很多种创建闭包的方法。如果一个闭包的类型已知,比如作为一个回调函数,你可以忽 略参数的类型和返回值。单个语句闭包会把它语句的值当做结果返回。
你可以通过参数位置而不是参数名字来引用参数——这个方法在非常短的闭包中非常有用。 当一个闭包作为最后一个参数传给一个函数的时候,它可以直接跟在括号后面。
如果你不需要计算属性但是需要在设置一个新值之前或者之后运行一些代码,使用willSet和didSet。
var variable: Type
{
get{}
set{}
willSet{}
didSet{}
}
类中的方法和一般的函数有一个重要的区别,函数的参数名只在函数内部使用,但是方法的
参数名需要在调用的时候显式说明(除了第一个参数)。默认情况下,方法的参数名和它在 方法内部的名字一样,不过你也可以定义第二个名字,这个名字被用在方法内部。
处理变量的可选值时,你可以在操作(比如方法、属性和子脚本)之前加?。如果?之前的值
是 nil,?后面的东西都会被忽略,并且整个表达式返回 nil。否则,?之后的东西都会被运行。
在这两种情况下,整个表达式的值也是一个可选值。
使用 toRaw 和 fromRaw 函数来在原始值和枚举值之间进行转换。
使用 struct 来创建一个结构体。结构体和类有很多相同的地方,比如方法和构造器。它们结 构体之间最大的一个区别就是 结构体是传值,类是传引用。
类、枚举和结构体都可以实现接口。
使用 extension 来为现有的类型添加功能,比如添加一个计算属性的方法。你可以使用扩展
来给任意类型添加协议,甚至是你从外部库或者框架中导入的类型。
在尖括号里写一个名字来创建一个泛型函数或者类型。
基础部分:
可选有点像在 Objective-C 中使用 nil,但是它可
以用在任何类型上,不仅仅是类。
Swift 是一个类型安全的语言(或者叫做强类型语言)。
有一种情况下必须要用分号,即你打算在同一行内写多条独立的语句.
Swift ??提供了 8,16,32 和 64 位的有符号和无符号整数类型。
- Double 表示 64 位浮点数。当你需要存储很大或者很高精度的浮点数时请使用此类型。
- Float 表示 32 位浮点数。精度要求不高的话可以使用此类型。
类型别名(type aliases)就是给现有类型定义另一个名字。你可以使用 typealias 关键字来定 义类型别名。
元组(tuples)把多个值组合成一个复合值。元组内的值可以使任意类型,并不要求是相 同类型。
元组在临时组织值的时候很有用,但是并不适合创建复杂的数据结构。
使用可选绑定(optional binding)来判断可选是否包含值,如果包含就把值赋给一个临时
常量或者变量。可选绑定可以用在 if 和 while 语句中来对可选的值进行判断并把值赋给一 个常量或者变量。
nil 不能用于非可选的常量和变量。如果你的代码中有常量或者变量需要处理值缺失 的情况,请把它们声明成对应的可选类型。
任何类型的可选都可以被设置为 nil,不只是对象类型。
隐式解析可选(implicitly unwrapped optionals)—当可选被第一次赋值之后就可以确定之后一直有值的时候,隐式解析可选非常有用。隐式 解析可选主要被用在 Swift 中类的构造过程中。
运算符有一目, 双目和三目运算符.
加法操作 + 也用于字符串的拼接:
每个比较运算都返回了一个标识表达式是否成立的布尔值
2.单字节 Unicode 标量,写成 \xnn,其中 nn 为两位十六进制数。
3.双字节 Unicode 标量,写成 \unnnn,其中 nnnn 为四位十六进制数。
4.四字节 Unicode 标量,写成 \Unnnnnnnn,其中 nnnnnnnn 为八位十六进制数。
Swift 的 String 类型是值类型。
Swift ??供了三种方式来比较字符串的值:字符串相等,前缀相等和后缀相等。
Swift 语言??供经典的数组和字典两种集合类型来存储集合数据。
写 Swift 数组应该遵循像 Array<SomeType>这样的形式,其中 sometype 是这个数组中唯
一允许存在的数据类型。 我们也可以使用像 SomeType[]这样的简单语法。
Array<sometype> == sometype[]
类型检查与类型推断
Swift 的 switch 语句比 C 语言中更加强大。在 C 语言中,如果??个 case 不小心漏写了 break,这个 case 就会“掉入”下一个 case,Swift 无需写 break,所以不会发生这种“掉入”
的情况。Case 还可以匹配更多的类型模式,包括范围(range)匹配,元组(tuple)和特
定类型的??述。switch case 语句中匹配的值可以是由 case 体内部临时的常量或者变量决 定,也可以由 where 分句??述更复杂的匹配条件。
case 块的模式允许将匹配的值绑定到一个临时的常量或变量,这些常量或变量在该 case 块 里就可以被引用了——这种行为被称为值绑定。
严格地说,sayGoodbye 函数确实还返回一个值,即使没有定义返回值。没有 定义返回类型的函数返回了一个 Void 类型的特殊值。这仅是一个空元组,这里边没有元素,可以被写成()。
??示:返回值可以忽略,但一个定义了返回值的函数则必须有返回值。对于一个定义了返回类型的函数来说,如果没有返回值,那么将不允许控制流离开函数的底部。
这种形参类型名称被称之为本地形参名(local parameter name),因为它们只能在函数的主体中使用。
外部形参名:有时当你调用一个函数将每个形参进行命名是非常有用的,以表明你把每个实参传递给函数的目的。如果您为形参??供一个外部形参名称,那么外部形参名必须在调用时使用。
请在函数形参列表的末尾放置带默认值的形参。这将确保所有函数调用都使用顺序相同的无默认值实参,并让在每种情况下清晰地调用相同的函数。
函数最多可以有一个可变形参,而且它必须出现在参数列表的最后,以避免使用多个形参调用函数引发歧义。如果你的函数有一个或多个带有默认值的形参,并且还有可变形参,请将可变形参放在所有默认形参之后,也就是的列表的最末尾。
你对变量形参所做的改变不会比调用函数更持久,并且在函数体外是不可见的。变量形参仅存在于函数调用的声明周期中。
当你把变量作为实参传递给 in out 形参时,需要在直 接在变量前添加 & 符号,以表明它可以被函数修改。in-out 参数不能有默认值,可变参数的参数也不能被标记为 inout。如果您标记 参数为 inout,它不能同时被标记为 var 或 let。
具有相同匹配类型的不同函数可以被赋给同一个变量,和非函数类型一样.与其他类型一样,当你给函数赋一个常量或者变量时,你可以让 Swift 去推断函数的类型。
在 函数 章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采取如下三种形式之 一:
1. 全局函数是一个有名字但不会捕获任何值的闭包
2. 嵌套函数是一个有名字并可以捕获其封闭函数域内值的闭包
3. 闭包表达式是一个利用轻量级语法所写的可以捕获其上下文中变量或常量值的没有名字 的闭包
闭包表达式语法可以使用常量、变量和 inout 类型作为参数,但不??供默认值。 也可以在 参数列表的最后使用可变参数。元组也可以作为参数和返回值。单行表达式闭包可以省略 return
闭包表达式语法有如下一般形式:
{ (parameters) -> returnType in
statements
}
如果您需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用 trailing 闭包来增强函数的可读性。
Trailing 闭包是一个书写在函数括号之外(之后)的闭包表达式,函数支持将其作为最后一个参数调用。如果函数只需要闭包表达式一个参数,当您使用 trailing 闭包时,您甚至可以把() 省略掉。
捕获 (Caputure)
Swift 最简单的闭包形式是嵌套函数,也就是定义在其他函数体内的函数。 嵌套函数可以捕 获其外部函数所有的参数以及定义的常量和变量。
闭包可以在其定义的上下文中捕获常量或变量。 即使定义这些常量和变量的原作用域已经 不存在,闭包仍然可以在闭包函数体内引用和修改这些值。
无论您将函数/闭包赋值给一个常量还是变量,您实际上都是将常量/变量的值设置为对应函数/闭包的引用。
枚举
在 Swift 中,枚举类型是一等(first-class)类型。它们采用了很多传统上只被类所支持的 特征,例如计算型属性(computed properties),用于??供关于枚举当前值的附加信息,实
例方法(instance methods),用于??供和枚举所代表的值相关联的功能。枚举也可以定义 构造器(initializers)来??供一个初始成员值;可以在原始的实现基础上扩展它们的功能;
可以遵守协议(protocols)来??供标准的功能。
注意,原始值和关联值是不相同的。当你开始在你的代码中定义枚举的时候原始值是被预先 填充的值,像上述三个 ASCII 码。对于一个特定的枚举成员,它的原始值始终是相同的。 关联值是当你在创建一个基于枚举成员的新常量或变量时才会被设置,并且每次当你这么做 得时候,它的值可以是不同的。
结构体与类
注意:结构体总是通过被复制的方式在代码中传递,因此请不要使用引用计数。
与 Objective-C 语言不同的是,Swift 允许直接设置结构体属性的子属性。
所有结构体都有一个自动生成的成员逐一初始化器,用于初始化新结构体实例中成员的属
性。新实例中各个属性的初始值可以通过属性的名称传递到成员逐一初始化器之中:
1. let vga = resolution(width:640, heigth: 480)
与结构体不同,类实例没有默认的成员逐一初始化器。
结构体和枚举是值类型
值类型被赋予给一个变量,常数或者本身被传递给一个函数的时候,实际上操作的是其的
拷贝。实际上,在 Swift 中,所有的基本类型:
整数(Integer)、浮点数(floating-point)、布尔值(Booleans)、字符串(string)、数组(array)和
字典(dictionaries),都是值类型,并且都是以结构体的形式在后台所实现。
需要注意的是 tenEighty 和 alsoT enEighty 被声明为常量(constants)而不是变量。然而你依 然可以改变 tenEighty.frameRate 和 alsoTenEighty.frameRate,因为这两个常量本身不会改 变。它们并不储存这个 VideoMode 实例,在后台仅仅是对 VideoMode 实例的引用。所
以,改变的是被引用的基础 VideoMode 的 frameRate 参数,而不改变常量的值。此处常量的值是引用,该引用不可以改变。
Swift 中数组(Array)和字典(Dictionary)类型均以结构体的形式实现。然而当数组被赋予一 个常量或变量,或被传递给一个函数或方法时,其拷贝行为与字典和其它结构体有些许不同。
无论何时将一个字典实例赋给一个常量或变量,或者传递给一个函数或方法,这个字典会
即会在赋值或调用发生时被拷贝。
如果字典实例中所储存的键(keys)和/或值(values)是值类型(结构体或枚举),当赋值或调用 发生时,它们都会被拷贝。相反,如果键(keys)和/或值(values)是引用类型,被拷贝的将 会是引用,而不是被它们引用的类实例或函数。字典的键和值的拷贝行为与结构体所储存
的属性的拷贝行为相同。
如果你将一个数组(Array)实例赋给一个变量或常量,或者将其作为参数传递给函数或方法
调用,在事件发生时数组的内容不会被拷贝。相反,数组公用相同的元素序列。当你在一
个数组内修改??一元素,修改结果也会在另一数组显示。对数组来说,拷贝行为仅仅当操作有可能修改数组长度时才会发生。这种行为包括了附加
(appending),插入(inserting),删除(removing)或者使用范围下标(ranged subscript)去替换这 一范围内的元素。
在操作一个数组,或将其传递给函数以及方法调用之前是很有必要先确定这个数组是有一 个唯一拷贝的。通过在数组变量上调用 unshare 方法来确定数组引用的唯一性。(当数组赋
给常量时,不能调用 unshare 方法)
如果一个数组被多个变量引用,在其中的一个变量上调用 unshare 方法,则会拷贝此数 组,此时这个变量将会有属于它自己的独立数组拷贝。当数组仅被一个变量引用时,则不
会有拷贝发生。
存储属性、计算属性、类型属性、延迟存储属性
属性将值跟特定的类、结构或枚举关联。存储属性存储常量或变量作为实例的一部分,计
算属性计算(而不是存储)一个值。计算属性可以用于类、结构体和枚举里,存储属性只
能用于类和结构体。
这种行为是由于结构体(struct)属于值类型。当值类型的实例被声明为常量的时候,它的
所有属性也就成了常量。
属于引用类型的类(class)则不一样,把一个引用类型的实例赋给一个常量后,仍然可以修
改实例的变量属性。
延迟存储属性是指当第一次被调用的时候才会计算其初始值的属性。在属性声明前使用 @lazy 来标示一个延迟存储属性。注意:必须将延迟存储属性声明成变量(使用 var 关键字),因为属性的值在实例构造完成 之前可能无法得到。而常量属性在构造过程完成之前必须要有初始值,因此无法声明成延迟 属性。
注意:必须使用 var 关键字定义计算属性,包括只读计算属性,因为他们的值不是固定的。
let 关键字只用来声明常量属性,表示初始化后再也无法修改的值。
可以为除了延迟存储属性之外的其他存储属性添加属性监视器,也可以通过重载属性的方式 为继承的属性(包括存储属性和计算属性)添加属性监视器。
注意:willSet 和 didSet 监视器在属性初始化过程中不会被调用,他们只会当属性的值在初始化之外的地方被设置时被调用。注意:如果在 didSet 监视器里为属性赋值,这个值会替换监视器之前设置的值。
另外,在全局或局部范围都可以定义计算型变量和为存储型变量定义监视器,计算型变量跟 计算属性一样,返回一个计算的值而不是存储值,声明格式也完全一样。
注意:全局的常量或变量都是延迟计算的,跟延迟存储属性相似,不同的地方在于,全局的
常量或变量不需要标记@lazy 特性;局部范围的常量或变量不会延迟计算。
对于值类型(指结构体和枚举)可以定义存储型和计算型类型属性,对于类(class)则只能 定义计算型类型属性。值类型的存储型类型属性可以是变量或常量,计算型类型属性跟实例的计算属性一样定义成变量属性。
注意:跟实例的存储属性不同,必须给存储型类型属性指定默认值,因为类型本身无法在初
始化过程中使用构造器给类型属性赋值。
使用关键字 static 来定义值类型的类型属性,关键字 class 来为类(class)定义类型属性。
Swift 默认仅给方法的第一个参数名称一个局部参数名称;默认同时给第二个和后 续的参数名称局部参数名称和外部参数名称。
有时为方法的第一个参数??供一个外部参数名称是非常有用的,尽管这不是默认的行为。你 可以自己添加一个显式的外部名称或者用一个井号(#)作为第一个参数的前缀来把这个局 部名称当作外部名称使用。
相反,如果你不想为方法的第二个及后续的参数??供一个外部名称,可以通过使用下划线(_) 作为该参数的显式外部名称,这样做将覆盖默认行为。
结构体和枚举是值类型。一般情况下,值类型的属性不能在它的实例方法中被修改。
但是,如果你确实需要在??个具体的方法中修改结构体或者枚举的属性,你可以选择变异
(mutating)这个方法,然后方法就可以从方法内部改变它的属性;并且它做的任何改变在方
法结束时还会保留在原始结构中。方法还可以给它隐含的 self 属性赋值一个全新的实例,这 个新实例在方法结束后将替换原来的实例。
声明类的类型方法,在方法的 func 关键字之前加上关键字 class;声明结构 体和枚举的类型方法,在方法的 func 关键字之前加上关键字 static。
在类型方法的方法体(body)中,self 指向这个类型本身,而不是类型的??个实例。对于结 构体和枚举来说,这意味着你可以用self来消除静态属性和静态方法参数之间的歧义(类似 于我们在前面处理实例属性和实例方法参数时做的那样)。
一个类型方法可以调用本类中另一个类型方法的名称,而无需在方法名称前面加上类型名称 的前缀。同样,结构体和枚举的类型方法也能够直接通过静态属性的名称访问静态属性,而
不需要类型名称前缀。
根据使用场景不同附属脚本也具有不同的含义。通常附属脚本是用来访问集合(collection),
例如,Swift 的字典(Dictionary)实现了通过附属脚本来对其实例中存放的值进行存取操作。 在附属脚本中使用和字典索引相同类型的值,并且把一个字典值类型的值赋值给这个附属脚 本来为字典设值:
列表(list)或序列(sequence)中元素的快捷方式。你可以在你自己特定的类或结构体中 自由的实现附属脚本来??供合适的功能。
构造器的工作是准备新实例以供使用,并确保实例中的所有属性都拥有有效的初始化值。
不像 Objective-C,在 Swift 中,初始化器默认是不继承的,见初始化器的继承与重 写
子类可以为继承来的实例方法(instance method),类方法(class method),实例属性
(instance property),或附属脚本(subscript)??供自己定制的实现(implementation)。 我们把这种行为叫重写(overriding)。
你可以将一个继承来的只读属性重写为一个读写属性,只需要你在重写版本的属性里??供 getter 和 setter 即可。但是,你不可以将一个继承来的读写属性重写为一个只读属性。
你不可以同时??供重写的 setter 和重写的属性观察器。
你可以通过在关键字 class 前添加@final 特性(@final class)来将整个类标记为 final 的,
这样的类是不可被继承的,否则会报编译错误。
构造过程是为了使用??个类、结构体或枚举类型的实例而进行的准备过程。这个过程包含 了为实例中的每个属性设置初始值和为其执行必要的准备和初始化任务。它 们的主要任务是保证新实例在第一次使用前完成正确的初始化。类和结构体在实例创建时,必须为所有存储型属性设置合适的初始值。存储型属性的值不
能处于一个未知的状态。
只要在构造过程结束前常量的值能确定,你可以在构造过程中的任意时间点修改常量属性
的值。对??个类实例来说,它的常量属性只能在定义它的类的构造过程中修改;不能在子
类中修改。
默认构造器、逐一对象构造器、自己定制构造器
Swift 将为所有属性已??供默认值的且自身没有定义任何构造器的结构体或基类,??供一 个默认的构造器。这个默认构造器将简单的创建一个所有属性值都设置为默认值的实例。
除上面??到的默认构造器,如果“结构体”对所有存储型属性??供了默认值且自身没有??供定制的构造器,它们能自动获得一个逐一成员构造器。
构造器可以通过调用其它构造器来完成实例的部分构造过程。这一过程称为构造器代理,
它能减少多个构造器间的代码重复。
注意,如果你为??个值类型定义了一个定制的构造器,你将无法访问到默认构造器(如果
是结构体,则无法访问逐一对象构造器)。这个限制可以防止你在为值类型定义了一个更
复杂的,完成了重要准备构造器之后,别人还是错误的使用了那个自动生成的构造器。
Swift ??供了两种类型的类构造器来确保所有类实例中存储型属性都能获得初始值,它们
分别是指定构造器和便利构造器。指定构造器是类中最主要的构造器。一个指定构造器将初始化类中??供的所有属性,并根据父类链往上调用父类的构造器来实现父类的初始化。每一个类都必须拥有至少一个指定构造器。
规则1
指定构造器必须调用其直接父类的的指定构造器。
规则 2
便利构造器必须调用同一类中定义的其它构造器。
规则 3
便利构造器必须最终以调用一个指定构造器结束。
一个更方便记忆的方法是:
指定构造器必须总是向上代理
便利构造器必须总是横向代理
两段式构造过程
Swift 中类的构造过程包含两个阶段。第一个阶段,每个存储型属性通过引入它们的类的 构造器来设置初始值。当每一个存储型属性值被确定后,第二阶段开始,它给每个类一次
机会在新实例准备使用之前进一步定制它们的存储型属性。
Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造过程能顺利完成:
安全检查 1 指定构造器必须保证它所在类引入的所有属性都必须先初始化完成,之后才能将其它构造
任务向上代理给父类中的构造器。
如上所述,一个对象的内存只有在其所有存储型属性确定之后才能完全初始化。为了满足
这一规则,指定构造器必须保证它所在类引入的属性在它往上代理之前先完成初始化。
安全检查 2 指定构造器必须先向上代理调用父类构造器,然后再为继承的属性设置新值。如果没这么
做,指定构造器赋予的新值将被父类中的构造器所覆盖。
安全检查 3 便利构造器必须先代理调用同一类中的其它构造器,然后再为任意属性赋新值。如果没这 么做,便利构造器赋予的新值将被同一类中其它指定构造器所覆盖。
安全检查 4 构造器在第一阶段构造完成之前,不能调用任何实例方法、不能读取任何实例属性的值, 也不能引用 self 的值。
阶段 1
??个指定构造器或便利构造器被调用; 完成新实例内存的分配,但此时内存还没有被初始化;
指定构造器确保其所在类引入的所有存储型属性都已赋初值。存储型属性所属的内存完成
初始化;
指定构造器将调用父类的构造器,完成父类属性的初始化;
这个调用父类构造器的过程沿着构造器链一直往上执行,直到到达构造器链的最顶部;
当到达了构造器链最顶部,且已确保所有实例包含的存储型属性都已经赋值,这个实例的 内存被认为已经完全初始化。此时阶段 1 完成。
阶段 2
从顶部构造器链一直往下,每个构造器链中类的指定构造器都有机会进一步定制实例。构
造器此时可以访问 self、修改它的属性并调用实例方法等等。 最终,任意构造器链中的便利构造器可以有机会定制实例和使用 self。
假设要为子类中引入的任意新属性??供默认值,请遵守以下 2 个规则: 规则 1
如果子类没有定义任何指定构造器,它将自动继承所有父类的指定构造器。 规则 2
如果子类??供了所有父类指定构造器的实现--不管是通过规则 1 继承过来的,还是通过自 定义实现的--它将自动继承所有父类的便利构造器。
先将子类中的属性初始化—————>调用父类的构造器—————>修改子类与父类中属性的值。
如果你使用闭包来初始化属性的值,请记住在闭包执行时,实例的其它部分都还没
有初始化。这意味着你不能够在闭包里访问其它的属性,就算这个属性有默认值也不允
许。同样,你也不能使用隐式的 self 属性,或者调用其它的实例方法。
反初始化函数只适用于类类型。子类继承了父类的反初始化函数,并且在子类反初始化函数实现的最后,父类的反初始化函 数被自动调用。即使子类没有??供自己的反初始化函数,父类的反初始化函数也总是被调用。
引用计数只应用在类的实例。结构体(Structure)和枚举类型是值类型,并非引用类型,
不是以引用的方式来存储和传递的。
Swift ??供两种方法来解决强引用环:弱引用和无主引用。
对于生命周期中引用会变为 nil 的实例,使用弱引用;对于初始化时赋值之后引用再也不会
赋值为 nil 的实例,使用无主引用。
弱引用只能声明为变量类型,因为运行时它的值可能改变。弱引用绝对不能声明为常 量。
因为弱引用可以没有值,所以声明弱引用的时候必须是可选类型的。在 Swift 语言中,推荐 用可选类型来作为可能没有值的引用的类型。
无主引用只能定义为非可选类型(non-optional type)。在属性、变量前添加 unowned
关键字,可以声明一个无主引用。
两个属性都必须有值,且初始化完成后不能为 nil。这种场景下,则 要一个类用无主引用属性,另一个类用隐式展开的可选属性。
自判断链接(Optional Chaining)是一种可以请求和调用属性、方法及子脚本的过程,它的
自判断性体现于请求或调用的目标当前可能为空(nil)。如果自判断的目标有值,那么调用 就会成功;相反,如果选择的目标为空(nil),则这种调用将返回空(nil)。多次请求或调用可以被链接在一起形成一个链,如果任何一个节点为空(nil)将导致整个链失效。
类型转换在 Swift 中使用 is 和 as 操作符实现。
Any 和 AnyObject 的转换
Swift 为不确定类型??供了两种特殊类型别名: 1. AnyObject 可以代表任何 class 类型的实例。
2. Any 可以表示任何类型,除了方法类型(function types)。
扩展
Swift 中的扩展可以:
1. 添加计算型属性和计算静态属性 2. 定义实例方法和类型方法
3. ??供新的构造器
4. 定义下标
5. 定义和使用新的嵌套类型
6. 使一个已有类型符合??个接口
如果你定义了一个扩展向一个已有类型添加新功能,那么这个新功能对该类型的所有 已有实例中都是可用的,即使它们是在你的这个扩展的前面定义的。
注意:扩展可以添加新的计算属性,但是不可以添加存储属性,也不可以向已有属性添加属 性观测器(property observers)。
Protocol(协议)用于统一方法和属性的名称,而不实现任何功能。协议能够被类,枚举,结 构体实现,满足协议要求的类,枚举,结构体被称为协议的遵循者。
用类来实现协议时,使用 class 关键字来表示该属性为类成员;用结构体或枚举实现协议时, 则使用 static 关键字来表示:
协议方法支持变长参数(variadic parameter),不支持默认参数(default parameter)。
用 class 实现协议中的 mutating 方法时,不用写 mutating 关键字;用结构体,枚举
实现协议中的 mutating 方法时,必须写 mutating 关键字。
协议本身不实现任何功能,但你可以将它当做类型来使用。
使用场景:
1. 作为函数,方法或构造器中的参数类型,返回值类型
2. 作为常量,变量,属性的类型
3. 作为数组,字典或其他容器中的元素类型
委托是一种设计模式,它允许类或结构体将一些需要它们负责的功能交由(委托)给其他的类 型。
委托模式的实现很简单: 定义协议来封装那些需要被委托的函数和方法, 使其遵循者拥有
这些被委托的函数和方法。 委托模式可以用来响应特定的动作或接收外部数据源??供的数据,而无需要知道外部数据源
的类型。
当一个类型已经实现了协议中的所有要求,却没有声明时,可以通过扩展来补充协议声明:
协议合成并不会生成一个新协议类型,而是将多个协议合成为一个临时的协议,超出 范围后立即失效。
@objc 用来表示协议是可选的,也可以用来表示暴露给 Objective-C 的代码,此外,
@objc 型协议只对类有效,因此只能在类中检查协议的一致性
注意:可选协议只能在含有@objc 前缀的协议中生效。且@objc 的协议只能被类遵循。
swapTwoValues 函数和 Stack 类型可以作用于任何类型,不过,有的时候对使用在泛型函 数和泛型类型上的类型强制约束为??种特定类型是非常有用的。类型约束指定了一个必须 继承自指定类的类型参数,或者遵循一个特定的协议或协议构成。