关键字作为标识符
在Swift中,关键字不能直接作为标识符,如果需要把关键字作为标识符,则需要在该标识符需要被`
包住;如:let `class` = "我是值"
注意:
`
不是标识符的一部分,它也可以用于其它标识符,如: aID
和 `aID`
是等价的;
进制数字表示
进制 | 表示 | 说明 |
---|---|---|
2进制 | 0b | 第1个字符是阿拉伯数字零,第2个字符是小写英文字母b,不能大写,是binary(二进制的)的首字母; |
8进制 | 0o | 第1个字符是阿拉伯数字零,第2个字符是小写英文字母o,不能大写,是octonary(八进制的)的首字母; |
16进制 | 0x | 第1个字符是阿拉伯数字零,第2个字符是小写英文字母x,不能大写,是hexadecimal(十六进制的)的代表; |
我认为:它们之所以都以数字开头,是因为它们是需要被放在数字的开头的,且被用来表示数字的,如果若以非数字的字符开头,则容易被编译器识别为非数字;之所以选择数字中的0,是因为在意义上,0是最好的选择;
值绑定
- 值绑定的表达必须是赋值
=
表达式,不能是+=、-=
等之类的改变赋值表达式,并且>等号左边必须是新变量或新常量的声明,如:let bindingVal = val
或者let bindingVal:Type = val
或者var bindingVal = val
或者var bindingVal:Type = val
,不能在值绑定语句外声明变量; - 值绑定语句可以有多个表达式,表达式之间用逗号
,
分隔; - 当值绑定应用在期望是布尔值的地方时,值绑定会选解包val,如果val为nil,则值绑定会返回布尔值false,如果val不为nil,则值绑定会把解包后的值赋值给bindingVal,当有多个值绑定表达式时,表达之间是与(且)的关系;当值绑定表达式应用在期望值不是布尔值的地方时,它不会进行解包操作,只是作为一般的表达式进行赋值;
原生集合
不可变的数组在访问效率上比可变数组高,因为可变数组通过牺牲访问效率换取可变性,当往可变数组添加新元素的时候,数组要重新改变大小,然后重排它们的索引下标,这些都会影响性能;
原生字典Dictionary
- 添加元素:给一个不存在的键赋一个有效值;
- 删除元素:
2.1 把相应的键赋值为nil;
2.2 通过字典的removeValue(forKey:)方法; - 替换元素:
3.1 给一个存在的键赋值;
3.2 通过updateValue(_:forKey:)方法;
闭包
- 如果闭包省略了返回类型的声明 且 闭包中只有一条语句,如{(a:Int) in a * 2},则Swift根据上下文推断闭包的返回类型,经过推断后,如果上下文中要求该闭包的返回类型不为Void,则会把仅有的一条语句的结果当作返回值,如果上下文中要求该闭包的返回类型为Void,则Swift就不会返回仅有的一条的语句的结果,如果上下文中没有对闭包的类型进行要求,则Swift也会默认把仅有的一条语句的结果当作返回值并自动推断该闭包的返回类型;
- 闭包不支持外部参数名;
可变方法与不可变方法
在类中的方法可以更换属性的值,但在结构体和枚举的方法不能更换属性值,不过可以更换引用类型的属性的属性值,因为更换引用类型的属性的属性值并不会更换引用类型属性中保存的地址;如果要在结构体和枚举的方法中更换属性的值,则可以在方法声明前添加mutating关键字;
构造函数
结构体和类在创建实例的过程中需要进行一些初始化工作,这个过程称为构造过程。
如果没有对结构体和类中的可选存储属性赋初始值,则编译器会自动把其初始化为nil;
对于基类,如果没有提供任何构造函数,编译器会自动提供如下构造函数:
init() {
}
所以,如果类中有非可选类型的存储属性没有赋初始值的话,就会导致这些存储属性没有被正确的初始化,从而引起编译器会报错;
对于子类,则有如下规则:
- 如果子类没有定义任何指定构造函数,那么它将自动继承父类的所有指定构造函数;
- 如果子类提供了所有父类指定构造函数的实现,无论是通过规则1继承过来的,还是通过自己编写实现的,它都将自动继承父类的所有便利构造函数;
对于结构体,如果没有提供任何构造函数,编译器会根据情况自动提供如下构造函数:
-
如果结构体中有非可选类型的存储属性没有赋初始值的话,编译器会自动提供一个带有所有存储属性参数列表的构造函数,并在构造函数对相应的存储属性赋值,如下:
init(所有存属性的参数列表) { 对所有存储属性进行相应的赋值 }
-
如果结构体中所有非可选类型的存储属性都赋有初始值,则编译会自动提供2个构造函数,如下所示:
2.1 不带有任何参数,也不在构造函数内执行任何初始化工作的空构造函数:
init() { }
2.2 带有所有存储属性参数列表的构造函数,并在构造函数对相应的存储属性赋值:
init(所有存属性的参数列表) { 对所有存储属性进行相应的赋值 }
self与Self
self指代自己;即,如果self的上下文是实例环境,则self指实例自己;如果self的上下文是类型环境,则self指代类型自己;Self指代自己的类型;
关于属性的重写
- 不能用存储属性去重写属性;如:用
override var gby = 27
重写父类的gby
属性是不被允许的; - 常用属性不可以被重写;
- 不可以重写只读计算属性的观察者;
- 不可以把可变存储属性重写成只读计算属性;
as、as!、as?操作符
-
as操作符
1.1 向上转型,即从子类转换为父类;
1.2 消除二义性,经常用于数值类型的转换;如:
let num1 = 27 as Double
1.3 用于在switch语句中进行模式匹配;
如果不知道一个对象是什么类型,你可以通过switch语法检测它的类型,并尝试在不同的情况下对相应的类型进行处理,如下:switch animal { case let cat as Cat: print("如果是Cat类型对象,则做相应处理") case let dog as Dog: print("如果是Dog类型对象,则做相应处理") default: break }
-
as!操作符
2.1 向下转型;
2.2 将可靠类型转换为非可选类型;
注意:
使用as!操作符时,若在转换过程中不能转换为目标类型,则会在运行时报运行时错误;
-
as?操作符
转换过程不会进行拆包,转换结果是可选类型;有如下使用场景:
3.1 将非可选类型转换为可靠类型;
3.2 将可选类型转换为可选类型;
扩展
扩展可以用于类、结构体、枚举、协议,由于基本类型也都是结构体类型,所以的扩展也可以用于基本类型;
扩展的具体限制如下;
- 扩展中不可以定义存储属性;
- 类的扩展中不能添加指定构造函数和析构函数;
- 扩展中可以定义遵守协议;
协议
- 协议中定义属性时,必须明确指定
get
或set
,也可两者都指定;其中,get
表示该属性无论是存储属性还是计算属性,其值必须可获取;set
表示该属性无论是存储属性还是计算属性,其值必须可赋值; - 协议中定义静态属性或者静态方法时需要使用关键字
static
,当类实现该静态属性或者静态方法时,可以使用static
或者class
关键字; - 在Swift中,协议是作为数据类型来使用的,协议名字可以出现在任意数据类型出现的位置;但是,协议不能被实例化;
- 可以对协议进行扩展,对协议进行扩展时与对其它类型进行扩展一样,在扩展中定义的是实现,不是接口;遵守协议的类型会默认继承协议的扩展中的实现,所以可以在协议的扩展中定义协议的默认实现;
泛型
- 在使用泛型类或者泛型函数时,其泛型所指代的类型是被自动推断出来的;
- 在调用用泛型函数时,不允许明确指定泛型所指代的类型;
- 在使用泛型类型时,当不能自动推断出泛型所指代的类型时,需要明确指定泛型所指代的类型;
关联associatedtype
- 关联associatedtype只用于协议的定义中,它是用来实现协议的泛型功能的;
- 关联关键字associatedtype是用于在协议中定义类型的占位符的,在实现该协议的类型中,需要使用别名关键字typealias来指定占位类型的具体指代类型;
在Swift中,class、struct、enums都可以是用参数化类型来表达泛型的,只有在协议中需要使用associatedtype关键字来表达参数化类型。为什么协议不采用这样的语法形式呢?原因如下:
- 采用语法的参数化方式的泛型其实定义了整个类型的家族,在概念上这对于一个可以具体实现的类型(class、struct、enums)是有意义的,比方说Array,Array。但对于协议来说,协议表达的含义是single的。你只会实现一次GeneratorType,而不会实现一个GeneratorType协议,接着又实现另外一个GeneratorType协议。
- 协议在Swift中有两个目的,第一个目的是用来实现多继承(swift语言被设计为单继承的),第二个目的是强制实现者必须准守自己所指定的泛型约束。关键字associatedtype是用来实现第二个目的的。
try、try!、try?操作符
try?
操作会把尝试执行的函数的返回值转为可选类型;如果尝试执行的函数或方法招聘错误,则程序不会崩溃,只是返回一个nil;如果没有招聘错误,则会返回可选值;
try!
可以阻止错误向上传播;
Swift特性
- Swift中的块注释支持嵌套;
- Swift中语句结束后可以不加分号,但当多条语句在同一行时,各语句间需要用
、
来分隔; - 布尔值为:true、false
-
===
只能用于比较引用类型; - 逻辑运算符为:
!(非)、&&(与)、||(或)
,其中&&、||
为短路运算; - 逻辑非:!,位非:~
- 在数字字面量中可以插入下划线
_
来提高可读性,在数字前面可认添加多个0来提高可读性; - 整形、浮点型、布尔型、字符、字符串、集合本质上都是结构体类型;
- 无论是字符字面量还是字符串字面量都是用双引号
"
包住的;在声明时,如果不明确指定类型,Swift会把"
包住的字符默认推断为字符串类型; - Swift中字符Character和字符串String都是结构体类型,作相等比较时只能使用==或!=比较相等或不等,而Foundation框架中的NSString字符串是引用类型,所以NSString类型的字符的相等比较需要使用===或者!==运算符;
- Swift中的控制语句(如:if、switch、for,等等)中的条件语句可以不用圆括号()括住;并且语句组的花括号{}是必须的,即使语句组中只有一条语句,花括号{}也不能省略;
- 分支语句switch可以比较整数、浮点、字符、字符串、元组、枚举、可选类型;当switch匹配的是可选类型时,case中的值也必须是可选值,在case中书写可选字符的方法是:在普通字面量后面加上问号?;case中的值可以是离散的,也可以是连续的范围,一个case中可以配置多个值,多个值之间是或的关系;
- fallthrough不能用于当下一个case标签的匹配处是声明变量的case中;
- Set的声明方式只能通过Set<Type>泛型的方式声明,如:var aSet:Set<Type> 或 let aSet:Set<Type>,Set的表示方式可以用Array的字面量形式,即:[...]
- 枚举不能同时具有原生始值和相关值这2个特性;
- 属性观察者不能用于计算属性,但计算属性可以在子类中重写它的观察者方法;
- 运算符两侧必须要么都有空格,要么都没有空格;
- 静态属性和方法用static关键字修饰,但在类的静态计算属性和方法即可以用static修饰,也可以用class关键字修饰,它们的区别是:static修饰的静态计算属性不能在子类中被重写,class关键字修饰的静态计算属性可以被子类重写;
- swift不允许无用的、无意义的、多余的语法规则;如:计算属性不能用
let
关键字声明,不能重写常量属性和只读计算属性的属性观察者; - 在Objective-C和Swift混合编程时,Objective-C的id类型和Swift的AnyObject类型可以互换,但是两者有本质的区别:id类型是泛型,可以代表任何对象指针类型,编译时编译器不检查id类型,是动态的;面swft的AnyObject类型是一个实实在在表示类的类型,编译时编译器会检查AnyObject类型;
- 面向对象类型的对属性的支持情况:
面向对象类型 | 存储属性 | 计算属性 | 静态属性 |
---|---|---|---|
枚举 | 否 | 是 | 是 |
结构体 | 是 | 是 | 是 |
类 | 是 | 是 | 是 |