# 答《 卓同学的 Swift 面试题 》

## class 和 struct 的区别

class 为类, struct 为结构体, 类是引用类型, 结构体为值类型, 结构体不可以继承

## Set 独有的方法有哪些？

``````// 定义一个 set
let setA: Set<Int> = [0,1, 2, 3, 4, 4]// {1, 2, 3, 4}, 顺序可能不一致, 同一个元素只有一个值
let setB: Set<Int> = [1, 3, 5, 7, 9]// {1, 3, 5, 7, 9}
// 取并集 A | B
let setUnion = setA.union(setB)// {0,1, 2, 3, 4, 5, 7, 9}
// 取交集 A & B
let setIntersect = setA.intersection(setB)// {1, 3}
// 取差集 A - B
let setRevers = setA.subtracting(setB) // {0,2, 4}
// 取对称差集, A XOR B = A - B | B - A
let setXor = setA.symmetricDifference(setB) //{0,2, 4, 5, 7, 9}

``````

## 实现一个 min 函数，返回两个元素较小的元素

``````func getMin<T: Comparable>(_ a: T, _ b: T) -> T {
return a < b ? a : b
}
getMin(1, 2)
``````

## map、filter、reduce 的作用 && map 与 flatmap 的区别

map 函数 -- 对数组中的每一个对象做一次运算

``````let stringArray = ["Objective-C", "Swift", "Python", "HTML5", "C",""]

let resultAry = stringArray.map { (element) -> Int? in
if element.length > 0 {
return element.length
}else{
return nil
}

}
print(resultAry)
//[Optional(11), Optional(5), Optional(6), Optional(5), Optional(1), nil]
``````

fiatMap 函数 -- 也是对每个对象做一次运算，但是有区别

``````let stringArray = ["Objective-C", "Swift", "Python", "HTML5", "C",""]
let resultAry = stringArray.flatMap { (element) -> Int? in
if element.length > 0 {
return element.length
}else{
return nil
}

}
print(resultAry)
//[11, 5, 6, 5, 1]
``````

``````let stringArray = [["Objective-C", "Swift"], ["Python", "HTML5", "C",""]]

let resultAry = stringArray.map { \$0 }
print(resultAry)
//[["Objective-C", "Swift"], ["Python", "HTML5", "C", ""]]

//flatMap
let stringArray = [["Objective-C", "Swift"], ["Python", "HTML5", "C",""]]

let resultAry = stringArray.flatMap { \$0 }
print(resultAry)
//["Objective-C", "Swift", "Python", "HTML5", "C", ""]
``````

filter 过滤，数组中的元素按照 闭包里面的规则过滤

``````let stringArray = ["Objective-C", "Swift", "Python", "HTML5", "C",""]

let resultAry = stringArray.filter { (element) -> Bool in
//元素长度大于5的 取出
return element.length >= 5
}
print(resultAry)
//["Objective-C", "Swift", "Python", "HTML5"]
``````

reduce 计算，按顺序对数组中的元素进行操作，然后记录操作的值再进行下一步相同的操作，可以想象成累加。

``````let stringArray = ["Objective-C", "Swift", "Python", "HTML5", "C",""]

let resultStr = stringArray.reduce("Hi , I'm PierceDark,") { (element1, element2) -> String in
return element1 + " ," + element2
}
print(resultStr)
//Hi , I'm PierceDark, ,Objective-C ,Swift ,Python ,HTML5 ,C ,
``````

## 如何获取当前代码的函数名和行号

`#file`用于获取当前文件文件名
`#line`用于获取当前行号
`#column`用于获取当前列编号
`#function`用于获取当前函数名

## 如何声明一个只能被类 conform 的 protocol

``````protocol SomeClassProtocl: class {
func someFunction()
}
``````

## defer 使用场景

`defer` 语句块中的代码, 会在当前作用域结束前调用, 常用场景如异常退出后, 关闭数据库连接 ,而且 `defer`是先加入后执行

``````func someQuery() -> ([Result], [Result]){
let db = DBOpen("xxx")
defer {
db.close()
}
guard results1 = db.query("query1") else {
return nil
}
guard results2 = db.query("query2") else {
return nil
}
return (results1, results2)
}

