自从swift流行起来之后,一直没有认真学习过Swift,还是杀下心来按照Swift AppleDoc系统学习一次。依照传统,先用Swift在屏幕上打印“Hellow, world!”
|
|
如果你曾使用 C 或者 Objective-C 写代码,那么 Swift 的语法不会让你感到陌生——在 Swift 语言当中,这一行代码就是一个完整的程序!你不需要为每一个功能导入单独的库比如输入输出和字符串处理功能。写在全局范围的代码已被用来作为程序的入口,所以你不再需要 main()
函数。同样,你也不再需要在每句代码后边写分号。
简单值
使用 let
来声明一个常量,用 var
来声明一个变量。常量的值在编译时并不要求已知,但是你必须为其赋值一次。这意味着你可以使用常量来给一个值命名,然后一次定义多次使用。
不需要总是显式地写出类型,在声明一个常量或者变量的时候直接给它们赋值就可以让编译器推断它们的类型。如果初始值并不能提供足够的信息(或者根本没有提供初始值),就需要在变量的后边写出来了,用冒号分隔。
|
|
值绝对不会隐式地转换为其他类型。如果你需要将一个值转换为不同的类型,需要使用对应的类型显示地声明。
|
|
其实还有一种更简单的方法来把值加入字符串:将值写在圆括号里,然后再在圆括号的前边写一个反斜杠 ( \) ,举个栗子:
|
|
使用方括号( [])来创建数组或者字典,并且使用方括号来按照序号或者键访问它们的元素。
|
|
数组和字典可以有两种初始化方式分别如下:
|
|
控制流
使用 if
和 switch
来做逻辑判断,使用 for-in
, for
, while
,以及 repeat-while
来做循环,不在强制使用圆括号,但仍旧需要使用花括号来括住代码块。
|
|
if语句当中,条件必须是布尔表达式,注意和Objective-C的区别。
当然要达到Objective-C的if
可判断布尔表达式和真假值,你可以一起使用 if
和 let
来操作那些可能会丢失的值。这些值使用可选项表示。可选的值包括了一个值或者一个 nil来表示值不存在。在一个值的类型后边使用问号( ?)来把某个值标记为可选的。
|
|
如果可选项的值为 nil
,则条件为 false
并且花括号里的代码将会被跳过。否则,可选项的值就会被展开且赋给 let后边声明的常量,这样会让展开的值对花括号内的代码可用。
高级用法:
|
|
另一种处理可选值的方法是使用 ??
运算符提供默认值。如果可选值丢失,默认值就会使用。
|
|
Switch 选择语句支持任意类型的数据和各种类型的比较操作——它不再限制于整型和测试相等上。在执行完 switch 语句里匹配到的 case 之后,程序就会从 switch 语句中退出。执行并不会继续跳到下一个 case 里,所以完全没有必要显式地在每一个 case 后都标记 break 。
|
|
注意 let
可以用在模式里来指定匹配的值到一个常量当中。
你可以使用 for-in来遍历字典中的项目,这需要提供一对变量名来储存键值对。字典使用无序集合,所以键值的遍历也是无序的。
|
|
使用 while来重复代码快直到条件改变。循环的条件可以放在末尾,这样可以保证循环至少运行了一次。
|
|
使用 ..<来创建一个不包含最大值的区间,使用 … 来创造一个包含最大值和最小值的区间。
|
|
函数和闭包
使用 func
来声明一个函数。通过在名字之后在圆括号内添加一系列参数来调用这个方法。使用 ->来分隔形式参数名字类型和函数返回的类型。
默认情况下,函数使用他们的形式参数名来作为实际参数标签。在形式参数前可以写自定义的实际参数标签,或者使用 _ 来避免使用实际参数标签。
|
|
使用元组来创建复合值——比如,为了从函数中返回多个值。元组中的元素可以通过名字或者数字调用。
|
|
注意:返回值元素取成员可直接用成员名称,也可以用以元素成员从下标为0顺序的取值例如
statistics.2
函数同样可以接受多个参数,然后把它们存放进数组当中。
|
|
函数可以内嵌。内嵌的函数可以访问外部函数里的变量。你可以通过使用内嵌函数来组织代码,以避免某个函数太长或者太过复杂。
|
|
同时,函数是一等类型,这意味着函数可以把函数作为值来返回。
|
|
函数也可以把另外一个函数作为其自身的参数。
|
|
闭包:函数其实就是闭包的一种特殊形式:一段可以被随后调用的代码块。闭包中的代码可以访问其生效范围内的变量和函数,就算是闭包在它声明的范围之外被执行——你已经在内嵌函数的栗子上感受过了。你可以使用花括号({ })
括起一个没有名字的闭包。在闭包中使用 in
来分隔实际参数和返回类型。
|
|
你有更多的选择来把闭包写的更加简洁。当一个闭包的类型已经可知,比如说某个委托的回调,你可以去掉它的参数类型,它的返回类型,或者都去掉。
|
|
你可以调用参数通过数字而非名字——这个特性在非常简短的闭包当中尤其有用。当一个闭包作为函数最后一个参数出入时,可以直接跟在圆括号后边。如果闭包是函数的唯一参数,你可以去掉圆括号直接写闭包。
|
|
对象和类
通过在class
后接类名称来创建一个类。在类里边声明属性与声明常量或者变量的方法是相同的,唯一的区别的它们在类环境下。同样的,方法和函数的声明也是相同的写法。
|
|
通过在类名字后边添加一对圆括号来创建一个类的实例。使用点语法来访问实例里的属性和方法。
|
|
为Shape
类的添加一个重要的东西:一个用在创建实例的时候来设置类的初始化器。使用 init来创建一个初始化器。
|
|
注意使用 self
来区分 name
属性还是初始化器里的 name
参数。创建类实例的时候给初始化器传参就好像是调用方法一样。每一个属性都需要赋值——要么在声明的时候(比如说 numberOfSides
),要么就要在初始化器里赋值(比如说 name),使用 deinit
来创建一个反初始化器,如果你需要在释放对象之前执行一些清理工作的话。
子类的方法如果要重写父类的实现,则需要使用 override
——不使用 override
关键字来标记则会导致编译器报错。编译器同样也会检测使用 override
的方法是否存在于父类当中。
|
|
除了存储属性,你也可以拥有带有 getter 和 setter 的计算属性。
|
|
在 perimeter
的 setter
中,新值被隐式地命名为 newValue
。你可以提供一个显式的名字放在 set
后边的圆括号里。
设置一个新值的前后执行代码,使用 willSet
和 didSet
。比如说,下面的类确保三角形的边长始终和正方形的边长相同.
|
|
当你操作可选项的值的时候,你可以在可选值后边使用 ?
比如方法,属性和下标脚本。如果 ?
前的值是 nil
,那 ?
后的所有内容都会被忽略并且整个表达式的值都是 nil
。否则,可选项的值将被展开,然后 ?
后边的代码根据展开的值执行。在这两种情况当中,表达式的值是一个可选的值。
|
|
枚举和结构体
使用 enum
来创建枚举,类似于类和其他所有的命名类型,枚举也能够包含方法
|
|
默认情况下,Swift 从零开始给原始值赋值后边递增,但你可以通过指定特定的值来改变这一行为。在上边的栗子当中,原始值的枚举类型是 Int,所以你只需要确定第一个原始值。剩下的原始值是按照顺序指定的。你同样可以使用字符串或者浮点数作为枚举的原始值。使用 rawValue 属性来访问枚举成员的原始值。
使用 init?(rawValue:) 初始化器来从一个原始值创建枚举的实例。
|
|
枚举成员的值是实际的值,不是原始值的另一种写法。事实上,在这种情况下没有一个有意义的原始值,你根本没有必要提供一个。
|
|
注意有两种方法可以调用枚举的 hearts
成员:当给 hearts
指定一个常量时,枚举成员 Suit.Hearts
会被以全名的方式调用因为常量并没有显式地指定类型。在 Switch
语句当中,枚举成员可以通过缩写的方式 .hearts
被调用,因为 self
已经明确了是 suit
。总之你可以在任何值的类型已经明确的场景下使用使用缩写。
|
|
enum
高阶用法:如果枚举拥有原始值,这些值在声明时确定,就是说每一个这个枚举的实例都将拥有相同的原始值。另一个选择是让case与值关联——这些值在你初始化实例的时候确定,这样它们就可以在每个实例中不同了。比如说,考虑在服务器上请求日出和日落时间的case,服务器要么返回请求的信息,要么返回错误信息。
|
|
注意现在日出和日落时间是从 ServerResponse
值中以switch case
匹配的形式取出的
使用 struct
来创建结构体。结构体提供很多类似与类的行为,包括方法和初始化器。其中最重要的一点区别就是结构体总是会在传递的时候拷贝其自身(传值),而类则会传递引用。
|
|
协议和扩展
使用 protocol
来声明协议。类,枚举以及结构体都兼容协议。
|
|
注意使用 mutating
关键字来声明在 SimpleStructure
中使方法可以修改结构体。在 SimpleClass
中则不需要这样声明,因为类里的方法总是可以修改其自身属性的。
Extension: 使用 extension
来给现存的类型增加功能,比如说新的方法和计算属性。你可以使用扩展来使协议到别处定义类型,或者你导入的其他库或框架。
|
|
你可以使用协议名称就像其他命名类型一样——比如说,创建一个拥有不同类型但是都遵循同一个协议的对象的集合。当你操作类型是协议类型的值的时候,协议外定义的方法是不可用的。
|
|
尽管变量 protocolValue
有 SimpleClass
的运行时类型,但编译器还是把它看做 ExampleProtocol
。这意味着你不能访问类在这个协议中扩展的方法或者属性
错误处理
你可以用任何遵循 Error
协议的类型来表示错误。使用 throw
来抛出一个错误并且用 throws
来标记一个可以抛出错误的函数。如果你在函数里抛出一个错误,函数会立即返回并且调用函数的代码会处理错误。
|
|
有好几种方法来处理错误。一种是使用 do-catch
。
处理方式1:在 do
代码块里,你用 try
来在能抛出错误的函数前标记。在 catch
代码块,错误会自动赋予名字 error
,如果你不给定其他名字的话。
|
|
处理方式2:你可以提供多个 catch
代码块来处理特定的错误。你可以在 catch
后写一个模式,用法和 switch
语句里的 case
一样。
|
|
处理方式3:另一种处理错误的方法是使用 try?
来转换结果为可选项。如果函数抛出了错误,那么错误被忽略并且结果为 nil
。否则,结果是一个包含了函数返回值的可选项。
|
|
defer: 使用 defer
来写在函数返回之前最后要执行的代码块,无论是否错误被抛出。你甚至可以在没有错误处理的时候使用 defer
,来简化需要在多处地方返回的函数。
|
|
泛型
把名字写在尖括号里来创建一个泛型方法或者类型。
泛型方法:
|
|
泛型枚举、结构体创建泛型:
|
|
where:在类型名称后紧接 where
来明确一系列需求——比如说,来要求类型实现一个协议,要求两个类型必须相同,或者要求类必须继承自特定的父类。写<T: Equatable>
和 <T where T: Equatable>
是同一回事
|
|
摘自:swift 官网
所有代码在Xcode9 Swift4 环境编译没问题,代码戳这里 https://github.com/keshiim/LearnSwift4