答卓同学的Swift 面试题

题目: 卓同学的 Swift 面试题

翻了 喵神SwiftGG 的资料, 借鉴了 Arco_vvEnum 的回答, ...算是查漏补缺

1. class 和 struct 的区别

  • 结构体(和枚举, 元组)是值类型, 类(和函数, 闭包)是引用类型.
  • 类可以通过继承来共享代码. 而结构体(和枚举)不能被继承.
    结构体共享代码可以用组合, 泛型以及协议扩展

2. 不通过继承, 代码复用(共享)的方式有哪些

  • 协议扩展
  • 泛型
  • 组合

3. Set 独有的方法有哪些

  • SetArray, Dictionary 是 Swift 提供的三种基本集合类型,
    数据类型必须明确, 且遵循 Hashable, 被实现为泛型集合
    Array 是有序数据集;
    Set 是无序无重复数据的集;
    Dictionary 是无序的键值对的集

    当集合元素顺序不重要或者希望每个元素只出现一次时可以使用 Set

  • 操作方法

    • a.intersection(b) a 与 b 的交集
    • a.symmetricDifference(b) a 与 b 的对等差分(a 与 b 独有元素的并集)
    • a.union(b) a 与 b 的并集
    • a.subtracting(b) a 与 b 的差集(属于 a 不属于 b)
  • 判断方法

    • a.isSubset(of: b) a 是否是 b 的子集
    • a.isSuperset(of: b) a 是否包含 b
    • a.isStrictSubset(of: b) a 是否是 b 的子集 并且 a 与 b 不相等
    • a.isStrictSuperset(of: b) a 是否包含 b 并且 a 与 b 不相等
    • a.isDisjoint(with: b) a 是否与 b 没有交集

4. 实现一个 min 函数, 返回两个元素较小的元素

func min<T: Comparable>(a: T, b: T) -> T {
    return a < b ? a : b
}

5. map, filter, reduce 的作用

  • map 函数能够被集合调用,它接受一个闭包作为参数,作用于数组中的每个元素。闭包返回一个变换后的元素,接着将所有这些变换后的元素组成一个新的集合(集合类型与值可以改变, 但是数量不变)并返回;
  • filter (过滤器) 传入一个返回 Bool 类型的函数 保留返回值为 true 的元素
  • reduce 中的参数为两个:一个初始值、一个 combine 闭包. reduce 把集合中所有的值合并成 一个新值, 该值类型可以与原元素值不同

6. map 与 flatmap 的区别

  • map 返回的集合元素数量与原集合一致
  • flatMap 返回后的集合中不存在 nil, 可以把 Optional 解包
  • flatMap 可以把集合降维

7. 什么是 copy on write

  • Swift 中的写时复制是指,值类型只在 引用不唯一 并且 被改动 前进行复制
    传统意义上的值类型会在被传递或者被赋值给其他变量时就发生复制行为,但是这将会带来极大的,也是不必要的性能损耗。
    写时复制将在值被传递和赋值给变量时首先检查其引用计数,如果引用计数为 1 (唯一引用),那么意味着并没有其他变量持有该值,对当前值的复制也就可以完全避免,以此在保持值类型不可变性的优良特性的同时,保证使用效率。

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