``````

## String 与 NSString 的关系与区别

`String`是结构体，值类型,`NSString`是类,引用类型。可以相互转换

## 怎么获取一个 String 的长度

``````"test".count
``````

## 如何截取 String 的某段字符串

PS：`Swift`截取子串真的不好用= = ，`Swift4`不用`substring:to` , `substring:from`, `substring:with`了，但还是很难用 .还不如转成`NSString`再去截取。。

``````let newStr = str.substring(to: index) // Swift 3
let newStr = String(str[..<index]) // Swift 4

let newStr = str.substring(from: index) // Swift 3
let newStr = String(str[index...]) // Swift 4

let range = firstIndex..<secondIndex // If you have a range
let newStr = = str.substring(with: range) // Swift 3
let newStr = String(str[range])  // Swift 4
``````

## throws 和 rethrows 的用法与作用

throws 用在函数上, 表示这个函数会抛出错误.

``````enum DivideError: Error {
case EqualZeroError;
}
func divide(_ a: Double, _ b: Double) throws -> Double {
guard b != Double(0) else {
throw DivideError.EqualZeroError
}
return a / b
}
func split(pieces: Int) throws -> Double {
return try divide(1, Double(pieces))
}

``````

rethrows 与 throws 类似, 不过只适用于参数中有函数, 且函数会抛出异常的情况, rethrows 可以用 throws 替换, 反过来不行

``````func processNumber(a: Double, b: Double, function: (Double, Double) throws -> Double) rethrows -> Double {
return try function(a, b)
}
``````

## try？ 和 try！是什么意思

``````print(try? divide(3, 1))
// Optional(3.0)
print(try? divide(3, 0))
// nil
``````

``````print(try! divide(3, 1))
// 2.0
print(try! divide(3, 0))
// 崩溃
``````

## associatedtype 的作用

`protocol`使用的泛型

``````protocol ListProtcol {
associatedtype Element
func push(_ element:Element)
func pop(_ element:Element) -> Element?
}
``````

``````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()
}
}

``````

``````class AnyList<T>: ListProtcol {
var list = [T]()
func push(_ element: T) {
self.list.append(element)
}
func pop(_ element: T) -> T? {
return self.list.popLast()
}
}

``````

``````extension ListProtcol where Element == Int {
func isInt() ->Bool {
return true
}
}
``````

## 声明一个只有一个参数没有返回值闭包的别名

``````    typealias SomeClosuerType = (String) -> ()
let someClosuer: SomeClosuerType = { (name: String) in
print("hello,", name)
}
``````

## Self 的使用场景

Self 通常在协议中使用, 用来表示实现者或者实现者的子类类型

``````protocol CopyProtocol {
func copy() -> Self
}
``````

``````struct SomeStruct: CopyProtocol {
let value: Int
func copySelf() -> SomeStruct {
return SomeStruct(value: self.value)
}
}
``````

``````class SomeCopyableClass: CopyProtocol {
func copySelf() -> Self {
return type(of: self).init()
}
required init(){}
}
``````

## 什么时候使用 @objc

`Swift4`之后，继承`NSObject``Swift`类不会自动与 `OC`交互了，属性前面需要加上`@objc`

``````@objc protocol OptionalProtocol {
@objc optional func optionalFunc()
func normalFunc()
}
class OptionProtocolClass: OptionalProtocol {
func normalFunc() {
}
}
let someOptionalDelegate: OptionalProtocol = OptionProtocolClass()
someOptionalDelegate.optionalFunc?()

``````

## Optional（可选型） 是用什么实现的

``````enum Optional<Wrapped> {
case none
case some(Wrapped)
}
``````

## 如何自定义下标获取

``````extension AnyList {
subscript(index: Int) -> T{
return self.list[index]
}
subscript(indexString: String) -> T?{
guard let index = Int(indexString) else {
return nil
}
return self.list[index]
}
}

``````

## ?? 的作用

``````var test : String? = nil
print(test ?? "optional = nil")
//输出    optional = nil
``````

