swift面试题及答案

1   MVVM

给MVC解耦 解重  双向绑定
详情解答 https://blog.csdn.net/sun6223508/article/details/95453167 

Alamofire怎么实现的

使用线程池  文件图片上传代码基于TCP/IP  网络提交基于URLSession.dataTask  URLRequest

3 frame 和 bounds 有什么不同?

frame指的是:该view在父view坐标系统中的位置和大小。(参照点是父view的坐标系统)
bounds指的是:该view在本身坐标系统中的位置和大小。(参照点是屏幕坐标系统)

4 runtime是怎么实现的 ,那些地方用到了

obj_msgsend     
黑魔法  tableview没数据时  添加一个view

字典转模型   模型转字典 class_propertyList   property_getName

自定义KVO   isa指针  NSKVONotifying_B
详情 https://blog.csdn.net/sun6223508/article/details/96101302 
 

5 runloop流程

6  浅拷贝和深拷贝的区别? 

浅拷贝:只复制指向对象的指针,而不复制引用对象本身。
深拷贝:复制引用对象本身。内存中存在了两份独立对象本身,当修改A时,A_copy不变。

7 怎么定义数组

var  arry:[String] = []  或  var arr:Array<String> = Array<String>()

8 switf 2.0增加了一个新的关键字来实现递归枚举。enum List{ case Node(T,List)}什么关键字可以实现递归枚举?

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

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

9 Category(类别)、 Extension(扩展)和继承的区别

区别:
1. 分类有名字,类扩展没有分类名字,是一种特殊的分类。
2. 分类只能扩展方法(属性仅仅是声明,并没真正实现),类扩展可以扩展属性、成员变量和方法。
3. 继承可以增加,修改或者删除方法,并且可以增加属性。

10 oc 系统对象的 copy 与 mutableCopy 方法

一、非集合类对象的copy与mutableCopy
  在非集合类对象中,对不可变对象进行copy操作,是指针复制,mutableCopy操作是内容复制;
  对可变对象进行copy和mutableCopy都是内容复制。用代码简单表示如下:
    NSString *str = @"hello word!";
    NSString *strCopy = [str copy] // 指针复制,strCopy与str的地址一样
    NSMutableString *strMCopy = [str mutableCopy] // 内容复制,strMCopy与str的地址不一样

    NSMutableString *mutableStr = [NSMutableString stringWithString: @"hello word!"];
    NSString *strCopy = [mutableStr copy] // 内容复制
    NSMutableString *strMCopy = [mutableStr mutableCopy] // 内容复制

二、集合类对象的copy与mutableCopy (同上)
  在集合类对象中,对不可变对象进行copy操作,是指针复制,mutableCopy操作是内容复制;
  对可变对象进行copy和mutableCopy都是内容复制。但是:集合对象的内容复制仅限于对象本身,对集合内的对象元素仍然是指针复制。(即单层内容复制)
    NSArray *arr = @[@[@"a", @"b"], @[@"c", @"d"];
    NSArray *copyArr = [arr copy]; // 指针复制
    NSMutableArray *mCopyArr = [arr mutableCopy]; //单层内容复制
   
    NSMutableArray *array = [NSMutableArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c",nil];
    NSArray *copyArr = [mutableArr copy]; // 单层内容复制
    NSMutableArray *mCopyArr = [mutableArr mutableCopy]; // 单层内容复制
    
【总结一句话】:
    只有对不可变对象进行copy操作是指针复制(浅复制),其它情况都是内容复制(深复制)!

11 ViewController生命周期

按照执行顺序排列:
1. initWithCoder:通过nib文件初始化时触发。
2. awakeFromNib:nib文件被加载的时候,会发生一个awakeFromNib的消息到nib文件中的每个对象。      
3. loadView:开始加载视图控制器自带的view。
4. viewDidLoad:视图控制器的view被加载完成。  
5. viewWillAppear:视图控制器的view将要显示在window上。
6. updateViewConstraints:视图控制器的view开始更新AutoLayout约束。
7. viewWillLayoutSubviews:视图控制器的view将要更新内容视图的位置。
8. viewDidLayoutSubviews:视图控制器的view已经更新视图的位置。
9. viewDidAppear:视图控制器的view已经展示到window上。 
10. viewWillDisappear:视图控制器的view将要从window上消失。
11. viewDidDisappear:视图控制器的view已经从window上消失。

12 如何对iOS设备进行性能测试?

 Profile-> Instruments ->Time Profiler

13  开发项目时你是怎么检查内存泄露?

1). 静态分析 analyze。
2). instruments工具里面有个leak可以动态分析。

