初见swift
- 打印‘hello world'
print("hello world");
教程
- 类型
- 基础类型:
整型
Int
、浮点型Double
,Float
、布尔型Bool
、文本型String
。(还有Uint8,Int32等)
** 可以通过整型的min
和max
属性来获取对应类型的最小和最大值。**
- 集合类型:
Array
、Set
、Dictionary
。
- 特有类型:
元祖(Tuple)
可以允许创建或者传递一组数据,如可以作为函数返回值返回多个值
。
Optional 类型用于处理值缺失的情况
。
- 变量定义
- 如果代码中有不需要改变的值,请使用
let
关键字将其声明为常量
。只将需要改变的值声明为var变量
。- 类型标注:可以在一行中定义多个同样类型的变量,用
,
号分割,并在最后一个变量名后添加上类型标注
(var
+:
+空格
+类型
)即可。
var red,green,blue: Double;//示例- 命名:各种字符,包含 Unicode。常量和变量名不能包含
数学符号
、剪头
、保留的 Unicode 码位
、连线和制表符
。不能以数字
开头。** 不能将命名好的常量和变量进行互换 **。
- 输出
print(_:)
是一个用来输出的全局函数,输出的内容会在最后换行。print(_:appendNewline)
同1
,但唯一的区别是输出内容最后不会换行。子串插值
将常量名或者变量名做占位加入至长子串重,swift 会做值替换。即\(var)
或者\(let)
形式插入子串中。
- 类型安全和类型推断
- 类型安全
在编译时进行类型检查,将不匹配之类型标记为错误
。- 类型推断
当没有显式制定类型时,swift会在编译代码时推断选择合适的类型,原理就是检查你的赋值即可
。
- 数值型字面量
- 十进制:无前缀
- 二进制:前缀是
0b
- 八进制:前缀是
0o
- 十六进制:前缀
0x
- 数值类型转换
整数转换
用当前值来初始化一个期望的目标类型的新数字。
let two: Uint16 = 2_000;
let one: Uint8 = 1;
let and = two + Uint16(one);//目标常量即为Uint16类型
- ** 整数和浮点数转换 **
整数和浮点数转换必须显式制定类型
let one = 3;
let two = 0.1415926;
let pi = Double(one) + two;let pi_int = Int(pi);//反向转换
- 类型别名
类型别名
给现有类型定义另一个名字。使用typealias
关键字。
typealias scoreType = Uint16;//定义Uint16的别名
let oneScore: scoreType;//用别名定义常量
- 元组
元组
把多个值组合成一个复合值。内部的值可以是任意类型,可以有多种类型。
let http404Error = (404,"nor found");//定义一个类型为(Int,String)的元组
let (statuscode,_) = http404Error;//若只需要元组的部分值,可以用_
忽略不需要的元素。
let code = http404Error.0;//通过下标访问元组的单个元素,下标从0开始。let http200Status = (code:200,des:"ok");//定义元组时给元素命名 print("status code is \(http200Status.code)");//通过命名的元素获取值
元组在临时组织值时很有用,但不适合创建复杂的数据结构。若数据结构不是临时使用,请使用
类
或者结构体
。
- 可选类型
Optional来处理值缺省的情况:
有值=x
、无值=nil
。示例:String有一个
toInt
方法,但并不能保证所有String都能转换成整型,故:
let string = "123";
let num: Int? = string.toInt();
上述成功返回Int
;但有可能失败,故?
保证返回值只可能是Int
或什么也没有
。let words: Int? = nil;
**
nil
不能用于非可选的常量和变量。若代码中有常量
或变量
需处理值缺失
的情况,请将其声明为可选类型
。
nil
在swift中不是空指针,而是一个确定的值
,用来表示值缺失
。
- if语句以及强制解析
可选值的_强制解析
当确定可选类型包含值时,可在可选值后面加一个!
来获取值。即表示该可选值有值,可使用之
。若无可选值,则会运行时错误。
if let oneString: Int? = upString{
print("the string is (oneString!)");
}可选绑定
判断可选类型是否包含值。若包含,则赋给一个临时常量/变量;多用在if
和while
语句中来对可选类型的值进行判断并赋值给一个常量/变量。示例代码如上- 隐式解析可选类型:略。
- 错误处理
错误处理
应对程序执行中出错的条件。抛出->捕捉->传递错误
//1. 一个函数可通过在声明中添加throws
关键字来抛出错误消息,并且在其前置try
。
func canThrowAnError() throws{
//可能会抛出错误(当遇到错误条件,其能报错;调用它的地方能抛出错误并合理处理)
}//2. `do`的声明创建了一个新的包含作用域,使得错误能被传播到一个或更多`catch`从句。 do{ try canThrowAnError()//3. ~ //没有错误消息抛出 do some thing }catch Error.outofindex{//4. ~ //有一个具体错误消息抛出 } }catch{ //有一个错误消息抛出 }
- 断言
断言
若某个条件不满足,代码可能无法继续执行,此时触发一个断言
来结束代码运行并通过调试来找到问题所在。- 可以使用全局
assert
函数书写一个断言,当表达式=false是,触发断言。示例如下:
let age = -3;
assert(age>=0,"年龄不能小于零!");//因为age<0,故触发断言,终止运行代码- 何时使用断言:
- 下标索引太小或太大
- 函数传值可能非法的验证
- 可选值现在是nil,后面代码中需要一个非nil值
- 断言可导致应用终止运行,故尽量让非法条件不出现;在app发布前断言非法条件,有助于快速发现问题。
** --- **
基本运算符#
- 运算符:
+
、&&
、++i,i++
、a=b
、+-*/
、%
、a..<b
、a...b
、-a,!b
、a?b:c
、===
、!==
、a??b
。
a=b
赋值运算符不返回值。- 允许对浮点数进行取余运算
%
-9%4=-1;9%-4=1;9%4=1;
8%3.5=0.5;//浮点数取余,得 Doublea..<b
、a...b
区间取值:a<=i<b
、a<=i<=b
。+
可用于 String 拼接。如"hello,"+"world"
输出hello,world
。===
、!==
恒等,判断2个对象是否引用同一个实例。a ?? b
空合运算符,等价于(a != nil) ? a! : b
。
条件:a 必须是 Optional 类型;b 必须和 a 存储值类型一致。
字符串和字符#
- 字符串
String
是字符Character
的有序集合。 -
字符串字面量
代码中一段预定义
的字符串值。"**"用于为常量和变量提供初始值。 -
初始化空字符串
将空字符串字面量作为初始值,赋给变量,亦可初始化新的String
实例。
var emptyString = "";//空子串字面量 var emptyString2 = String();//初始化方法,空子串字面量
-
字符串可变性
即将特定字符串分配给一个var
并对其修改;或分配给一个let
并保证其不被修改。OC
中是通过NSString
和NSMutableString
来实现。 -
String
是值类型。let,var
赋值操作,或在func
传递时,进行值copy。即创建新副本传递。
但 swift 编译器做了优化,除非必要,一般不会实际复制。 -
字符使用
通过for-in
循环遍历String
中的Character
属性,获取每一个字符的值。
for character in "string"{
let oneCharacter: Character = character;//建立独立的字符
} -
字符串和字符连接
可以通过+
、+=
、亦可通过append
将字符附加到一个String
的尾部。 -
字符串插值
类似于"hello,\(X)"
,其中X可以是var
、let
、字面量
、表达式
。 -
Unicode
swift中的字符串
和字符
是完全兼容Unicode
标准的。
Unicode
码位范围是U+0000
`U+D7FF`或者`U+E000`U+10FFFF
。不包括代理项码位:U+D800
~U+DFFF
。 -
字符串字面量的特殊字符
包括\0
空字符、\\
反斜线、\t
水平制表符、\n
换行符、\r
回车符、\"
双引号、\'
单引号。
Unicode标量:写成\u{n}
,其中n
为任意1~8位十六进制的Unicode码。
let one = ""yes"is yes";//"yes" is yes
let heart = "\u{2665}";//U+2665 可扩展的字形群集
每一个swift的
Character
类型代表一个可扩展字形群。
- 计算字符数量
调用
count(-:)
let string: String = "hello,world";
print(count:(count(string)));//
count(_:)
返回的字符数量并不总是和NSString的length相同
,原因便是前者计算可扩展字形群集。
- 访问和修改字符串
通过String的
属性
、方法
、下标
语法 访问和读取。
- 字符串索引:string.index<->对应着字符串中的每个字符的位置。不可用
Int
做索引
startIndex
、endIndex
分别对应字符串的第一个字符、最后位置。endIndex和子串长度相等,并非最后一个字符
* index的方法`predecessor()`、`successor()`分别得到当前索引的前一个、后一个索引。`advance(start:n:)`获取特定索引。
* 全局函数`indices(string)`获取一个涵盖全部索引的范围(Range),以在字符串中访问分立的字符。
- 插入和删除
insert()_:anIndex:
在字符串指定索引插入一个字符
。
var sayHello = "hello";
sayHello.insert(",",anIndex:sayHello.endIndex);//sayHello目前是"hello,"
splice(_:atIndex:)
在字符串的指定索引插入一个字符串
。
sayHello.insert("you!",anIndex:sayHello.endIndex);//sayHello目前是"hello,you!"
removeAtIndex(_:)
在字符串指定索引删除一个字符
。
sayHello.removeAtIndex(sayHello.endIndex.predecessor());//sayhello目前是:"hello,you".(sayhello删除前有一个换行符)
removeRange(_:)
在字符串指定索引删除一个子字符串
。
let range = advance(sayHello.endIndex,-4)..<sayHello.endIndex;//a<=i<b
sayHello.removeRange(range);//sayhello目前是:"hello".
比较字符串
** Swift 提供了三种方式比较文本值:字符串字符相等
、前缀相等
、后缀相等
。**字符串/字符相等:使用
==
和!=
判断是否相等前缀/后缀相等:使用字符串方法:
hasPrefix(_:)
、hasSuffix(_:)
来判断字符串是否拥有特定的前缀/后缀。字符串的Unicode表示形式
** Swift提供的访问字符串Unicode表示形式的方式:String.UTF8View
for codeUnit in myname.utf8 { print("\(codeUnit)"); }
集合类型
**Swift提供了Arrays
、Sets
、Dictionaries
三种基本的集合类型。
Array
是有序数据集。
Set
是无序无重复数据集。
Dictionary
是无序的key-value对
数据集。
Arrays
- 定义
Array<T>
:其中T
是数组中唯一允许存在的数据类型。亦可写作[T]
简单语法。
创建空数组
var cells = Int;//构造语法带有默认值的数组
创建特定大小,并所有数据都被默认值的构造方法
var doubles = Double;//Doubel数组:[.0,.0,.0]通过2个数组相加创建新数组
同类型数组使用
+运算符
var otherDoubles = Double;
var newDoubles = doubles + otherDoubles;//新数组:[.0,.0,.0,1.0,1.0]用字面量构造数组
用一个或多个数值构造数组的方式
var names: [String] = ["zhou","wu","zheng","wang"];
- 访问和修改数组
通过数组的方法
和属性
、下标语法
进行访问和修改数组操作。
cells.count
获取数据数量
cells.isEmpty
检测数组count是否为零
cells.apped(newCell)
在数组后面添加新的元素
cells +=[newCell1,newCell2]
在数组后面追加n个元素
cells[1]
= "newcell";//直接使用下标语法获取和修改元素
cells.insert(newCell3,anIndex:0);
使用insert(_:anIndex:)
在某个索引值前
添加元素
cell = cells.removeAtIndex(0);
//使用removeAtIndex
移除特定索引值中
的元素,并返回该元素
- 遍历Array
for cell in cells{};
//使用for-in
遍历之
for(index,cell) in cells.enumerate(){};
//使用enumerate()
返回一个元组(索引值+元素)
** Set **
-
Set
用来存储同类型的
、无序的
、单一的
元素集合。附加:可哈希的
-
Set<T>
,T
表示Set
允许存储的类型。无等价的简化形式。
- 创建和构造一个空Set:
var sexuals = Set<String>();//构造器创建特定类型空Set- 空数组字面量创建空Set
sexuals.insert("man");
sexuals = [];//依然是空的Set- 数组字面量创建Set.
并且可以使用简化形式写一个或者多个值作为Set元素
var sexuals: Set<String> = ["man","woman"];
- 访问和修改Set
sexuals.count
获取set的数量
sexual.isEmpty
检查set是否为空set
sexuals.insert("unkown");
//insert(_:)
添加新元素
sexuals.remove("unkown");
//remove(_:)
删除元素,并返回该元素;若无此值,返回nil。
sexuals.contains("man");
//contains(_:)
是否包含特定元素
- 遍历set
for sexual in sexuals{}
//
for sexual in sexuals.sort(){}
//set.sort()
方法,返回一个排序的集合
- 完成set操作
图示显示了集合
a
和b
各种操作的结果
- 使用
intersect(_:)
方法根据两个集合中都包含的值创建的一个新的集合。- 使用
exclusiveOr(_:)
方法根据值在一个集合中但不在两个集合中的值创建一个新的集合。- 使用
union(_:)
方法根据两个集合的值创建一个新的集合。- 使用
subtract(_:)
方法根据不在该集合中的值创建一个新的集合。三个集合
a
b
和c
,以及通过悬浮区域表述集合间共享的元素
- 使用
是否等
运算符==
来判断两个集合是否包含全部相同的值。- 使用
isSubsetOf(_:)
方法来判断一个集合中的值是否也被包含在另外一个集合中。- 使用
isSupersetOf(_:)
方法来判断一个集合中包含的值是另一个集合中所有的值。- 使用
isStrictSubsetOf(_:)
或者isStrictSupersetOf(_:)
方法来判断一个集合是否是另外一个集合的子集合或者父集合并且和特定集合不相等。- 使用
isDisjointWith(_:)
方法来判断两个结合是否不含有相同的值。
Dictionary
-
字典
是存储多个同类型
、无序
的值的容器。每个值都关联一个键,作为值的标识符。Key遵循哈希协议 -
Dictionary<Key,Value>
定义,亦可[Key:Value]
快捷定义。推荐后者
创建空字典
var nameDic = [Int: String()];//空的[Int: String]字典字典确定类型后,使用
[:]
创建空字典
var nameDic[1] = "chow";
nameDic = [:];
- 字典字面量
即是一种将写1个或多个KV对作Dic集合的途径
var nameDic:[Int: String] = [1:"chow",2,"pan"];
- 读取和访问字典
略 - 字典遍历
略
控制流
- 循环:
for
、for-in
、 - 条件:
if
、switch
for循环
for-in
对一个集合里面每个元素执行一系列语句。区间
、数组元素
、字符串中字符
等。
for index in 1...5{};//index是每次循环遍历开始时自动赋值的常量let
for _ in 1...5{};//若不需要每一项的值,用-替代之,来忽略对值的访问
for
重复执行一系列语句直至达到某条件即可。一般通过计数器手动递增
for var index=0;index<3;++index{}//不需括号while循环
while
、repeat=while
每次在循环开始时计算条件是否符合
whilecondition
{statements
}
repeat {statements
} whilecondition
;//类似于do-while
do-while
每次在循环结束时计算条件是否符合条件语句
if
条件较为简单的情况下
ifstatements
{}else{}
switch
条件较复杂,较多时的情况下。会尝试把某个值与若干模式进行匹配。
switch
必须是完备的。即每一个可能的值都必须至少有一个case
与之对应。默认分支
default
必须在最后。无需显式展示
break
语句,即不是必须的。每个
case分支
后必须至少一条语句,但一个case
可包含多个模式:case v1,v2:
case分支
模式可以是一个值区间:case 1..25:
case分支
模式可以是一个元组:point坐标:case (_2...8,4):
或者case (_2,_):
_
匹配所有 值
值绑定:``case分支
模式允许将匹配的值绑定到一个临时let
或var
上
where:``case分支
模式可以使用where
来判断额外的条件let num = 10; switch num { case 1: respond; case 2,3: respond; case 4..<6: respond; case let seven where x == 7://where respond; case let other_num://等同于default了。 respond; case default: break; }
控制转移语句
-
控制转移语句
用于改变代码的执行顺序,实现代码的跳转:
continue
、break
、fallthrough
、return
、throw
。
continue
结束本次循环,开始循环体下一个循环break
结束整个循环体或switch的case代码块,跳转至循环体外或switch外的第一行代码。fallthrough
贯穿:一个case分支完成后,swift默认是不会自动落入下一个case分支中的,case末尾使用fallthrough
来使其能实现贯穿,即落入下一个case分支。fallthrough
不会检查落入的case匹配条件
- 带
tag
的语句
循环体
和switch代码块
均可以嵌套使用,以构造复杂的控制流。因此,显式的指明break
和continue
影响的是哪一层循环体非常重要。
实现:可以用tag
来标记一个循环体
和switch代码块
,使用break
和continue
时,带上该tag
即可控制其代表的循环体
和switch代码块
。
语法:tagName: while condition {statements}
- 提前退出
guard
和if
相似,区别在于guard
语句总是有个else
分句。?,不太清晰其机制,待查。
检测API是否可用
Swift 有内置支持去检查接口的可用性的,这可以确保我们不会不小心地使用对于当前部署目标不可用的API。
使用一个可用性条件在一个if
或guard
语句中去有条件的执行一段代码,这取决于我们想要使用的API是否在运行时是可用的。if #available(iOS 9,OSX 10.10, *){ // 在 iOS 使用 iOS 9 APIs , 并且在 OS X 使用 OS X v10.10 APIs }else { // 回滚至早前 iOS and OS X 的API }
以上可用性条件指定在iOS, if 段的代码仅仅在iOS9及更高可运行;在OS X,仅在OS X v10.10及更高可运行。最后一个参数, * ,是必须的并且指定在任何其他平台上, if 段的代码在最小可用部署目标指定项目中执行。
统一代码:
if #available(`platform name` `version`, `...`, *) { `statements to execute if the APIs are available` } else { `fallback statements to execute if the APIs are unavailable` }
函数
- 定义和调用
定义
func sayHi(name: String)->String{
let greeting = "hi,(name)";
return greeting;
}调用
sayHi("pan");
- 函数参数和返回值
参数
和返回值
极为灵活,可以定义任何类型的函数。多重输入参数,即多个参数
无参函数
多参量函数:
当调用超过一个参数的函数时,第一个参数后的参数根据其对应的参数名称标记无返回值函数:无
->
和返回类型
func sayGoodbye(name: String){print("goodbye (name)");}
严格上来说,虽然没有返回值被定义, sayGoodbye(_:) 函数依然返回了值。没有定义返回类型的函数会返回特殊的值,叫 Void 。它其实是一个空的元组(tuple),没有任何元素,可以写成 () 。多重返回值函数
用元组
类型(亦可做可选
)让多个值作为一个复合值从函数中返回。
func minMax(array:[Int])->(min: Int,max: Int)?{
if(array.isEmpty) return nil;
...
return (minNum,maxNum);
}函数参数名称
函数参数均有一个外部参数名和一个本地参数名;
外参:标记传递给函数调用的参数
内参:实现函数时使用
func someFunction(firstParameterName: Int,secondParameterName: Int) {} someFunction(1,secondParameterName:2);//调用
一般情况下,第一个参数省略其外部参数名,第二个以后的参数使用其本地参数名作为自己的外部参数名.所有参数需要有不同的本地参数名,但可以共享相同的外部参数名.
- 指定外部参数名
在本地参数名前指定外部参数名,中间以空格隔开。若提供了外部参数名,在函数调用时,必须使用外参名。
func sayHello(to man: String,and otherMan:String){} sayHello(to:"pan",and:"you");//调用
- 忽略外部参数名
若不想为第二个以及以后的参数设置参数名,用_
代替参数名即可。
func sayHello(man: String,_ otherMan:String){} sayHello("pan","you");//调用.因第一个参数可忽略外参
- 默认参数值
可以为参数定义默认值,默认值被定义后,调用函数时可以忽略该参数。
func sayHello(man: String,_ otherMan:String = "you"){} sayHello("pan");//调用.第二个参数默认为"you"
将带有默认值的参数放在函数参数列表的最后。这样可以保证在函数调用时,非默认参数的顺序是一致的,同时使得相同的函数在不同情况下调用时显得更为清晰。
- 可变参数
可变参数:可以接受0个或多个值。在变量类型后面加(...)
来定义可变参数。
func sayHello(men: String...){ for man in men{} } sayHello("pan","you","she");//调用
传入可变参数的值在函数体内当做这个类型的一个数组。
最多可以有一个可变参数,和它必须出现在参数列表中.
** 如果你的函数有一个或多个参数有默认值,还有一个可变的参数,将可变参写在参数列表的最后。**
常量参数和变量参数
函数参数默认是常量
。此时在函数体中更改之,会导致编译错误。
可以通过指定参数为变量参数,避免在函数中定义新的变量。
在参数名前加var
定义变量参数:
func sayHello(var man: String,_ otherMan:String = "you") {可以修改 man的值} let man_let = "pan"; sayHello(man_let);//调用.第二个参数默认为"you"
对变量参数所进行的修改在函数调用结束后便消失了,并且对于函数体外是不可见的。变量参数仅仅存在于函数调用的生命周期中。
- 输入输出参数
若想要一个函数可以修改参数的值,并且函数体运行结束后,修改仍然存在。将该参数定义为输入输出参数
即可。
参数定义:inout
加在参数定义前即可。该参数传入函数,在函数体中修改,然后传出函数,替换原来的值。
func swap(inout a: Int,inout b: Int){ let tempInt = a; a = b; b = tempInt; } var aInt = 3,bInt = 5; swap(&aInt,&bInt);//调用
- 只能将变量作为输入输出参数。你不能传入常量或者字面量(literal value),因为这些量是不能被修改的。
- 当传入的参数作为输入输出参数时,需要在参数前加 & 符,表示这个值可以被函数修改。
- 输入输出参数不能有默认值,而且可变参数不能用 inout 标记。如果你用 inout 标记一个参数,这个参数不能被 var 或者 let 标记。
- 函数类型
函数类型 = 参数类型 + 返回值类型。如(Int,Int)->Int;()->Void
- 使用函数类型
函数类型同其它类型一样使用。
var mathFunc = (Int,Int)->Int = addTwoInts;
matnFunc(2,4);//调用
注:相同匹配类型的不同函数亦可被赋值给同一个变量
- 函数类型作为参数类型
此时,可以将函数的部分实现交由函数的调用者。
func resultFunc(mathFunc: (Int,Int)->Int,_ a: Int,_ b: Int){
print("result:\(mathFunc(a,b)");
}
resultFunc(mathFunc,3,5);//调用
- 函数类型作为返回类型
即在函数返回->
之后写一个完整的函数类型。
//func 1
func stepForward(input: Int)->Int{
return input + 1;
}
//func 2
func stepBackward(input: Int)->Int{
return input - 1;
}
//根据参数来返回上述函数的其中一个
func chooseStepFunc(isStepBack: Bool)->(Int)->Int{
return isStepBack? stepBackward:stepForward;
}
引用时,返回的函数的引用
保存在了var或let中。
- 嵌套函数
把函数定义在别的函数体中,即为嵌套函数。
func chooseStepFunc(isStepBack: Bool)->(Int)->Int{
func stepForward(input: Int)->Int{return input + 1;}
func stepBackward(input: Int)->Int{return input - 1;}
return isStepBack?stepBackward:stepForward;
}
闭包
闭包
是自包含的函数代码块,可以被传递和使用。与OC中的block
类似。
-
闭包
可以捕获和存储其所在上下文中任意let和var的引用。即闭合并包裹着这些let和var,故为闭包。
swift会为你管理在捕获过程中涉及到的所有内存操作。 - 全局函数和嵌套函数实际上也是特殊的闭包。
- 闭包的三种形式:
- 全局函数:是一个
有名字
但不会捕获任何值
的闭包。 - 嵌套函数:是一个
有名字
并可以捕获其封闭函数域内值
的闭包。 - 闭包表达式:是一个利用轻量级语法所写的
可以捕获其上下文中let和var
的匿名闭包。
- 全局函数:是一个
- 闭包的优化:
- 利用上下文推断
- 隐式返回单表达式闭包,即单表达式闭包可以省略return关键字
- 参数命缩写
- 尾随闭包语法
- 闭包表达式
嵌套函数
是一个在复杂函数中方便进行命名和定义自包含代码模块
的方式。
闭包表达式
利用简洁语法构建内联闭包的方式。
示例:Swift 标准库提供了名为 sort 的函数,会根据您提供的用于排序的闭包函数将已知类型数组中的值进行排序。 一旦排序完成, sort(:) 方法会返回一个与原数组大小相同,包含同类型元素且元素已正确排序的新数组。原数组不会被 sort(:) 方法修改。
1. 参数是一个普通函数的方式 let names = ["chow","alex","barry","danel"];//逆序排序前的数组 func backwards(s1: String,s2: String)->Boll{ return s1>s2; } var reversed = names.sort(backwards);//调用闭包,输出: ["danel","chow","barry","alex"] 2. 闭合表达式语法 { (parameters)->returnType in statements; }
let reversed = names.sort( { (s1: String,s2: String)-> Bool in
return s1>s2;
})
闭包的函数体部分由关键字 in 引入。 该关键字表示闭包的参数和返回值类型定义已经完成,闭包函数体即将开始。
- 根据上下文推断类型:
reversed = names.sort({s1,s2 in return s1>s2})
- 单表达式闭包隐式返回
单行表达式闭包可以通过隐藏return
关键字来隐式返回单行表达式的结果。
reversed = names.sort({s1,s2 in s1>s2})
- 参数名称缩写
Swift 自动为内联函数提供了参数名称缩写功能,您可以直接通过 $0 , $1 , $2 来顺序调用闭包的参数。
in
关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成。
reversed = names.sort({$0>$1})
尾随闭包
将一个很长的闭包表达式作为函数最后一个参数,可以使用
尾随闭包
来增强函数的可读性-
尾随包:是一个书写在函数括号之后的
闭包表达式
,函数支持将其作为最后一个参数调用func funcTakeClosure(closure:()->Void){函数体} //不使用尾随包的调用 funcTakeClosure( {闭包主体} ) //使用尾随包 funcTakeClosure(){ 闭包主体 }
//示例
reversed = names.sort(){$0>$1} let digitNames = [ 0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four", 5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: > "Nine" ] let numbers = [16, 58, 510] let string = numbers.map() { (var number)->String in var output = ""; while number>0{ output = digitNames[number%10]! + output; number /= 10; } return output; } //输出结果:["OneSix", "FiveEight", "FiveOneZero"]
- 捕获值
- 闭包可以在其定义的上下文中捕获常量或变量。 即使定义这些常量和变量的原域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。
- Swift最简单的闭包形式是嵌套函数,也就是定义在其他函数的函数体内的函数。 嵌套函数可以捕获其外部函数所有的参数以及定义的常量和变量。
示例:
func makeIncrementor(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementor() -> Int {
runningTotal += amount
return runningTotal
}
return incrementor
}
-
incrementer
函数并没有任何参数,但是在函数体内访问了runningTotal
、amount
。这是因为其通过捕获在包含它的函数体内已经存在的runningTotal
、amount
的引用而实现。捕获了变量引用,保证了runningTotal
、amount
变量在调用完makeIncrementor
函数后不会消失,并且保证了在下一次执行incrementor
函数时,runningTotal
可以继续增加。 - 注意: 为了优化,Swift可能会捕捉和保存一份对值的拷贝,如果这个值是不可变或是在闭包外的。 Swift同样负责被捕捉的所有变量的内存管理,包括释放不被需要的变量。
let incrementByTen = makeIncrementor(forIncrement: 10)
incrementByTen()// 返回的值为10
incrementByTen()// 返回的值为20
incrementByTen()// 返回的值为30
闭包是引用类型
-
枚举
枚举
一个通用类型的一组相关值。
枚举成员
对应一个原始值
,该值可以是:字符串
、字符
、整型
或者浮点数
。并且也可以不必为其提供原始值。- 枚举语法
enum Sexual{ case man case woman,unkown } //引用:一旦确定是enum类型后,可以使用缩写语法 var you = Sexual.man; //change you = .woman;
枚举成员被创建时并不赋予一个默认的Int值。
- 匹配枚举值和switch语句
Sexual you = .man;
switch you{
case .man:
//is a man
case .woman:
//is a woman
}
* `switch`在判断枚举值时,必须穷举所有可能的值。
* 实在不需要部分值时,使用`default:`来涵盖一部分。
- 相关值(略)
- 原始值
相关值的另一种选择:枚举成员可以被默认值(原始值
)赋值,其中原始值
具有相同的类型。
枚举成员存储ASCII码示例
enum Sexual: Int{
case man = 1
case woman = 2
case unkown = 0
}
//此处原始值 类型为Int,且每个原始值在枚举声明中是唯一性的。
原始值的隐式赋值:其为Int或String类型时,不需要为每一个成员赋值,将会自动赋值的。
如:当使用整数作为原始值时,隐式赋值的值依次递增1。如果第一个值没有被赋初值,将会被自动置为0。
如:当使用字符串作为枚举类型的初值时,每个枚举成员的隐式初值则为该成员的名称。
使用枚举成员的rawValue
属性访问该枚举成员的原始值。
- 使用原始值初始化
枚举
、枚举变量
如果在定义枚举类型的时候使用了原始值,那么将会自动获得一个初始化方法,这个方法将原始值类型作为参数,返回枚举成员或者 。你可以使用这种初始化方法来创建一个新的枚举变量。
let se = Sexual(rawValue: 2);//se为Sexual?,且se = .woman
并非所有可能的 值都可以找到一个匹配的行星。正因为如此,构造函数可以返回一个可选的枚举成员。
- 递归枚举
递归枚举
是一种枚举类型,在它的枚举中,有一个或多个枚举成员拥有该枚举其它成员最为相关值的情况。
使用递归枚举时,编译器会插入一个中间层。你可以在枚举成员前加上 indirect 来表示这成员可递归;也可以在枚举类型开头加上 indirect 关键字来表示它的所有成员都是可递归的
类和结构体
见swift笔记2