目录1.Swift 有哪些特点它和 Objective-C 有什么区别2.Swift 中 let 和 var 有什么区别什么时候使用它们3.Swift 中的可选类型 Optional 是什么为什么需要它4.Swift 中如何安全地解包可选类型有哪些方式5.Swift 中的 guard 语句有什么作用和 if let 有什么区别回答重点6.Swift 中的闭包是什么它和函数有什么关系回答重点7.Swift 中什么是尾随闭包在什么场景下使用回答重点8.Swift 中的逃逸闭包和非逃逸闭包有什么区别回答重点9.Swift 的类型推断是如何工作的它有什么优势回答重点10.Swift 中的空合并运算符 ?? 是什么如何使用回答重点11.Swift 中的 class 和 struct 有什么区别如何选择使用回答重点12.Swift 中什么是值类型和引用类型有哪些值类型和引用类型回答重点13.Swift 中的协议 Protocol 是什么它有什么作用回答重点14.Swift 中什么是协议导向编程 POP它和面向对象编程有什么区别回答重点15.Swift 中的扩展 Extension 是什么它有哪些使用场景16.Swift 中的枚举有什么特别之处什么是关联值回答重点17.Swift 中的构造器有哪些类型convenience init 和 designated init 有什么区别回答重点18.Swift 中如何实现多态override 关键字的作用是什么回答重点19.Swift 的 ARC 自动引用计数是如何工作的回答重点20.Swift 中什么是循环引用如何避免循环引用回答重点21.Swift 中 weak 和 unowned 有什么区别如何选择使用回答重点22.Swift 中的泛型是什么为什么要使用泛型回答重点23.Swift 中如何给泛型添加约束associatedtype 是什么回答重点24.Swift 中如何处理错误throw、throws 和 try 有什么区别回答重点25.Swift 5.5 引入的 async/await 是什么如何使用回答重点26.Swift 中的 Actor 是什么它解决了什么问题回答重点27.Swift 中的属性包装器 Property Wrapper 是什么如何自定义回答重点28.Swift 中的访问控制有哪些级别它们有什么区别回答重点29.Swift 的高阶函数有哪些map、filter、reduce 分别做什么回答重点30.Swift 和 Objective-C 如何混编需要注意什么问题回答重点1.Swift 有哪些特点它和 Objective-C 有什么区别它的核心特点可以概括为三个词安全、快速、表现力强。安全性方面Swift 引入了可选类型Optional来处理空值这在编译期就能避免空指针异常。类型安全也是 Swift 的重要特性编译器会严格检查类型减少运行时错误。内存管理使用 ARC 自动引用计数开发者不需要手动管理内存。性能方面Swift 的执行效率接近 C 语言比 Objective-C 更快。这得益于 Swift 的编译优化和现代语言特性设计。表现力方面Swift 的语法简洁优雅代码可读性强。支持函数式编程、协议导向编程等多种编程范式让开发者能用更少的代码实现更多功能。和 Objective-C 相比最大的区别在于语法和安全性。Objective-C 是 C 语言的超集语法比较啰嗦而 Swift 语法更现代、更简洁。Objective-C 中可以给 nil 对象发送消息而不会崩溃但这也容易隐藏问题Swift 的可选类型强制开发者处理空值情况虽然写起来稍微麻烦一点但能避免很多潜在的 bug。扩展知识Swift 的核心优势Swift 作为苹果主推的编程语言在实际开发中展现出明显优势。首先是开发效率的提升Swift 的类型推断能自动推导出变量类型很多时候不需要显式声明。比如写let name Swift编译器自动知道这是字符串类型。其次是错误处理机制更完善。Swift 使用do-catch-throw模式处理错误相比 Objective-C 的NSError指针方式更直观。而且编译器会强制要求处理可能抛出的错误不会让异常被忽略。再就是泛型支持更强大。Swift 的泛型不仅可以用在函数上还能用在类、结构体、枚举上配合协议约束能写出高度抽象和复用的代码。从 Objective-C 到 Swift 的迁移虽然 Swift 优势明显但很多老项目仍在使用 Objective-C。好在 Swift 和 Objective-C 可以无缝混编在同一个项目中可以同时使用两种语言。Swift 可以调用 Objective-C 的代码反过来也可以这让渐进式迁移成为可能。不过需要注意的是Swift 的某些特性在 Objective-C 中无法使用比如泛型、元组、枚举的关联值等。如果要写给 Objective-C 调用的 Swift 代码需要加上objc标记并且只能使用 Objective-C 兼容的特性。目前苹果官方强烈推荐新项目使用 Swift 开发Swift 也在持续演进每年的 WWDC 都会发布新版本带来更多实用特性。从长远来看Swift 是 iOS 和 macOS 开发的未来方向。2.Swift 中 let 和 var 有什么区别什么时候使用它们let 和 var 都是 Swift 中声明变量的关键字它们的核心区别在于是否可以修改。let 声明的是常量一旦赋值后就不能再改变。这里的不能改变指的是不能重新赋值但如果是引用类型比如类的实例可以修改对象内部的属性只是不能让这个常量指向另一个对象。var 声明的是变量可以随时修改它的值。在实际开发中Swift 官方推荐优先使用 let只有在确实需要改变值的时候才用 var。这是一种防御性编程的思想因为不可变的数据更安全不会被意外修改代码也更容易理解和维护。而且编译器对常量有更好的优化性能上也会更好一点。比如在处理用户 ID、配置参数这类确定后不会改变的数据时就应该用 let。而对于计数器、临时结果这类需要不断更新的数据才用 var。扩展知识值类型和引用类型的区别let 和 var 的行为在值类型和引用类型上有些微妙的差异。对于值类型struct、enumlet 声明后整个值都不可变包括内部的属性都不能修改。struct Point { var x: Int var y: Int } let point Point(x: 10, y: 20) // point.x 15 // 错误不能修改但对于引用类型classlet 只是保证这个引用不变对象内部的属性还是可以修改的class Person { var name: String init(name: String) { self.name name } } let person Person(name: 张三) person.name 李四 // 可以修改属性 // person Person(name: 王五) // 错误不能改变引用编译器优化Swift 编译器对 let 常量有特殊优化。因为编译器知道这个值不会改变所以可以做一些激进的优化比如内联、常量传播等。在某些场景下使用 let 能带来可观的性能提升。而且在多线程环境下不可变数据天然是线程安全的不需要加锁保护这也是 let 的一个重要优势。实践建议养成习惯先写 let如果编译器报错说需要修改再改成 var。Xcode 也会给出警告提示哪些 var 可以改成 let。这种编程习惯能让代码更健壮bug 更少。在团队协作中看到 let 声明的变量其他开发者可以立即知道这个值不会改变不用担心在其他地方被修改代码的可预测性更强。3.Swift 中的可选类型 Optional 是什么为什么需要它回答重点可选类型是 Swift 最重要的特性之一用来表示一个值可能存在也可能不存在nil。在类型后面加个问号?就定义了一个可选类型比如String?表示可选的字符串。可选类型的变量要么包含一个具体的值要么是 nil。这和普通类型不同普通类型必须有值不能为 nil。为什么需要可选类型最核心的原因是为了安全。在很多编程语言中空指针异常是最常见的崩溃原因之一。Java、C 中的 NullPointerException 让无数程序员头疼因为你不知道一个对象是否为 null直到运行时才会暴露问题。Swift 通过可选类型把这个问题前移到编译期。编译器强制要求开发者明确处理值可能为空的情况不处理就无法编译通过。虽然写起来稍微麻烦一点但能避免大量的运行时崩溃。从本质上讲可选类型其实是一个枚举有两种情况有值.some(value)或没有值.none也就是 nil。这种设计让值是否存在变成了类型系统的一部分编译器能做更多的检查和优化。扩展知识可选类型的底层实现可选类型实际上是用泛型枚举实现的简化后的定义大概是这样enum OptionalWrapped { case none case some(Wrapped) }当我们写String?时实际上是OptionalString的语法糖。nil 就是.none有值的情况就是.some(值)。理解这一点很重要因为可选类型不是什么神秘的语言特性就是一个普通的枚举。这也解释了为什么可选类型可以用 switch 来处理let age: Int? 25 switch age { case .none: print(没有年龄信息) case .some(let value): print(年龄是 \(value)) }可选类型的使用场景在实际开发中可选类型用得非常频繁。字典取值返回的就是可选类型因为 key 可能不存在字符串转数字也返回可选类型因为转换可能失败从网络获取的数据往往也是可选的因为请求可能失败。let dict [name: Swift] let name dict[name] // String? 类型 let numberStr 123 let number Int(numberStr) // Int? 类型和其他语言的对比相比 Objective-C 中的 nil只能用于对象类型Swift 的可选类型更灵活任何类型都可以是可选的包括基本类型 Int、Double 等。很多现代语言也在借鉴可选类型的设计比如 Kotlin 的可空类型Nullable Types、Rust 的 Option 枚举都是类似的思想。这说明可选类型确实是解决空值问题的优秀方案。4.Swift 中如何安全地解包可选类型有哪些方式回答重点Swift 提供了多种解包可选类型的方式可以分为安全解包和强制解包两大类。安全解包的方式有四种。第一种是可选绑定Optional Binding使用 if let 或 guard let这是最常用的方式。if let 会在有值的时候执行代码块guard let 则在没有值的时候提前返回。第二种是可选链Optional Chaining用问号?.来调用可选类型的属性或方法如果是 nil 就直接返回 nil不会崩溃。第三种是空合并运算符??可以给可选类型提供一个默认值如果是 nil 就用默认值。第四种是使用 map 或 flatMap 等函数式方法对可选类型进行转换。强制解包是用感叹号!但这种方式很危险如果值是 nil 就会崩溃所以只有在百分百确定有值的情况下才能用。实际开发中应该尽量避免强制解包优先使用安全的解包方式。扩展知识各种解包方式的具体用法可选绑定是最推荐的方式代码清晰且安全let name: String? Swift if let unwrappedName name { print(名字是 \(unwrappedName)) } else { print(没有名字) }guard let 特别适合在函数开头做参数检查func greet(name: String?) { guard let name name else { print(名字不能为空) return } print(你好\(name)) }可选链在处理多层嵌套的可选类型时特别方便class Person { var address: Address? } class Address { var street: String } let person: Person? Person() let street person?.address?.street // 任何一层是 nil 都返回 nil空合并运算符让代码更简洁let username: String? nil let displayName username ?? 游客 // 如果 username 是 nil 就用游客隐式解包可选类型还有一种特殊的可选类型叫隐式解包可选类型Implicitly Unwrapped Optional用感叹号!声明比如String!。这种类型在使用时会自动解包不需要手动解包。它主要用在确定会有值但初始化时暂时是 nil 的场景比如 Interface Builder 连接的 IBOutlet。IBOutlet weak var label: UILabel! // 界面加载后一定有值但要注意如果在它还是 nil 的时候访问同样会崩溃所以使用时要格外小心。最佳实践在实际项目中建议按以下优先级选择解包方式优先使用可选绑定if let、guard let最安全链式调用用可选链?.代码更简洁需要默认值时用空合并运算符??尽量避免强制解包!除非确实有把握看到代码里有很多感叹号往往是个危险信号说明没有好好处理可选类型。优秀的 Swift 代码应该尽可能少用强制解包。5.Swift 中的 guard 语句有什么作用和 if let 有什么区别回答重点guard 语句是 Swift 中用于提前退出的控制流语句它的核心作用是让代码的正常流程更清晰把异常情况提前处理掉。guard 的语法是guard 条件 else { 退出代码 }当条件不满足时会执行 else 块中的代码必须通过 return、break、continue 或 throw 等方式退出当前作用域。最常见的用法是guard let来解包可选类型。和 if let 的主要区别在于代码结构和作用域。if let 是嵌套结构解包后的变量只能在 if 块内使用容易形成金字塔式的嵌套代码。而 guard let 解包后的变量可以在后续代码中使用代码呈扁平化结构可读性更好。另一个重要区别是语义上的。guard 表达的是守卫的含义用来检查前置条件不满足就提前退出。这种写法让函数的主要逻辑更清晰因为所有的边界检查都在开头完成了后面就是正常的业务逻辑不需要深层嵌套。实际开发中函数开头做参数校验时用 guard中间处理某个特定情况时用 if这样代码意图更明确。6.Swift 中的闭包是什么它和函数有什么关系回答重点闭包是一段可以在代码中传递和使用的功能代码块可以简单理解为没有名字的函数也叫匿名函数。闭包和函数本质上是一回事函数其实就是有名字的闭包。在 Swift 中函数和闭包都是引用类型都可以作为参数传递也可以作为返回值。闭包能做的事情函数都能做反过来也一样。闭包最大的特点是能够捕获和存储其所在上下文中的常量和变量即使定义这些常量和变量的原作用域已经不存在了闭包仍然可以使用和修改这些值。这就是闭包这个名字的由来——它能封闭并捕获外部的变量。在实际开发中闭包用得非常多。比如数组的 map、filter、sort 等方法都接收闭包作为参数异步网络请求的回调也是用闭包实现的。闭包让代码更简洁特别是那些只用一次的小功能没必要专门定义一个函数直接写个闭包就行。7.Swift 中什么是尾随闭包在什么场景下使用回答重点尾随闭包是 Swift 的一种语法糖当闭包是函数的最后一个参数时可以把闭包写在函数调用的括号外面让代码更简洁易读。如果闭包是唯一的参数甚至可以把函数调用的括号都省略掉。这种写法在闭包体比较长的时候特别有用能让代码结构更清晰。比如数组的 sorted 方法普通写法是array.sorted(by: { 闭包 })用尾随闭包可以写成array.sorted { 闭包 }。看起来像是在函数后面直接跟了个代码块读起来更自然。尾随闭包最适合的场景是那些把闭包作为主要操作的函数比如集合的 map、filter、forEach还有异步操作的回调函数。当闭包代码比较长时尾随闭包能避免代码嵌套太深提高可读性。不过如果闭包体很短比如就一行代码用不用尾随闭包区别不大根据个人喜好选择就好。8.Swift 中的逃逸闭包和非逃逸闭包有什么区别回答重点逃逸闭包和非逃逸闭包的区别在于闭包的生命周期和执行时机。非逃逸闭包是指在函数返回前就会执行完的闭包它不会逃出函数作用域。Swift 中的闭包参数默认都是非逃逸的这是更安全的选择因为编译器能做更多优化也不容易产生内存问题。逃逸闭包则是在函数返回后才执行的闭包需要用escaping关键字标记。最典型的场景就是异步操作的回调比如网络请求函数早就返回了但闭包要等网络响应回来才执行这时闭包就逃逸了。两者的区别还体现在对 self 的使用上。在非逃逸闭包中可以直接用 self因为不会产生循环引用。但在逃逸闭包中必须显式写 self提醒开发者注意可能的循环引用问题。从性能角度看非逃逸闭包因为生命周期短编译器可以把它优化为栈分配不需要堆内存效率更高。逃逸闭包必须在堆上分配因为要保证在函数返回后还能访问。9.Swift 的类型推断是如何工作的它有什么优势回答重点类型推断是 Swift 编译器的一项重要特性它能根据赋值的内容自动推导出变量的类型不需要开发者显式声明。工作原理很直观编译器会分析赋值表达式右边的值确定它的类型然后把这个类型赋给左边的变量。比如let name Swift编译器看到右边是字符串字面量就知道 name 是 String 类型。类型推断并不意味着 Swift 是弱类型语言相反 Swift 是强类型语言每个变量都有确定的类型。只是编译器帮我们自动推导了在编译期就确定好了类型运行时不会改变。类型推断的优势很明显。首先是代码更简洁不用写很多类型声明减少了代码量。其次是可读性更好特别是当类型名很长的时候省略类型能让代码重点更突出。最后是重构更容易如果改变了某个函数的返回类型不需要修改所有使用它的地方的类型声明。不过在某些情况下显式声明类型能提高代码可读性特别是类型不够明显的时候。所以类型推断和显式声明要根据实际情况灵活选择。10.Swift 中的空合并运算符 ?? 是什么如何使用回答重点空合并运算符??是用来给可选类型提供默认值的快捷方式它的作用是如果可选类型有值就使用这个值如果是 nil 就使用默认值。语法很简单可选值 ?? 默认值。比如let name username ?? 游客如果 username 有值就用 username如果是 nil 就用游客。这个运算符本质上是三元运算符的简化版。a ?? b等价于a ! nil ? a! : b但??的写法更简洁安全不需要强制解包。空合并运算符的好处是让代码更清晰。相比用 if let 或者三元运算符??表达的意图更明确——就是提供一个后备值。特别是在链式调用中??能让代码保持简洁流畅。实际开发中用得非常频繁比如处理用户输入、网络请求返回的可选数据、从字典取值等场景都可以用??来优雅地处理空值情况。11.Swift 中的 class 和 struct 有什么区别如何选择使用回答重点class 和 struct 最核心的区别是类型语义不同class 是引用类型struct 是值类型。值类型和引用类型的区别体现在赋值和传递时的行为。struct 在赋值或作为参数传递时会复制整个值创建一个独立的副本。而 class 传递的是引用多个变量可以指向同一个对象修改一个变量会影响到其他变量。具体功能上class 支持继承、多态、析构器可以用 deinit 做清理工作。struct 不支持继承但可以遵循协议。class 的实例存储在堆上需要 ARC 管理内存struct 通常存储在栈上性能更好。选择使用哪个要看具体场景。如果数据比较简单逻辑独立不需要继承优先用 struct。比如坐标点、尺寸、简单的数据模型。如果需要共享可变状态需要继承或者对象生命周期比较复杂就用 class。Swift 官方推荐优先使用 struct因为值类型更安全不容易产生意外的数据共享问题。Swift 标准库中的基础类型像 Int、String、Array、Dictionary 都是 struct 实现的这也体现了值类型在 Swift 中的重要地位。12.Swift 中什么是值类型和引用类型有哪些值类型和引用类型回答重点值类型和引用类型是 Swift 中两种基本的数据类型分类区别在于赋值和传递时的行为。值类型在赋值或传参时会复制整个值创建一个完全独立的副本。修改副本不会影响原来的值。就像复印文件复印件和原件互不影响。引用类型在赋值或传参时只复制引用可以理解为地址多个变量可以指向同一块内存。修改任何一个变量其他变量看到的都是修改后的值。就像给同一个文件起多个快捷方式改了文件所有快捷方式打开的都是改后的内容。Swift 中的值类型包括struct、enum、tuple以及所有基础类型比如 Int、Double、String、Bool、Array、Dictionary、Set 等。这些类型在标准库中都是用 struct 实现的。引用类型主要就是 class包括我们自定义的类以及 UIKit、Foundation 中的大部分类比如 NSObject、UIView、URLSession 等。闭包也是引用类型。值类型和引用类型的选择影响着程序的性能、安全性和设计模式理解它们的差异是掌握 Swift 的关键。13.Swift 中的协议 Protocol 是什么它有什么作用回答重点协议是 Swift 中定义接口和规范的方式可以理解为一份合同规定了遵循它的类型必须实现哪些方法和属性。协议只定义规范不提供实现。任何类型class、struct、enum都可以遵循协议只要实现了协议要求的内容就行。这让不同的类型可以有统一的接口提高了代码的灵活性和复用性。协议的作用主要有三个方面。首先是定义接口让不同的类型可以以相同的方式使用。比如定义一个 Drawable 协议不管是圆形、矩形还是三角形只要遵循了这个协议都能用相同的方式调用 draw 方法。其次是实现多态一个协议类型的变量可以存储任何遵循该协议的类型实例在运行时动态调用具体的实现。这比继承更灵活因为一个类型可以同时遵循多个协议而只能继承一个父类。第三是实现依赖注入和解耦。通过协议定义依赖而不是依赖具体的类型让代码更容易测试和维护。比如网络层定义一个 NetworkService 协议业务层只依赖协议不依赖具体实现这样可以轻松替换实现或者 mock 测试。Swift 推崇协议导向编程POP协议是 Swift 设计的核心之一理解协议是写好 Swift 代码的关键。14.Swift 中什么是协议导向编程 POP它和面向对象编程有什么区别回答重点协议导向编程Protocol-Oriented ProgrammingPOP是 Swift 推崇的编程范式核心思想是面向协议设计而不是面向类继承。POP 的核心理念是通过协议定义能力和行为通过协议扩展提供默认实现通过协议组合实现功能复用。相比传统的面向对象编程POP 更灵活、更模块化。和面向对象编程OOP的主要区别在于复用机制。OOP 通过继承实现代码复用一个类只能继承一个父类容易形成复杂的继承链而且父类的改变会影响所有子类。POP 通过协议组合实现复用一个类型可以遵循多个协议每个协议关注一个特定能力更加解耦和灵活。另一个重要区别是类型支持。OOP 主要针对引用类型class而 POP 对值类型struct、enum和引用类型一视同仁。Swift 的基础类型像 Int、String、Array 都是值类型用 POP 可以很好地扩展它们。POP 还避免了一些 OOP 的常见问题比如脆弱基类问题、菱形继承问题。通过协议扩展提供默认实现既保证了灵活性又避免了代码重复。实际开发中POP 和 OOP 不是对立的而是互补的。Swift 同时支持两种范式根据场景选择最合适的方式才是关键。15.Swift 中的扩展 Extension 是什么它有哪些使用场景扩展Extension是 Swift 中为已有的类型添加新功能的方式可以在不修改原始代码的情况下给类型增加方法、计算属性、构造器等。扩展最大的特点是可以扩展任何类型包括系统类型。你可以给 Int、String、Array 这些标准库类型添加新方法让它们更符合项目需求。也可以给自己的类型添加功能而不需要修改原来的定义。扩展能添加的内容包括计算属性不能添加存储属性、实例方法和类型方法、新的构造器、下标、嵌套类型以及让类型遵循协议。虽然限制比较多但已经足够强大。使用场景很广泛。最常见的是组织代码比如把一个类的不同功能分散到多个扩展中让代码结构更清晰。还可以用来添加协议遵循特别是标准库协议像 Equatable、Codable。另外就是扩展系统类型添加项目特定的便捷方法。扩展和继承不同扩展不能重写已有的方法只能添加新功能。而且扩展在编译期静态分派没有运行时开销性能很好。16.Swift 中的枚举有什么特别之处什么是关联值回答重点Swift 的枚举比其他语言强大得多不仅仅是定义一组常量它是一等公民类型功能非常丰富。Swift 枚举可以有关联值、原始值、方法、计算属性甚至可以遵循协议。这让枚举不只是简单的状态标识而是可以承载数据和逻辑的完整类型。关联值是 Swift 枚举的特色功能可以让每个枚举成员关联不同类型的值。比如定义一个二维码类型可以是字符串码也可以是数字码用关联值就能把不同类型的数据和枚举成员绑定在一起。关联值和原始值不同。原始值是所有成员共享同一种类型每个成员都有一个预设的值。关联值是每次创建枚举实例时动态指定的不同成员可以关联不同类型的值。Swift 的枚举还可以有方法这让枚举能封装相关的逻辑。比如网络请求结果的枚举可以有一个方法来处理不同的情况代码更内聚。可选类型 Optional 其实就是用枚举实现的有 none 和 some(value) 两种情况some 就是用关联值存储实际的值。理解了枚举的强大之处就能理解 Swift 很多设计的巧妙之处。17.Swift 中的构造器有哪些类型convenience init 和 designated init 有什么区别回答重点Swift 的构造器分为两种指定构造器Designated Initializer和便利构造器Convenience Initializer。指定构造器是类的主要构造器负责初始化所有属性必须调用父类的指定构造器完成继承链的初始化。每个类至少要有一个指定构造器它是初始化的主力军。便利构造器是辅助性的构造器用convenience关键字标记为类提供额外的初始化方式。便利构造器必须调用同一个类中的其他构造器最终会调用到指定构造器不能直接调用父类的构造器。两者的区别可以总结为指定构造器向上代理调用父类构造器便利构造器横向代理调用同类构造器。这种设计保证了初始化的安全性确保所有属性都被正确初始化。指定构造器通常比较少提供最基本最通用的初始化方式。便利构造器可以有多个提供各种快捷初始化方式内部调用指定构造器并提供默认参数。结构体没有这个区分所有构造器都是指定构造器。这是类特有的概念因为类有继承需要确保继承链上所有类的属性都正确初始化。18.Swift 中如何实现多态override 关键字的作用是什么回答重点Swift 中实现多态主要有两种方式类继承和协议。类继承的多态是传统的面向对象多态。子类继承父类可以重写父类的方法当用父类类型引用指向子类实例时调用重写的方法会执行子类的实现。这就是多态——同一个接口不同的实现。协议的多态更灵活。不同的类型只要遵循相同的协议就可以用协议类型统一处理调用协议方法时会执行各自的实现。这种多态不依赖继承关系更加解耦。override关键字用于标记子类重写父类的方法、属性或下标。它的作用有两个一是明确告诉编译器这是重写而不是新定义二是让编译器检查父类是否真的有这个成员避免拼写错误。重写时必须写override否则编译器会报错。如果写了override但父类没有对应成员编译器也会报错。这种强制性的标记避免了意外重写或拼写错误提高了代码安全性。final关键字和override相对用于阻止重写。给方法、属性或整个类加上final子类就不能重写了。这在设计框架时很有用可以保护关键方法不被改变。19.Swift 的 ARC 自动引用计数是如何工作的回答重点ARCAutomatic Reference Counting自动引用计数是 Swift 的自动内存管理机制用于管理类实例的内存。ARC 的工作原理很直接每个类实例都有一个引用计数表示有多少个引用指向它。当创建一个新引用指向实例时引用计数加 1当引用被销毁或指向其他对象时引用计数减 1。当引用计数降为 0 时说明没有任何引用指向这个实例了ARC 会自动释放这块内存。这个过程是在编译期自动插入的开发者不需要手动调用 retain 和 release这是 Objective-C 时代的事情了。编译器会分析代码在合适的位置插入增减引用计数的指令。ARC 只管理类实例的内存因为类是引用类型存储在堆上。结构体和枚举是值类型存储在栈上作用域结束自动释放不需要引用计数。虽然 ARC 很智能但也不是完美的。最大的问题是循环引用两个对象互相强引用导致引用计数永远不为 0内存泄漏。这时候需要用 weak 或 unowned 打破循环。20.Swift 中什么是循环引用如何避免循环引用回答重点循环引用是指两个或多个对象相互强引用导致引用计数永远无法降为 0造成内存泄漏。最典型的情况是对象 A 强引用对象 B对象 B 又强引用对象 A。这时候即使外部没有引用指向它们两个对象的引用计数都至少是 1ARC 无法释放它们内存就泄漏了。避免循环引用主要有三种方式使用 weak 弱引用、使用 unowned 无主引用或者打破引用关系。weak 引用不会增加引用计数而且当对象被释放时会自动设为 nil所以是可选类型。适合引用可能在生命周期中变为 nil 的情况最典型的就是 delegate。unowned 引用也不增加引用计数但不是可选类型对象释放后不会自动置空。适合引用的生命周期始终不短于被引用对象的情况比如父对象引用子对象时子对象引用父对象可以用 unowned。第三种方式是手动打破循环在合适的时机把某个引用设为 nil。比如在 deinit 或者某个清理方法中。21.Swift 中 weak 和 unowned 有什么区别如何选择使用回答重点weak 和 unowned 都是用来打破循环引用的弱引用方式但它们有重要的区别。weak 引用是可选类型当被引用的对象释放时会自动设为 nil。这意味着使用 weak 引用时需要解包而且要处理可能为 nil 的情况。weak 更安全因为即使对象被释放了访问 weak 引用也不会崩溃只是得到 nil。unowned 引用不是可选类型像普通引用一样可以直接使用但对象释放后不会自动置空。如果访问已经释放的 unowned 引用会导致崩溃。unowned 的优势是使用更方便不需要解包代码更简洁。选择哪个的原则很简单如果引用可能在生命周期中变为 nil用 weak如果引用在生命周期中肯定不为 nil而且不会比被引用对象活得更久用 unowned。典型场景是 delegate 用 weak因为 delegate 可能被设为 nil。而子对象引用父对象用 unowned因为子对象不会比父对象活得更久而且父对象肯定存在。22.Swift 中的泛型是什么为什么要使用泛型回答重点泛型是一种编写灵活、可复用代码的方式可以让函数、类、结构体、枚举适用于任何类型而不是固定的某一个类型。泛型的核心思想是用类型参数替代具体类型在使用时再指定具体类型。这就像函数参数一样函数定义时用形参调用时传实参。泛型是类型的参数。为什么要用泛型最大的好处是避免重复代码。如果没有泛型要实现一个交换两个 Int 的函数、交换两个 String 的函数、交换两个其他类型的函数代码几乎一样但要写很多遍。有了泛型一个函数就能处理所有类型。泛型还提供类型安全。相比用 Any 类型泛型在编译期就确定了类型不需要运行时类型转换更安全也更高效。编译器能做更多检查避免类型错误。Swift 标准库大量使用泛型。Array、Dictionary、Set、Optional 都是泛型类型这让它们既灵活又类型安全。理解泛型是掌握 Swift 的关键之一。23.Swift 中如何给泛型添加约束associatedtype 是什么回答重点泛型约束用来限制类型参数必须满足某些条件比如遵循某个协议或者继承某个类。添加约束的方式有两种。第一种是在类型参数后面用冒号指定比如T: Equatable表示 T 必须遵循 Equatable 协议。如果有多个约束用连接比如T: Equatable Codable。第二种是使用 where 子句写在函数签名或类型定义后面可以表达更复杂的约束。where 子句更灵活可以约束关联类型、指定类型相等等。associatedtype 是协议中的泛型机制用来定义关联类型。因为协议本身不能直接使用泛型参数所以用 associatedtype 来表示某个类型具体是什么由遵循协议的类型决定。关联类型让协议更灵活。比如定义一个容器协议不同的容器存储不同类型的元素用关联类型就能表达这种元素类型由具体容器决定的关系。遵循带关联类型的协议时可以用 typealias 显式指定关联类型也可以让编译器根据实现自动推断。大多数情况下自动推断就够了。24.Swift 中如何处理错误throw、throws 和 try 有什么区别回答重点Swift 使用do-catch-throw模式处理错误这是一种结构化的错误处理机制。throw 是抛出错误的关键字用在函数内部当遇到错误情况时用 throw 抛出一个遵循 Error 协议的错误值。throws 用在函数声明中标记这个函数可能抛出错误。调用 throws 函数时必须处理可能的错误要么用 try 捕获要么把错误继续向上抛。try 是调用 throws 函数时使用的关键字有三种变体普通 try 配合 do-catch 使用try? 把错误转为可选值try! 强制认为不会出错会抛错就崩溃。这三个关键字组成了完整的错误处理链条函数内部用 throw 抛出错误函数签名用 throws 声明可能抛错调用时用 try 处理错误。编译器会强制检查确保所有可能的错误都被处理了。相比 Objective-C 的 NSError 指针或者返回 nilSwift 的错误处理更清晰明确编译器能做更多检查不容易漏掉错误处理。25.Swift 5.5 引入的 async/await 是什么如何使用回答重点async/await 是 Swift 5.5 引入的结构化并发特性用于简化异步编程。async 关键字标记一个函数是异步的表示这个函数可能会挂起等待某些操作完成后再继续。异步函数可以在等待期间释放线程让线程去做其他事情提高效率。await 关键字用于调用异步函数标记这是一个可能挂起的点。在 await 处程序可能会暂停等待异步操作完成。await 只能在异步上下文中使用也就是异步函数内部或者 Task 里面。相比传统的回调和 DispatchQueueasync/await 让异步代码看起来像同步代码逻辑更清晰避免了回调地狱。错误处理也更简单可以直接用 try-catch不需要在回调中传递错误。async/await 和 GCD 不是替代关系而是更高层的抽象。底层依然是多线程和任务调度但开发者不需要关心这些细节只需要标记哪些地方是异步的编译器和运行时会处理好一切。26.Swift 中的 Actor 是什么它解决了什么问题回答重点Actor 是 Swift 5.5 引入的新类型用于保证并发安全防止数据竞争。Actor 类似于 class可以有属性和方法但有一个关键特性对 actor 的访问是互斥的同一时刻只有一个任务能访问 actor 的可变状态。这通过自动的同步机制实现开发者不需要手动加锁。Actor 解决的核心问题是数据竞争。在多线程环境下如果多个线程同时读写同一块数据可能导致数据不一致或崩溃。传统做法是用锁保护但容易出错。Actor 把同步机制内置到类型系统中编译器强制检查更安全。访问 actor 的属性和方法需要用 await因为可能需要等待其他任务完成。这让并发访问变得显式开发者能清楚知道哪里可能发生挂起。Actor 特别适合管理共享的可变状态比如缓存、计数器、状态管理器等。相比自己用锁保护的 classactor 更简洁、更安全编译器能帮你检查很多潜在问题。27.Swift 中的属性包装器 Property Wrapper 是什么如何自定义回答重点属性包装器Property Wrapper是 Swift 5.1 引入的特性用于在属性上添加通用的逻辑比如验证、转换、存储等避免重复代码。属性包装器本质上是一个结构体、类或枚举用propertyWrapper标记必须有一个wrappedValue属性。使用时在属性前加包装器名称属性的读写会自动通过包装器的wrappedValue进行。最典型的应用是 SwiftUI 中的State、Binding、Published等它们都是属性包装器。State让属性变化时自动刷新 UIPublished让属性变化时发出通知这些逻辑都封装在包装器中。自定义属性包装器很简单定义一个类型加上propertyWrapper标记实现wrappedValue属性即可。还可以提供projectedValue来暴露额外的功能通过$前缀访问。属性包装器让代码更简洁、更声明式把通用的属性逻辑抽取出来复用这是 Swift 强大表达力的体现。28.Swift 中的访问控制有哪些级别它们有什么区别回答重点Swift 有五种访问控制级别从限制最严到最宽依次是private、fileprivate、internal、public、open。private 是最严格的只能在定义的作用域内访问比如类内部、结构体内部、扩展内部。同一个文件的其他类型也访问不了。fileprivate 放宽到整个文件同一个源文件中的所有代码都能访问但其他文件不行。适合文件内的多个类型需要共享某些实现细节的场景。internal 是默认级别在整个模块内部都能访问。模块通常是一个 target比如你的 App、一个 Framework。同一模块内随便用但模块外不行。public 可以被其他模块访问但不能被继承或重写。适合提供给外部使用但不想被修改的 API。open 是最开放的不仅可以被其他模块访问还能被继承和重写。适合设计给外部扩展的框架类。选择访问级别的原则是尽量限制只暴露必要的接口。这样可以隐藏实现细节降低耦合让代码更容易维护和重构。29.Swift 的高阶函数有哪些map、filter、reduce 分别做什么回答重点高阶函数是接收函数作为参数或返回函数的函数Swift 的集合类型提供了很多实用的高阶函数。map 用于转换对集合中的每个元素应用一个转换函数返回转换后的新集合。比如把整数数组的每个元素乘以 2或者把对象数组转换为字符串数组都可以用 map。filter 用于筛选根据条件过滤集合中的元素只保留满足条件的元素。比如从数组中筛选出大于 10 的数字或者筛选出符合某个条件的对象。reduce 用于归约把集合中的所有元素组合成一个值。比如求和、求积、拼接字符串等。reduce 接收一个初始值和一个组合函数依次处理每个元素最终返回一个结果。除了这三个还有 compactMap过滤 nil、flatMap展平嵌套、forEach遍历、sorted排序、contains包含判断等常用高阶函数。使用高阶函数能让代码更简洁、更声明式避免手写循环提高可读性。而且这些函数都是链式调用可以组合使用实现复杂的数据处理逻辑。30.Swift 和 Objective-C 如何混编需要注意什么问题回答重点Swift 和 Objective-C 可以在同一个项目中无缝混编这是苹果为了平滑过渡设计的重要特性。从 Swift 调用 Objective-C 代码需要创建一个桥接头文件Bridging Header在里面 import 需要使用的 Objective-C 头文件。Xcode 会在第一次添加不同语言文件时自动提示创建文件名通常是项目名-Bridging-Header.h。从 Objective-C 调用 Swift 代码需要 import 自动生成的 Swift 头文件格式是项目名-Swift.h。这个文件是编译器自动生成的包含了所有暴露给 Objective-C 的 Swift 接口。Swift 的类需要继承 NSObject方法需要加objc才能被 Objective-C 看到。混编时需要注意几个问题。首先是类型转换Swift 的很多类型在 Objective-C 中没有对应比如元组、泛型、可选类型的内部机制。其次是命名规范Objective-C 没有命名空间Swift 的命名空间概念在 Objective-C 中会变成类名前缀。再就是协议Swift 的协议有些特性 Objective-C 不支持。总体来说常见的 API 都能很好地互操作但 Swift 的高级特性不能暴露给 Objective-C。混编适合渐进式迁移老项目或者使用 Objective-C 写的第三方库。