14 delegate 和 notification 的区别

1). 二者都用于传递消息,不同之处主要在于一个是一对一的,另一个是一对多的。
2). notification通过维护一个array,实现一对多消息的转发。
3). delegate需要两者之间必须建立联系,不然没法调用代理的方法;notification不需要两者之间有联系。

15 lldb(gdb)常用的控制台调试命令?

1). p 输出基本类型。是打印命令,需要指定类型。是print的简写
    p (int)[[[self view] subviews] count]
2). po 打印对象,会调用对象description方法。是print-object的简写
    po [self view]
3). expr 可以在调试时动态执行指定表达式,并将结果打印出来。常用于在调试过程中修改变量的值。
4). bt:打印调用堆栈,是thread backtrace的简写,加all可打印所有thread的堆栈
5). br l:是breakpoint list的简写

16 你一般是怎么用Instruments的? 

Instruments里面工具很多,常用:
1). Time Profiler: 性能分析
2). Zombies:检查是否访问了僵尸对象,但是这个工具只能从上往下检查,不智能。
3). Allocations:用来检查内存,写算法的那批人也用这个来检查。
4). Leaks:检查内存,看是否有内存泄露。

17  iOS的沙盒目录结构是怎样的?

沙盒结构:
1). Application:存放程序源文件,上架前经过数字签名,上架后不可修改。
2). Documents:常用目录,iCloud备份目录,存放数据。(这里不能存缓存文件,否则上架不被通过)
3). Library:
        Caches:存放体积大又不需要备份的数据。(常用的缓存路径)
        Preference:设置目录,iCloud会备份设置信息。
4). tmp:存放临时文件,不会被备份,而且这个文件下的数据有可能随时被清除的可能。

18 什么是 TCP / UDP ? 

TCP:传输控制协议。
UDP:用户数据协议。

TCP 是面向连接的,建立连接需要经历三次握手,是可靠的传输层协议。
UDP 是面向无连接的,数据传输是不可靠的,它只管发,不管收不收得到。
简单的说,TCP注重数据安全,而UDP数据传输快点,但安全性一般。

19 通信底层原理(OSI七层模型)  

OSI采用了分层的结构化技术,共分七层:
    物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。

20 tableView的重用机制?

UITableView 通过重用单元格来达到节省内存的目的: 通过为每个单元格指定一个重用标识符,即指定了单元格的种类,当屏幕上的单元格滑出屏幕时,系统会把这个单元格添加到重用队列中,等待被重用,当有新单元格从屏幕外滑入屏幕内时,从重用队列中找看有没有可以重用的单元格,如果有,就拿过来用,如果没有就创建一个来使用。

21不用中间变量,用两种方法交换A和B的值

// 1.中间变量
void swap(int a, int b) {
   int temp = a;
   a = b;
   b = temp;
}

// 2.加法
void swap(int a, int b) {
   a = a + b;
   b = a - b;
   a = a - b;
}

// 3.异或(相同为0,不同为1. 可以理解为不进位加法)
void swap(int a, int b) {
   a = a ^ b;
   b = a ^ b;
   a = a ^ b;
}

22 排序算法  选择排序、冒泡排序、插入排序三种排序算法

/** 
 *  【选择排序】:最值出现在起始端
 *  
 *  第1趟:在n个数中找到最小(大)数与第一个数交换位置
 *  第2趟:在剩下n-1个数中找到最小(大)数与第二个数交换位置
 *  重复这样的操作...依次与第三个、第四个...数交换位置
 *  第n-1趟,最终可实现数据的升序(降序)排列。
 *
 */
void selectSort(int *arr, int length) {
    for (int i = 0; i < length - 1; i++) { //趟数
        for (int j = i + 1; j < length; j++) { //比较次数
            if (arr[i] > arr[j]) {
                int temp = arr[i];
                arr[i] = arr[j];

                arr[j] = temp;
            }
        }
    }
}
/** 
 *  【冒泡排序】:相邻元素两两比较,比较完一趟,最值出现在末尾
 *  第1趟:依次比较相邻的两个数,不断交换(小数放前,大数放后)逐个推进,最值最后出现在第n个元素位置
 *  第2趟:依次比较相邻的两个数,不断交换(小数放前,大数放后)逐个推进,最值最后出现在第n-1个元素位置
 *   ……   ……
 *  第n-1趟:依次比较相邻的两个数,不断交换(小数放前,大数放后)逐个推进,最值最后出现在第2个元素位置 
 */