func getInfo(name: String = #function, line: Int = #line) {}

getInfo()

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

protocol MyProtocol: class {}

10. guard 使用场景

  • 解包, 避免 if let else 嵌套

11. defer 使用场景

defer 声明一个 block,当前代码执行的闭包退出时会执行该 block。

使用场景: 函数返回是一定要执行一些代码, 就放在defer代码块里面

12. String 与 NSString 的关系与区别

Stringstruct, 是值类型, 性能比 NSString 占优
NSString 继承自 NSObject, 是引用类型 拥有动态特性

String 可以使用 characters 进行枚举

13. 怎样获取一个 String 的长度

let length1 = "string".characters.count
let length2 = "string".data(using: .utf8)
let length3 = ("string" as NSString).length

14. 如何截取 String 的某段字符串

extension String {

  /// 截取字符串(包括边界)
  ///
  /// - Parameter range: 截取的边界
  /// - Returns: 截取后的字符串
  func subString(_ lower: Int, _ upper: Int) -> String {

    let startOffSet = lower < 0 ? 0 : lower

    let endOffSet   = upper > self.characters.count ? self.characters.count : upper

    let start = self.index(self.startIndex, offsetBy: startOffSet)
    let end = self.index(self.startIndex, offsetBy: endOffSet)

    let subRange = Range.init(uncheckedBounds: (lower: start, upper: end))

    return self.substring(with: subRange)
  }
}

15. throws 和 rethrows 的用法与作用

  • rethrowthrows 表示该函数里可能会抛出异常, 调用该函数时需要处理此异常
  • rethrows 一般用在参数中含有可以 throws 的方法的高阶函数中
  • throws 另一个 throws 时,应该将前者改为 rethrows, 提高可读性与准确性
//符合抛出异常条件时
func doSomething() throws {
    if somthingIsError {
      throw CustomError
    }
    //或者配合 do catch / try / try?
  }

func methodRethrows(num: Int, f: (Int) throws -> ()) rethrows {
    try f(num)
  }

16. try? 和 try! 是什么意思

在可能发生异常的语句前加 try

  • try! 表示强制执行, 确定其不会抛出异常, 如果调用中出现异常, 程序将崩溃, 这与对 Optional 值用 ! 进行强制解包的行为是一致的.
  • try? 会返回一个 Optional 值, 如果运行成功, 会包含这条语句的返回值, 否则将返回 nil, 并且意味着无视了错误的具体类型

17. associatedtype 的作用

  • 被用作协议中的泛型约束

    在协议中使用 associatedtype 可以让实现此协议的对象限定协议方法中参数的类型

18. 什么时候使用 final

  • final 关键字可以用在 class, func 或者 var 前面进行修饰, 表示不允许对该内容进行继承或者重写操作.

19. pubic 和 open 的区别

  • 只有被 open 标记的内容才能在别的框架中被继承或者重写

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

  • typealias myBlock = (String) -> Void

21. Self 的使用场景

  • 在协议中使用表示当前类型, 类似 associatedtype 的作用, 所以使用 Self 的协议只能被用作泛型约束, 不能用作独立类型, 因为只有获知实际类型 Self 才有意义
protocol Equatable {
     static func ==(lhs: Self, rhs: Self) -> Bool
}
  • 扩展协议时用来做类型约束
protocol MyProtocol { }

extension MyProtocol where Self: UIView { }

22. dynamic 的作用

Swift 中使用 KVO 仅限于 NSObject 的子类
该类属性添加 dynamic 关键字
可以把该属性添加到动态查找列表中 使其可以被动态调用, 即可实现KVO

23. 什么时候使用 @objc

使用@objc 修饰后的类型, 可以直接被 Objective-C 调用

  • 可以被 @objc 修饰的类型:

    • 未嵌套的类
    • 协议
    • 非泛型枚举 (仅限于原始值为整形的枚举)
    • 类和协议中的属性和方法
    • 构造器和析构器
    • 下标
  • Swift 中声明继承 NSObject 的类, 会被自动加上 @objc 修饰

  • Swift 中使用中文类名时, 若想让 Objective-C 调用

@objc(MyClass)
class 我的类: NSObject {
@objc(greeting:)
func 打招呼(名字: String) {
print("哈喽,(名字)")
}
}


- 若想在协议中实现 `optional` 方法 除了扩展协议, 还可以直接 添加 `@objc` 修饰符, 在方法前添加 `optional` 关键字

@objc protocol CounterDataSource {
optional func incrementForCount(count: Int) -> Int
optional var fixedIncrement: Int { get }
}


###24. Optional (可选型) 是怎么实现的

- `Optional` 是一个泛型枚举, 有两个枚举成员, 一个是 `.some(Wrapped)`, 一个是 `.none`,
- 实现 `ExpressibleByNilLiteral` 协议, 可以接收 `nil`

###25. 如何自定义下标获取

class MyClass {
private let a = ["a", "b"]

subscript(index: Int) -> String {
    return a[index]
}

}


###26. ?? 的作用
- 语法糖. 尝试取得 `Optional` 值时, 声明当 `Optional` 为 `.none` 时, 使用 `??` 右边的值.
- 另外右边的值为闭包时, 被 `@autoclosure` 优化, 获取值若不为 `nil`, 则不会被执行

###27. lazy 的作用
- 延迟计算, 实现惰性初始化, 如果不访问该属性, 它的缺省值就不会被计算, 可以用闭包做初始化时可以调用 `self`, 因为当它需要计算出来的时候, `self` 已经初始化完成, 并且该闭包属于瞬发闭包, 不会产生循环引用
- 由于它在没有值的情况下被初始化, 然后被访问时改变自己的值, 所以该属性不能是 `lazy let` 
- 声明在全局作用域下 和 类型属性(声明为 `static let` , 而非实例属性) 的常量是自动具有惰性( `lazy` ) 的, 并且线程安全
- `SequenceType` 和 `CollectionType` 协议都有一个 `lazy` 计算属性, 它能返回一个特殊的 `LazySequence` 或者 `LazyCollection`; 这种方法只能被用在 `map`, `flatMap`, `filter` 这样的高阶函数中, 是一种惰性的方式. 实现当值被访问时才会调用传入的函数, 对于庞大的序列, 可以提高很多效率

func double(x: Int) -> Int {
print("Computing double value of (x)…")
return 2*x
}
let doubleArray = array.lazy.map(increment).map(double)
print(doubleArray[3])

只用当 `array[3]` 被访问时, `double(increment(array[3]))` 才会执行

###28. 一个类型表示选项, 可以同时表示有几个选项选中(类似 UIViewAnimationOptions), 用什么类型表示

- 实现 `OptionSet` 协议的结构体 只需要一个整型的原始值(`rawValue`)

###29. inout 的作用
- Swift 常规方法中经常用到的是值传递。
值传递最明显的后果便是无法对原数据进行直接修改。
如果我们需要处理后的数据结果,那么就需要重新定义一个变量来接收值。
在原数据被废弃的情况下,这样既增多了代码量,也产生了空间大量浪费。
因此 Swift 提供了关键字修饰 `inout` 来申明数据地址传递,也称之为引用传递。  

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

- 定义 `CustomNSError` 协议, 继承自 `Error` 用来桥接原来 `NSError` 中的 `code`、`domain`、`UserInfo`.

public protocol CustomNSError : Error {

/// The domain of the error.
public static var errorDomain: String { get }

/// The error code within the given domain.
public var errorCode: Int { get }

/// The user-info dictionary.
public var errorUserInfo: [String : Any] { get }

}


如果想让我们的自定义 `Error` 可以转成 `NSError`,实现 `CustomNSError` 就可以完整的 `as NSError`.

###31. 下面的代码都用了哪些语法糖 / [1, 2, 3].map{ $0 * 2 }
`[1, 2, 3].map{ $0 * 2 }`

- 数组的语法糖 `[1, 2, 3]` 表示 `Array<Int>` 类型的数组
- 唯一参数尾随闭包, 可以省略括号
- `$0` 表示第一个参数, 省略了参数名的声明
- 单行表达式闭包可以省略 `return`

###32. 什么是高阶函数
- 以函数为参数的函数就是高阶函数
- 接收函数为参数, 并返回另一个函数的高阶函数为柯里化(Curry)函数

###33. 如何解决引用循环
- 使其中一个引用对象属性声明为 `weak` 或 `unowned`

###34. 下面的代码会不会崩溃,说出原因

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

- 不会崩溃, 引用唯一, 不会复制, 执行三次 `.removeLast()` 每次都会重新寻找下标

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

extension Collection where Iterator.Element == String {
func whatever() { }
}
["", ""].whatever()


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

- 被 `class` 修饰的方法可以被子类重写
- `static` 可以修饰结构体, 枚举和类的静态方法, `class` 只能修饰类的静态方法

---
##高级
###1. 一个 Sequence 的索引是不是一定从 0 开始?
- 不一定 可以自定义 `Generator` 改变其初始值, 还可以自定义排序方法
- 比如 `ArraySlice`
###2. 数组都实现了哪些协议

- `RandomAccessCollection` 
- `MutableCollection` : `MutableIndexable`, `Collection`
- ...还是看文档吧

###3. 如何自定义模式匹配
- 实现 `~=` 运算符, 第一个参数是 `case` 对象, 第二个参数是 `switch` 的对象

###4. autoclosure 的作用
- 把一句表达式自动封装成一个闭包

func f(_ foo: @autoclosure ()->Bool) { }

f(true)


###5. 编译选项 whole module optmization 优化了什么
- 减少编译时间, 编译时对同属一个 Module 的源码做整体分析, 而不是单文件分析
- 排除从不调用的函数

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

struct Person {

var name: String {
    mutating get {
        return store
    }
}

}

这个...写不写 `mutating` 的区别 不知道

###7. 如何让自定义对象支持字面量初始化
- ExpressibleByNilLiteral
- ExpressibleByIntegerLiteral
- ExpressibleByFloatLiteral
- ExpressibleByBooleanLiteral
- ExpressibleByStringLiteral
- ExpressibleByArrayLiteral
- ExpressibleByDictionaryLiteral
- 实现对应协议, 即可支持对应字面量初始化

###8. dynamic framework 和 static framework 的区别是什么
- 动态库文件名后缀:.dylib和.framework; 静态库文件名后缀:.a和.framework
- 静态库:链接时,静态库会被完整地复制到可执行文件中,被多次使用就有多份冗余拷贝
- 系统动态库:链接时不复制,程序运行时由系统动态加载到内存,供程序调用,系统只加载一次,多个程序共用,节省内存

---

##哲学
###1. 为什么数组索引越界会崩溃,而字典用下标取值时 key 没有对应值的话返回的是 nil 不会崩溃。
"一款语言必须要有一些基础的可以完全信任的东西,否则就会陷入循环验证的悖论。所以第一题的答案就是,数组取下标被设计为可以完全信任的操作。" -- [Enum](//www.greatytc.com/u/bbb690a0f0fb) 

###2.  一个函数的参数类型只要是数字(Int、Float)都可以,要怎么表示

func myMethod<T>(_ value: T) where T: ExpressibleByIntegerLiteral, T: ExpressibleByFloatLiteral {

}

-- [Arco_vv](//www.greatytc.com/u/07d93406ec39)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,783评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,360评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,942评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,507评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,324评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,299评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,685评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,358评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,652评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,704评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,465评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,318评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,711评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,991评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,265评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,661评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,864评论 2 335

推荐阅读更多精彩内容