Swift 中文文档:https://swift.bootcss.com
1、Swift 中的 nil 和Objective-C 中的 nil 不同
Swift 中的 nil 和Objective-C 中的 nil 不同,在 Objective-C 中 nil 是一个指向不存在对象的指针。在 Swift中, nil 不是指针,他是值缺失的一种特殊类型,任何类型的可选项都可以设置成 nil 而不仅仅是对象类型。
2、标识符的问题
class 不是合法的标识符,但可以使用 class
。反引号不属于标识符的一部分。
default 不是合法的标识符,但可以使用 default
class和default是关键字,不可以用关键字做标识符
3、复合类型
复合型类型是没有名字的类型,它由 Swift 本身定义。Swift 存在两种复合型类型:函数类型和元组类型。元组可以存放多个不同类型的元素。
4、Self 类型不是具体的类型
Self 类型不是具体的类型,而是让你更方便的引用当前类型,不需要重复或者知道该类的名字。
a、在协议声明或者协议成员声明时,Self 类型引用的是最终遵循该协议的类型。
b、在结构体,类或者枚举值声明时,Self 类型引用的是声明的类型。在某个类型成员声明时,Self 类型引用的是该类型。
self(小写) 表达式是对当前类型或者当前实例的显式引用
5、Try 运算符
try 表达式由 try 运算符加上紧随其后的可抛出错误的表达式组成,形式如下:
try 表达式
try 表达式的返回值是该表达式的值。
可选 try 表达式由 try? 运算符加上紧随其后的可抛出错误的表达式组成,形式如下:
try? 表达式
如果表达式没有抛出错误,可选 try 表达式的返回值是可选的该表达式的值,否则,返回值为 nil。
强制 try 表达式由 try! 运算符加上紧随其后的可抛出错误的表达式组成,形式如下:
try! 表达式
强制 try 表达式的返回值是该表达式的值。如果该表达式抛出了错误,将会引发运行时错误
6、闭包
a、一个闭包是否逃逸与其使用时的上下文相关。一个会被立即调用或者作为函数的非逃逸参数传递的闭包表达式是非逃逸的,否则,这个闭包表达式是逃逸的
b、默认情况下,闭包会捕获附近作用域中的常量和变量,并使用强引用指向它们
7、## 捕获列表
默认情况下,闭包会捕获附近作用域中的常量和变量,并使用强引用指向它们。你可以通过一个捕获列表来显式指定它的捕获行为。
捕获列表在参数列表之前,由中括号括起来,里面是由逗号分隔的一系列表达式。一旦使用了捕获列表,就必须使用 in
关键字,即使省略了参数名、参数类型和返回类型。
捕获列表中的项会在闭包创建时被初始化。每一项都会用闭包附近作用域中的同名常量或者变量的值初始化。例如下面的代码示例中,捕获列表包含 a
而不包含 b
,这将导致这两个变量具有不同的行为。
var a = 0var b = 0let closure = { [a] in print(a, b)}a = 10b = 10closure()// 打印“0 10”
在示例中,变量 b
只有一个,然而,变量 a
有两个,一个在闭包外,一个在闭包内。闭包内的变量 a
会在闭包创建时用闭包外的变量 a
的值来初始化,除此之外它们并无其他联系。这意味着在闭包创建后,改变某个 a
的值都不会对另一个 a
的值造成任何影响。与此相反,闭包内外都是同一个变量 b
,因此在闭包外改变其值,闭包内的值也会受影响。
如果闭包捕获的值具有引用语义则有所不同。例如,下面示例中,有两个变量 x
,一个在闭包外,一个在闭包内,由于它们的值是引用语义,虽然这是两个不同的变量,它们却都引用着同一实例。
class SimpleClass { var value: Int = 0}var x = SimpleClass()var y = SimpleClass()let closure = { [x] in print(x.value, y.value)}x.value = 10y.value = 10closure()// 打印“10 10”
如果捕获列表中的值是类类型,你可以使用 weak
或者 unowned
来修饰它,闭包会分别用弱引用和无主引用来捕获该值。
myFunction { print(self.title) } // 隐式强引用捕获myFunction { [self] in print(self.title) } // 显式强引用捕获myFunction { [weak self] in print(self!.title) } // 弱引用捕获myFunction { [unowned self] in print(self.title) } // 无主引用捕获
在捕获列表中,也可以将任意表达式的值绑定到一个常量上。该表达式会在闭包被创建时进行求值,闭包会按照指定的引用类型来捕获表达式的值。例如:
// 以弱引用捕获 self.parent 并赋值给 parentmyFunction { [weak parent = self.parent] in print(parent!.title) }
关于闭包表达式的更多信息和例子,请参阅 闭包表达式。关于捕获列表的更多信息和例子,请参阅 解决闭包引起的循环强引用。
8、guard
Guard 语句
如果一个或者多个条件不成立,可用 guard
语句来退出当前作用域。
guard
语句的格式如下:
guard condition else { statements}
guard
语句中条件的结果必须是 Bool 类型或者 Bool 的桥接类型。另外,条件也可以是一条可选绑定,请参阅 可选绑定。
在 guard
语句中进行可选绑定的任何常量或者变量,其可用范围从声明开始直到作用域结束。
guard
语句必须有 else
子句,而且必须在该子句中调用返回类型是 Never
的函数,或者使用下面的语句退出当前作用域:
return
break
continue
throw
关于控制转移语句,请参阅 控制转移语句。关于 Never
返回类型的函数,请参阅 永不返回的函数。
guard 语句语法
guard-statement
9、计算型属性
setter 子句是可选的,getter 子句是必须的
使用如下形式声明一个计算型变量或计算型属性:
var 变量名称: 类型 {
get {
语句
}
set(setter 名称) {
语句
}
}
10、dynamic
dynamic 修饰符的类成员会由 Objective-C 运行时系统进行动态派发,所以它们会被隐式标记 objc 特性
11、nonobjc
nonobjc 特性告诉编译器该声明不能在 Objective-C 代码中使用,即便它能在 Objective-C 中表示。
12、objcMembers
在Swift中,继承自NSObject的类如果有比较多的属性或方法都需要加上@objc的话,会多比较多的代码。那么可以利用@objcMembers减少代码。被@objcMembers修饰的类,会默认为类、子类、类扩展和子类扩展的所有属性和方法都加上@objc。当然如果想让某一个扩展关闭@objc,则可以用@nonobjc进行修饰
13、输入输出参数 inout
函数参数默认是常量。试图在函数体中更改参数值将会导致编译错误。这意味着你不能错误地更改参数值。如果你想要一个函数可以修改参数的值,并且想要在这些修改在函数调用结束后仍然存在,那么就应该把这个参数定义为输入输出参数(In-Out Parameters)。
定义一个输入输出参数时,在参数定义前加 inout
关键字。一个 输入输出参数
有传入函数的值,这个值被函数修改,然后被传出函数,替换原来的值。想获取更多的关于输入输出参数的细节和相关的编译器优化,请查看 输入输出参数 一节。
你只能传递变量给输入输出参数。你不能传入常量或者字面量,因为这些量是不能被修改的。当传入的参数作为输入输出参数时,需要在参数名前加 &
符,表示这个值可以被函数修改。
注意
输入输出参数不能有默认值,而且可变参数不能用
inout
标记。
下例中,swapTwoInts(_:_:)
函数有两个分别叫做 a
和 b
的输入输出参数:
func swapTwoInts(_ a: inout Int, _ b: inout Int) { let temporaryA = a a = b b = temporaryA}
swapTwoInts(_:_:)
函数简单地交换 a
与 b
的值。该函数先将 a
的值存到一个临时常量 temporaryA
中,然后将 b
的值赋给 a
,最后将 temporaryA
赋值给 b
。
13、逃逸闭包
将一个闭包标记为 @escaping 意味着你必须在闭包中显式地引用 self
14、恒等运算符
因为类是引用类型,所以多个常量和变量可能在幕后同时引用同一个类实例。(对于结构体和枚举来说,这并不成立。因为它们作为值类型,在被赋予到常量、变量或者传递到函数时,其值总是会被拷贝。)
判定两个常量或者变量是否引用同一个类实例有时很有用。为了达到这个目的,Swift 提供了两个恒等运算符:
相同(===)
不相同(!==)
使用这两个运算符检测两个常量或者变量是否引用了同一个实例:
if tenEighty === alsoTenEighty {
print("tenEighty and alsoTenEighty refer to the same VideoMode instance.")
}
// 打印 "tenEighty and alsoTenEighty refer to the same VideoMode instance."
请注意,“相同”(用三个等号表示,===)与“等于”(用两个等号表示,==)的不同。“相同”表示两个类类型(class type)的常量或者变量引用同一个类实例。“等于”表示两个实例的值“相等”或“等价”,判定时要遵照设计者定义的评判标准。
15、Swift 下标 subscript
下标可以定义在类、结构体和枚举中,是访问集合、列表或序列中元素的快捷方式。可以使用下标的索引,设置和获取值,而不需要再调用对应的存取方法。举例来说,用下标访问一个 Array 实例中的元素可以写作 someArray[index],访问 Dictionary 实例中的元素可以写作 someDictionary[key]。
16、类类型的构造器代理 convenience
为了简化指定构造器和便利构造器之间的调用关系,Swift 构造器之间的代理调用遵循以下三条规则:
规则 1
指定构造器必须调用其直接父类的的指定构造器。
规则 2
便利构造器必须调用同类中定义的其它构造器。
规则 3
便利构造器最后必须调用指定构造器。
17、try try? try! 的区别
try 出现异常处理异常
try? 不处理异常,返回一个可选值类型,出现异常返回nil
try! 不让异常继续传播,一旦出现异常程序停止,类似NSAssert()
18、throws 和 rethrows 的用法与作用
throws 用在函数上, 表示这个函数会抛出错误.
rethrows 与 throws 类似, 不过只适用于参数中有函数, 且函数会抛出异常的情况, rethrows 可以用 throws 替换, 反过来不行
如
func processNumber(a: Double, b: Double, function: (Double, Double) throws -> Double) rethrows -> Double {
return try function(a, b)
}
19、associatedtype 的作用
简单来说就是 protocol 使用的泛型
例如定义一个列表协议
protocol ListProtcol {
associatedtype Element
func push(_ element:Element)
func pop(_ element:Element) -> Element?
}
实现协议的时候, 可以使用 typealias 指定为特定的类型, 也可以自动推断, 如
class IntList: ListProtcol {
typealias Element = Int // 使用 typealias 指定为 Int
var list = Element
func push(_ element: Element) {
self.list.append(element)
}
func pop(_ element: Element) -> Element? {
return self.list.popLast()
}
}
class DoubleList: ListProtcol {
var list = Double
func push(_ element: Double) {// 自动推断
self.list.append(element)
}
func pop(_ element: Double) -> Double? {
return self.list.popLast()
}
}
20、weak 和 unowned 应用场合
- weak
一般使用在两个对象满足其生命周期没有太大关系之间。例如viewController和delegate等,它们的生命周期不是包含关系,而是互相平行的。
- unowned
一般使用在两个对象满足其中一个对象的生命周期包含另一个对象的生命周期。例如一个viewController中的timer的生命周期会被包含在viewController生命周期内,所以timer中的Block对self(viewController)的引用就使用unowned。
21、[高阶函数map、flatMap、CompactMap 、filter 、reduce的区别?