void bublleSort(int *arr, int length) {
    for(int i = 0; i < length - 1; i++) { //趟数
        for(int j = 0; j < length - i - 1; j++) { //比较次数
            if(arr[j] > arr[j+1]) {
                int temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
        } 
    }
}
/**
 *  折半查找:优化查找时间(不用遍历全部数据)
 *
 *  折半查找的原理:
 *   1> 数组必须是有序的
 *   2> 必须已知min和max(知道范围)
 *   3> 动态计算mid的值,取出mid对应的值进行比较
 *   4> 如果mid对应的值大于要查找的值,那么max要变小为mid-1
 *   5> 如果mid对应的值小于要查找的值,那么min要变大为mid+1
 *
 */ 

// 已知一个有序数组, 和一个key, 要求从数组中找到key对应的索引位置 
int findKey(int *arr, int length, int key) {
    int min = 0, max = length - 1, mid;
    while (min <= max) {
        mid = (min + max) / 2; //计算中间值
        if (key > arr[mid]) {
            min = mid + 1;
        } else if (key < arr[mid]) {
            max = mid - 1;
        } else {
            return mid;
        }
    }
    return -1;
}

23 求最大公约数

/** 1.直接遍历法 */
int maxCommonDivisor(int a, int b) {
    int max = 0;
    for (int i = 1; i <=b; i++) {
        if (a % i == 0 && b % i == 0) {
            max = i;
        }
    }
    return max;
}
/** 2.辗转相除法 */
int maxCommonDivisor(int a, int b) {
    int r;
    while(a % b > 0) {
        r = a % b;
        a = b;
        b = r;
    }
    return b;
}


// 扩展:最小公倍数 = (a * b)/最大公约数

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

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

25 闭包是引用类型吗? 

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

26 你能通过extension(扩展)保存一个属性吗?请解释一下原因。

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

27 在Objective-C中,一个常量可以这样定义: const int number = 0   类似的Swift是这样定义的:

  let number = 0 两者之间有什么不同吗?如果有,请说明原因。

const常量是一个在编译时或者编译解析时被初始化的变量。通过let创建的是一个运行时常量,是不可变的。它可以使用stattic或者dynamic关键字来初始化。谨记它的值只能被分配一次。

28 对一个optional变量拆包有多少种方法?并在安全方面进行评价。

  答案:强制拆包 !操作符——不安全

     隐式拆包变量生命——大多数情况下不安全

     可选绑定——安全

     自判断链接(optional chaining)——安全

     nil coalescing运算符(空值合并运算符)——安全

     Swift2.0的新特性guard语句——安全

     Swift2.0的新特性optional pattern(可选模式)——安全

29 在Swift中,什么时候用结构体,什么时候用类?

  答案:在Swift中,类和结构体有许多不同的特性。下面是两者不同的总结:

  类支持继承,结构体不支持。

  类是引用类型,结构体是值类型

  并没有通用的规则决定结构体和类哪一个更好用。一般的建议是使用最小的工具来完成你的目标,但是有一个好的经验是多使用结构体,除非你用了继承和引用语义。

  注意:在运行时,结构体在性能方面更优于类,原因是结构体的方法调用是静态绑定,而类的方法调用是动态实现的。这就是尽可能得使用结构体代替类的又一个好的原因。

30  下面的代码输出是什么?并说明理由。

  var thing = "cars"    

  let clousure = {[thing] in print("I love (thing)")}

  thing = "airplanes"

  closure()

  答案:输出的是:I love cars。当闭包被声明的时候,捉捕列表就复制一份thing变量,所以被捕捉的值并没有改变,即使你给thing赋予了一个新值。

  如果你要忽视闭包中捕捉列表的值,那么编译器引用那个值而不是复制。这种情况下,被引用变量的值的变化将会反映到闭包中,正如下面的代码所示:

  var thing = "cars"

  let closure = { print("I love (thing)")}

  thing = "airplanes"

  Prints"I love airplanes"

31 思考下面的代码:

  代码:

  var optional1:String? = nil

  var optional2:String? = .None

  nil和.None有什么不同?optional1和optional2有什么不同?

  答案:两者没有什么不同。

  Optional.None(简称.None)是optional变量值初始化的标准方法,而nil只是.None语法的一种修饰。事实上下面语句输出是正确的:

  nil == .None//On Swift1.x this doesnt compile.You need Optional.None记住枚举类型的Optional下的None:

  enum Optional{ case None case Some(T)}

32  view1声明称var类型,view2声明let类型。这里有什么区别吗?下面的最后一行代能编译吗?

  代码:

  import UIKit

  var view1 = UIView()view1.alpha = 0.5

  let  view2 = UIView()view2.alpha = 0.5

  //最后一行代码能编译吗?

  答案:view1是个变量可以重新赋值给一个新的实例化的UIView对象。使用let你只赋值一次,所以下面的代码是不能编译的:

33 Swift中如何定义变量和常量?

  使用let来声明常量,使用var来声明变量

  view2 = view1//Error:view2 is immutable

  但是UIView是一个引用类型的类,所以你可以改变view2的属性,也就是说最后一行代码是可以编译的:

  let view2 = UIView()view2.alpha = 0.5//Yes!

34 分号去哪里了?

  答案:分号在Swift中是可选的,不过出于易读性的目的,苹果建议你不要再使用分号了。但有时候仍会在Swift中使用分号,比如在循环语句中。

<think>嗯,用户现在想要了解SwiftUI的面试题答案。我需要先确定用户的具体需求。他们可能是在准备iOS开发职位面试,特别是涉及SwiftUI的部分。用户之前询问过回调地狱,所以可能已经有一定的Swift和异步编程经验,现在希望扩展SwiftUI的知识。 首先,我得整理常见的SwiftUI面试题,涵盖基础概念、布局、数据流、高级特性等方面。同时,答案需要准确且简明,帮助用户快速复习重点。还要注意面试题可能涉及实际应用场景,比如状态管理、动画实现、与UIKit的集成等。 用户可能期望的问题类型包括:解释SwiftUI的核心概念,比如声明式语法;比较SwiftUI和UIKit的区别;如何处理数据绑定和状态管理;常用修饰符的作用;布局系统的理解;以及调试和测试方法等。此外,可能需要包括一些代码示例,比如使用@State、@Binding、ObservableObject等。 另外,用户可能对性能优化、自定义视图、Combine框架的集成等问题感兴趣。需要确保答案不仅正确,还能展示最佳实践和常见陷阱,比如避免在视图体内执行繁重操作,如何高效更新UI等。 还要考虑用户可能的后续问题,比如如何处理复杂动画或与Core Data的集成,但现在先集中在常见的面试题上。需要结构化回答,分点列出问题和答案,便于用户理解和记忆。可能还需要比较不同的数据流工具,比如@StateObject和@ObservedObject的区别,或者EnvironmentObject的使用场景。 最后,要确保使用中文,符合用户的要求,并且格式清晰,避免Markdown,但保持段落和代码示例的清晰分隔。需要检查每个答案的准确性,避免错误,比如正确解释SwiftUI的生命周期方法,或者如何预览视图的不同方式。</think>以下是针对 **SwiftUI 面试题**的常见问题及参考答案,涵盖基础、进阶和实际应用场景,帮助系统性准备面试: --- ### 一、基础概念 #### 1. **SwiftUI 的核心特点是什么?** - **答案**: SwiftUI 是 Apple 的**声明式 UI 框架**,特点包括: - **声明式语法**:通过描述 UI 的最终状态,而非逐步操作视图。 - **实时预览**:Xcode 提供实时预览,无需编译运行即可查看 UI 变化。 - **跨平台**:支持 iOS、macOS、watchOS 和 tvOS。 - **数据驱动**:通过 `@State`、`@Binding` 等属性包装器自动同步数据与 UI。 --- #### 2. **SwiftUI 中 `@State` 和 `@Binding` 的区别?** - **答案**: - **`@State`**: 用于**视图内部**的私有状态管理,值变化时触发视图更新。 ```swift struct CounterView: View { @State private var count = 0 // 仅当前视图使用 var body: some View { Button("点击 \(count)") { count += 1 } } } ``` - **`@Binding`**: 用于**父子视图间共享状态**,传递对某个状态的引用。 ```swift struct ParentView: View { @State private var text = "" var body: some View { ChildView(text: $text) // 传递 Binding } } struct ChildView: View { @Binding var text: String var body: some View { TextField("输入", text: $text) } } ``` --- ### 二、布局与修饰符 #### 3. **解释 SwiftUI 的布局过程** - **答案**: SwiftUI 布局分为三步: 1. **父视图提议尺寸**:父视图向子视图提供一个可用空间(Proposed Size)。 2. **子视图计算需求**:子视图根据内容返回自身需求尺寸(Intrinsic Size)。 3. **父视图放置子视图**:父视图根据子视图需求,决定最终位置和大小。 **示例**: ```swift HStack { // 父视图 Text("Hello") // 子视图1 Text("World") // 子视图2 } ``` --- #### 4. **如何让视图占满剩余空间?** - **答案**: 使用 `.frame(maxWidth: .infinity, maxHeight: .infinity)` 修饰符: ```swift VStack { Text("顶部") Spacer() // 占满剩余空间 Text("底部") } .frame(maxWidth: .infinity, maxHeight: .infinity) .background(Color.gray) ``` --- ### 三、数据流与状态管理 #### 5. **`@ObservedObject` 和 `@StateObject` 的区别?** - **答案**: - **`@ObservedObject`**: 用于**外部传入的可观察对象**,不负责生命周期管理(可能被意外释放)。 - **`@StateObject`**: 由视图**持有并管理生命周期**,确保对象在视图更新时不被重建。 **代码示例**: ```swift class DataModel: ObservableObject { @Published var count = 0 } struct ContentView: View { @StateObject private var model = DataModel() // 正确方式 var body: some View { ChildView(model: model) } } struct ChildView: View { @ObservedObject var model: DataModel // 从父视图传入 var body: some View { Text("\(model.count)") } } ``` --- #### 6. **如何跨多层视图传递数据?** - **答案**: 使用 `@EnvironmentObject`: 1. **定义可观察对象**: ```swift class Settings: ObservableObject { @Published var isDarkMode = false } ``` 2. **注入到根视图**: ```swift @main struct MyApp: App { @StateObject var settings = Settings() var body: some Scene { WindowGroup { ContentView().environmentObject(settings) } } } ``` 3. **在子视图中访问**: ```swift struct ChildView: View { @EnvironmentObject var settings: Settings var body: some View { Toggle("深色模式", isOn: $settings.isDarkMode) } } ``` --- ### 四、高级特性 #### 7. **如何在 SwiftUI 中实现动画?** - **答案**: 使用 `.animation` 修饰符或 `withAnimation` 函数: ```swift struct AnimatedView: View { @State private var isScaled = false var body: some View { Button("缩放") { withAnimation(.easeInOut(duration: 0.3)) { isScaled.toggle() } } .scaleEffect(isScaled ? 1.5 : 1.0) } } ``` --- #### 8. **SwiftUI 如何与 UIKit 集成?** - **答案**: 通过 `UIViewRepresentable` 或 `UIViewControllerRepresentable` 协议: ```swift struct MyUIKitView: UIViewRepresentable { func makeUIView(context: Context) -> UIActivityIndicatorView { UIActivityIndicatorView(style: .large) } func updateUIView(_ uiView: UIActivityIndicatorView, context: Context) { uiView.startAnimating() } } // 使用 struct ContentView: View { var body: some View { MyUIKitView() } } ``` --- ### 五、常见陷阱 #### 9. **为什么视图不更新?** - **可能原因**: - 未使用 `@Published` 或 `@State` 等属性包装器。 - 直接修改结构体的值(应通过 `Binding` 或方法触发更新)。 - 在非主线程更新 UI(需确保数据操作在 `MainActor` 中)。 --- #### 10. **如何调试 SwiftUI 的布局问题?** - **技巧**: - 使用 `.border(Color.red)` 查看视图边界。 - 通过 `Self._printChanges()` 检查视图更新原因。 - 在 Xcode 中使用 **View Hierarchy Debugger**。 --- ### 六、面试加分点 - **性能优化**:理解 `LazyVStack` 与 `VStack` 的区别,避免不必要的视图重建。 - **自定义修饰符**:封装常用 UI 样式(如阴影、圆角)。 - **Combine 集成**:用 `@Published` 和 `sink` 实现响应式数据流。 - **预览技巧**:展示多设备、深色模式的预览配置。 掌握这些知识点,能系统性应对 SwiftUI 相关的面试问题!
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值