美文网首页
面试点汇总

面试点汇总

作者: 坤哥爱卿 | 来源:发表于2025-02-27 15:22 被阅读0次

1、对一个 optional 变量拆包有多少种方法?

答案:
强制拆包 !
操作符——不安全
隐式拆包变量声明——大多数情况下不安全
可选绑定——安全
自判断链接(optional chaining)——安全
nil coalescing 运算符(空值合并运算符)——安全
Swift 的新特性 guard 语句——安全
Swift 的新特性 optional pattern(可选模式) ——安全(@Kametrixom支持)

2、你能通过extension (扩展)保存一个属性吗?

答案:不能。扩展可以给当前的类型添加新的行为,但是不能改变本身的类型或者本身的接口。如果你添加一个新的可存储的属性,你需要额外的内存来存储新的值。扩展并不能实现这样的任务。

3、用泛型来声明枚举的问题

在 Swift 中,你能解释一下用泛型来声明枚举的问题吗?拿下面代码中 Either 枚举来举例说明吧,它有两个泛型类型的参数 T 和 V,参数 T 在关联值类型为left情况下使用,参数 V在关联值为 rihgt 情况下使用,代码如下:

enum Either{
  case Left(T)
  case Right(V)
}

答案:上面的代码会出现编译错误:
unimplemented IR generation feature non-fixed multi-payload enum layout
问题是 T 的内存大小不能确定, 因为它依赖于T类型本身,但 enum 情况下需要一个固定大小的有效载荷。

最常用的解决方法是讲泛类型用引用类型包装起来,通常称为 box,代码如下:

class Box{
  let value: T
  init(_ value: T) {
    self.value = value
  }
}
  
enum Either{
  case Left(Box)
  case Right(Box)
}

4、闭包是引用类型?

答案:闭包是引用类型。如果一个闭包被分配给一个变量,这个变量复制给另一个变量,那么他们引用的是同一个闭包,他们的捕捉列表也会被复制。

5、如何把一个负整数转换成一个无符号的整数?

UInt 类型是用来存储无符号整型的。下面的代码实现了一个有符号整型转换的初始化方法:

然而,在下面的代码中,当你给一个负值的时候,它会产生一个编译时错误:

let myNegative = UInt(-1)

答案:使用下面的初始化方法:

// 定义一个负整数
let negativeInt: Int = -1
// 将其转换为无符号整数(这里使用 UInt 类型)
let unsignedInt = UInt(bitPattern: negativeInt)
print("转换后的无符号整数: \(unsignedInt)")

6、什么关键字可以实现递归枚举?

答案:indirect 关键值可以允许递归枚举,代码如下:

enum List{
 indirect case Cons(T, List)
}

7、描述一种在 Swift 中出现循环引用的情况,并说明怎么解决。

答案:循环引用出现在当两个实例对象相互拥有强引用关系的时候,这会造成内存泄露,原因是这两个对像都不会被释放。只要一个对象被另一个对象强引用,那么该对象就不能被释放,由于强引用的存在,每个对象都会保持对方存在。
解决这个问题的方法是,用 weak 或者 unowned 引用代替其中一个的强引用,来打破循环引用。

8、Swift中的泛型?

泛型 可以将类型 参数化,提高代码复用率,减少代码量。
Swift泛型函数 并不会 在底层 生成 若干个 (匹配类型)函数 ,产生函数重载,而是: 在函数调用时,会将 参数的类型 传递给 目标函数。
Swift泛型应用在协议上时,需要使用关联类型(associatedtype)。
泛型约束 可以 更精确的知道 参数 需要 遵循什么标准。
//someT遵循的是某个class,someU遵循的是某个协议,这样在传参的时候明确参数类型

func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
    // 这里是泛型函数的函数体部分
}

9、Swift 为什么将 Array,String,Dictionary,Set,设计为值类型?

值类型 相比 引用类型的优点

值类型和引用类型相比,最大优势可以高效的使用内存;
值类型在栈上操作,引用类型在堆上操作;
栈上操作仅仅是单个指针的移动,
堆上操作牵涉到合并,位移,重链接

Swift 这样设计减少了堆上内存分配和回收次数,使用 copy-on-write将值传递与复制开销降到最低
String,Array,Dictionary设计成值类型,也是为了线程安全考虑。通过Swift的let设置,使得这些数据达到了真正意义上的“不变”,它也从根本上解决了多线程中内存访问和操作顺序的问题。

10、Swift 和 SwiftUI 中监听属性值变化的几种方法?

1. 属性观察器

属性观察器允许在属性值即将被设置或已经被设置时执行特定的代码。

class MyClass {
    var myProperty: Int = 0 {
        willSet {
            print("New value will be set: \(newValue)")
        }
        didSet {
            print("Old value was: \(oldValue)")
        }
    }
}