## 一个类型表示选项，可以同时表示有几个选项选中（类似 UIViewAnimationOptions ），用什么类型表示

``````struct SomeOption: OptionSet {
let rawValue: Int
static let option1 = SomeOption(rawValue: 1 << 0)
static let option2 =  SomeOption(rawValue:1 << 1)
static let option3 =  SomeOption(rawValue:1 << 2)
}
let options: SomeOption = [.option1, .option2]

``````

## Error 如果要兼容 NSError 需要做什么操作

``````enum SomeError: Error, LocalizedError, CustomNSError {
case error1, error2
public var errorDescription: String? {
switch self {
case .error1:
return "error description error1"
case .error2:
return "error description error2"
}
}
var errorCode: Int {
switch self {
case .error1:
return 1
case .error2:
return 2
}
}
public static var errorDomain: String {
return "error domain SomeError"
}
public var errorUserInfo: [String : Any] {
switch self {
case .error1:
return ["info": "error1"]
case .error2:
return ["info": "error2"]
}
}
}
print(SomeError.error1 as NSError)
// Error Domain=error domain SomeError Code=1 "error description error1" UserInfo={info=error1}

``````

## 下面的代码都用了哪些语法糖

``````[1, 2, 3].map{ \$0 * 2 }
``````
1. 快速创建数组
2. 第一个参数用`\$0`代替
3. 闭包没有声明函数参数, 返回值类型, 数量, 依靠的是闭包类型的自动推断
4. 闭包中语句只有一句时, 自动将这一句的结果作为返回值

## 如何解决引用循环

1. 转换为值类型
2. 注意各个强引用
可以参考我的博客Swift 强引用的解决方案（unowned 、 weak 、隐式解析可选属性）

## 下面的代码会不会崩溃，说出原因

``````var mutableArray = [1,2,3]
for _ in mutableArray {
mutableArray.removeLast()
}

``````

## 给集合中元素是字符串的类型增加一个扩展方法，应该怎么声明

``````extension Set where Element == String {
var isStringElement:Bool {
return true
}
}
``````

## 定义静态方法时关键字 static 和 class 有什么区别

`static`不能被继承 ，`class`可以

## 一个 Sequence 的索引是不是一定从 0 开始？

``````class Countdown: Sequence, IteratorProtocol {
var count: Int
init(count: Int) {
self.count = count
}
func next() -> Int? {
if count == 0 {
return nil
} else {
defer { count -= 1 }
return count
}
}
}

var countDown = Countdown(count: 5)
print("begin for in 1")
for c in countDown {
print(c)
}
print("end for in 1")
print("begin for in 2")
for c in countDown {
print(c)
}
print("end for in 2")

``````

``````begin for in 1
5
4
3
2
1
end for in 1
begin for in 2
end for in 2
``````

## 数组都实现了哪些协议

`MutableCollection`, 实现了可修改的数组, 如 `a[1] = 2`
`ExpressibleByArrayLiteral`, 实现了数组可以从`[1, 2, 3]` 这种字面值初始化的能力

## 编译选项 whole module optmization 优化了什么

http://www.jianshu.com/p/8dbf2bb05a1c

## 下面代码中 mutating 的作用是什么

``````struct Person {
var name: String {
mutating get {
return store
}
}
}
``````

`mutating`表示有可能修改这个结构体，所以只有`var` 的对象才可以调用

## 如何让自定义对象支持字面量初始化

`ExpressibleByArrayLiteral` 可以由数组形式初始化
`ExpressibleByDictionaryLiteral` 可以由字典形式初始化
`ExpressibleByNilLiteral` 可以由nil 值初始化
`ExpressibleByIntegerLiteral` 可以由整数值初始化
`ExpressibleByFloatLiteral`可以由浮点数初始化
`ExpressibleByBooleanLiteral` 可以由布尔值初始化
`ExpressibleByUnicodeScalarLiteral`
`ExpressibleByExtendedGraphemeClusterLiteral`
`ExpressibleByStringLiteral`

## 一个函数的参数类型只要是数字（Int、Float）都可以，要怎么表示。

``````func isNumber<T : SignedNumber>(number : T){
print(" it is a number")
}
``````