let instance = MyClass()
instance.myProperty = 5
// 输出:
// New value will be set: 5
// Old value was: 0

2. Swift 闭包

class MyClass {
    var myProperty: Int = 0 {
        didSet {
            propertyDidChange?(myProperty)
        }
    }
    
    var propertyDidChange: ((Int) -> Void)?
}

let instance = MyClass()
instance.propertyDidChange = { newValue in
    print("Property did change: \(newValue)")
}

instance.myProperty = 5
// 输出: Property did change: 5

3. Swift Combine 框架

Combine 框架是 Swift 中用于处理异步事件流的框架,可以用于监听属性值的变化

import Combine

class MyClass {
    @Published var myProperty: Int = 0
}
let instance = MyClass()
let cancellable = instance.$myProperty.sink { value in
    print("New value: \(value)")
}
instance.myProperty = 5
// 输出: New value: 5

4. SwiftUI 监听属性值变化

在 SwiftUI 中,可以使用属性包装器来监听属性值的变化,常用的包装器有 @State、@Binding、@ObservedObject 和 @EnvironmentObject。

@State

struct ContentView: View {
    @State private var count: Int = 0
    
    var body: some View {
        VStack {
            Text("Count: \(count)")
            Button("Increment") {
                count += 1
            }
        }
    }
}

ObservedObject

class MyModel: ObservableObject {
    @Published var count: Int = 0
}

struct ContentView: View {
    @ObservedObject var model: MyModel
    
    var body: some View {
        VStack {
            Text("Count: \(model.count)")
            Button("Increment") {
                model.count += 1
            }
        }
    }
}

@Binding

struct ParentView: View {
    @State private var count: Int = 0
    
    var body: some View {
        VStack {
            ChildView(count: $count)
            Button("Increment") {
                count += 1
            }
        }
    }
}

struct ChildView: View {
    @Binding var count: Int
    
    var body: some View {
        Text("Count: \(count)")
    }
}

@EnvironmentObject

class UserData: ObservableObject {
    @Published var username = "John"
}

struct ContentView: View {
    @EnvironmentObject var userData: UserData
    
    var body: some View {
        VStack {
            Text("Hello, \(userData.username)!")
        }
    }
}

struct MainView: View {
    var body: some View {
        ContentView()
            .environmentObject(UserData())
    }
}

11、Swift 高阶函数

Swift 中的高阶函数是指那些以其他函数作为参数或者返回值的函数。这些函数通常用于简化代码、提高可读性和功能性。常见的高阶函数包括 map、filter、reduce 、flatMap、compactMap、allSatisfy 等

map 函数用于对集合中的每个元素应用一个指定的转换闭包,然后返回一个包含转换结果的新集合。

let numbers = [1, 2, 3, 4, 5]
let doubled = numbers.map { $0 * 2 }
print(doubled) // 输出 [2, 4, 6, 8, 10]

filter 函数用于从集合中选择满足指定条件的元素,并返回一个包含满足条件的元素的新集合。

let numbers = [1, 2, 3, 4, 5]
let evenNumbers = numbers.filter { $0 % 2 == 0 }
print(evenNumbers) // 输出 [2, 4]

reduce 函数用于将集合中的所有元素组合成单个值,并返回该值。

let numbers = [1, 2, 3, 4, 5]
let sum = numbers.reduce(0, { $0 + $1 })
print(sum) // 输出 15

flatMap 函数用于对集合中的每个元素应用一个转换闭包,并将结果拼接成一个新的集合。

let nestedArray = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
let flatArray = nestedArray.flatMap { $0 }
print(flatArray) // 输出 [1, 2, 3, 4, 5, 6, 7, 8, 9]

compactMap 函数用于对集合中的每个元素应用一个转换闭包,并过滤掉结果中的 nil 值。

let strings = ["1", "2", "3", "hello", "5"]
let numbers = strings.compactMap { Int($0) }
print(numbers) // 输出 [1, 2, 3, 5]

allSatisfy 函数用于检查序列中的所有元素是否都满足指定条件

let numbers = [2, 4, 6, 8, 10]
let allEven = numbers.allSatisfy { $0 % 2 == 0 }
print(allEven) // 输出 true

12、Swift 中的错误处理机制如何工作?

Swift 中的错误处理机制允许程序在运行时检测和响应错误条件。Swift 提供了强大的错误处理模型,包括抛出、捕捉和传递错误。

1. 定义错误类型:首先,通过实现Error协议来定义可能发生的错误类型。这些错误类型通常是枚举,用来表示不同的错误情况。

2. 抛出错误:使用throw关键字来抛出一个错误。这通常发生在函数内部,当函数遇到无法解决的问题时,它会抛出一个错误,以表示失败。

3. 标记抛出错误的函数:在函数声明中使用throws关键字来标记这个函数可能会抛出错误。这意味着调用这个函数的代码需要处理这些错误。

4. 捕捉和处理错误:使用do-catch语句来捕捉和处理错误。在do代码块中调用可能抛出错误的代码。如果在do块中抛出了错误,控制流会转移到catch块,让你有机会响应和处理错误。

5. 使用try、try?和try!来调用抛出错误的函数,分别表示:检查错误并使用do-catch处理、将结果转换为可选值(如果发生错误则结果为nil)、断言调用不会抛出错误(如果实际抛出错误,则会触发运行时错误)。

这种错误处理机制使得Swift代码能够优雅地处理错误和异常情况,提高了程序的健壮性和可维护性。

13、Swift 中 ARC(自动引用计数)的工作原理。

自动引用计数(ARC)是Swift中用于内存管理的机制。ARC自动跟踪和管理应用程序的内存使用,确保当实例不再被需要时释放它们占用的内存。ARC的工作原理如下:

1. 每当创建一个类的新实例时,ARC 会分配一块内存来存储该实例的信息,包括实例的类型信息以及与之相关的存储属性。

2. 为了确保实例在使用中不被销毁,ARC 会跟踪和计算每个实例的引用次数。每次你将实例赋给属性、常量或变量时,引用计数会加一。

3. 当引用计数变为零时,即没有任何属性、常量或变量引用该实例,ARC 会自动释放该实例所占用的内存。

4. 为了防止循环引用导致的内存泄漏,Swift 使用强引用(默认行为)、弱引用和无主引用来区分引用类型。弱引用和无主引用不会增加实例的引用计数。

通过这种方式,ARC帮助Swift开发者管理内存,大大简化了内存管理的工作,但开发者仍需注意避免循环引用等问题。

14、Swift 中的动态派发是什么,它是如何工作的?

动态派发是一种运行时决定方法调用的机制。在Swift中,动态派发主要通过虚拟派发表实现,这涉及到引用类型如类(class)。动态派发允许Swift在运行时选择响应消息的最终实现,这为方法重写和多态提供了基础。

1. 当你调用一个类的方法时,Swift运行时会查找这个类的虚拟派发表,找到对应方法的实际实现地址,然后跳转到这个地址执行方法。

2. 由于动态派发的存在,Swift可以在运行时而非编译时决定调用哪个方法的实现,这增加了程序的灵活性,但也可能略微降低性能。

3. Swift中默认情况下类的方法是动态派发的。然而,通过使用final关键字标记方法或类,可以阻止方法被重写,从而允许编译器优化调用,采用更快的静态派发。

动态派发是面向对象编程中的一个关键概念,它使得子类可以定制或改变继承而来的行为。

相关文章

  • Cookie测试点汇总

    1.禁止使用Cookie设置浏览器禁止使用Cookie,访问网页后,检查存放Cookie文件中未生成相关文件; 2...

  • APP测试点汇总

    关键词:移动端测试 一、APP测试点 1、数据的排序是否正确; 2、界面跳转是否正确; 3、出现异常情...

  • 裸面被残虐,阿里电面试题汇总

    裸面被残虐,阿里电面试题汇总 裸面被残虐,阿里电面试题汇总

  • 用户界面测试点汇总

    关键词:界面测试 所谓界面测试就是指,布局是否合理、整体风格是否一致、各个控件的放置位置是否符合客户使用习惯...

  • 2014-2019年,国家在推动编程教育的政策有哪些?

    下面为大家汇总了近几年国家推动编程教育发布的一系列政策: 2014年9月 浙江省提出《深化高考改革试点方案》,信息...

  • 360面经汇总

    来源:cv.qiaobutang.com/post/55b05e670cf2b4161e7707c8

  • 面经汇总(更新)

    面试的问题都是大同小异的,每轮面试结束后最好对问题做一个复盘的记录总结,不断迭代出最全面最佳的回答。 一定得自信,...

  • NLP面经汇总

    1.竞技世界 这次面试的体验非常棒 从项目入手,然后延申开来问一些基础知识以及代码实现 1. 根据我简历上的...

  • 顺丰快递发大招:收寄人信息都不显示

    今年5月17日,顺丰首度对外披露已展开试点的“丰密面单”。目前,“丰密面单”正式上线。该面单将可实现收寄件人姓名、...

  • 大数据项目测试点汇总图

    大数据项目越来越多,希望这份汇总图能给大家带来帮助,此图大部分测试点由公司小伙伴王薇、杨文晶、李春辉共同整理。![...

网友评论

      本文标题:面试点汇总

      本文链接:https://www.haomeiwen.com/subject/fmxnpjtx.html