iOS面试核心知识点深度解析:从ARC到性能优化的系统性整理
2026/9/11 0:51:34 网站建设 项目流程

2023年之后iOS行情怎么样,大家心里都有数。一面二面三轮下来,问的东西又杂又深,从ARC底层到Swift并发,从组件化到性能监控,题库散落得到处都是,博客文章很多还停留在两三年前。我自己既当过面试官,也被别人面过,知道这些题背后真正想考察的是什么,所以决定开这个栏目,把iOS面试里最常出现、最容易被追问的知识点做一个系统性整理,持续更新,每期讲透一个模块。

这个栏目不只是把题目和答案贴出来,我会把“面试官为什么这样问”“回答到什么程度算合格”“哪些点容易翻车”一起讲清楚。毕竟面试不是考试,背答案没用,要的是真懂。适合准备跳槽的iOS开发、刚入行的初中级工程师,以及想补基础的同学——不管你处于哪个阶段,这套内容都能让你少走弯路。

1. 栏目定位与整体框架

做这个栏目之前,我在网上翻了很多面经,发现一个通病:信息碎片化严重。有人贴了一套题,但只有题目没有解析;有人写了解析,但又太浅,停留在“是什么”的层面,不讲“为什么”。另外还有时效性问题,iOS每年都有新东西,Swift并发、SwiftUI、灵动岛适配、WidgetExtension、Scene生命周期,这些新考点旧题库里根本没有。

所以这个栏目我按自己的理解做了分层,把iOS面试内容拆成六个模块,每个模块单独成篇,持续更新:

  • 语言基础层:Objective-C与Swift的底层原理,包括内存管理、运行时、泛型、协议、值类型与引用类型等。
  • 并发与多线程:GCD、Operation、Swift Concurrency、锁机制、线程安全。
  • 系统机制层:RunLoop、生命周期、事件响应链、内存警告、动态库与静态库。
  • 网络与存储:HTTP/HTTPS、TCP/UDP、DNS解析、网络库封装、数据持久化方案选型。
  • 架构与工程化:MVC/MVVM/VIPER、组件化、模块化、CocoaPods/SPM、CI/CD、性能优化。
  • 新特性与生态:SwiftUI、Swift Concurrency、Widget、App Clips、分屏适配、开发者模式等。

每个模块我计划用一篇到两篇文章的篇幅来讲,里面会包含题目解析、常见追问、答题思路和经典源码分析。这期的开篇,我把几个真正的核心高频题目拿出来做一次完整拆解,当成整个栏目的样板,大家看完就知道后续内容是什么风格了。

1.1 为什么题库要分层而不是直接刷题

很多人准备面试的方式是打开GitHub搜“iOS面试题大全”,然后从头到尾刷一遍。说实话,这样效率非常低,而且很容易造成“背了答案但不会用”的假象。

面试官问一道题的时候,通常不是在等你背出标准答案,而是在通过这个问题评估你的技术深度和思维习惯。举个真实例子,面试官问“ARC是怎么工作的”,初级回答是“编译器自动帮你加retain和release”,中级回答是“ARC在编译期把内存管理代码插入到合适的位置,在运行时通过引用计数管理对象生命周期”,高级回答还会补充“ARC不是垃圾回收,没有GC线程,引用计数到0就立即释放,同时要处理循环引用的问题,而且Swift和OC的ARC实现上有差异”。同一个问题,三个层次的回答,拿到offer的概率完全不同。

分层的好处就在这里:你可以先自测自己当前在哪个层次,再有针对性地补短板。基础题背概念,中阶题讲原理,高阶题上代码和案例——每个层次的要求不一样,如果没有分层思路,很容易陷入“背了一堆概念,一问细节就露馅”的尴尬。

1.2 持续更新对iOS面试为什么特别重要

iOS生态迭代节奏快,这个圈子的面试题变化也快。三年前大家还在问Masonry和SDWebImage的原理,现在面试官已经开始问Swift Concurrency的TaskGroup怎么用、MainActor的隔离原理是什么、iOS 16之后NavigationStack怎么用。

这个栏目叫“持续更新”,不是说说而已。我每发现一道值得讲的新题,或者看到面试现场出现的新追问方向,都会把内容补进来。比如最近两年,问iOS分屏适配和Widget开发的明显变多了。原因是iPadOS的普及带来了新的适配要求,而Apple Watch和桌面小组件的生态也在扩大,面试官想知道你是否具备处理这些新场景的经验。这种动态变化,只有持续更新的内容才能覆盖到。

2. 内存管理高频题精讲:ARC、循环引用与Autoreleasepool

内存管理是iOS面试的必考区,基本上每一轮技术面都会碰到。这一节我挑三道出现频率最高的题目展开讲,每题都会给到从初级到进阶的完整回答思路。

2.1 面试题一:ARC到底是什么,和GC有什么区别

这是iOS面试的经典开胃菜。初级回答通常是“引用计数管理,编译器帮我们插入retain和release”,这个方向没错,但太单薄了,缺少关键细节。

完整的回答应该包含这几个层次:第一,ARC是编译期的特性,编译器在分析对象生命周期后,在合适的位置自动插入强引用、释放操作,不是运行时GC机制,没有垃圾回收线程在后台扫描。第二,ARC在运行时依赖Runtime提供的函数,比如objc_retain、objc_release、objc_autoreleaseReturnValue等,并借助编译器优化(比如免构造调用)来减少不必要的内存操作。第三,ARC有针对优化场景的处理,例如__weak修饰的变量会被注册到Runtime的weak表中,对象销毁时会自动置nil,这些是MRC时代需要手动处理的事情。

关于ARC和GC的区别,可以从三个维度对比:

对比项ARCJVM GC
回收时机引用计数归零时立即释放内存不足或GC触发时批量回收
机制编译期静态分析+运行时计数可达性分析+分代回收
对性能影响插入少量内存操作指令有STW停顿,但通常不可见
程序员干预需要处理循环引用基本无需管理

这道题的高阶加分点是提到Swift的ARC实现。Swift的ARC和OC的ARC机制本质类似,但在编译优化上做了更多,比如Swift的写时复制、结构体的值类型语义配合ARC能减少不必要的堆分配。如果你能把这个差异讲出来,面试官会认为你真的是在做iOS底层,而不是背概念。

2.2 面试题二:循环引用的常见场景和排查方法

循环引用这道题,面试官通常会追问“你实际项目中遇到过哪些循环引用”,如果你只背了“Block里用self会导致循环引用”这一句,肯定不够。

常见的循环引用有四类:第一类是Block捕获self,这个最典型。self持有block,block里又强引用了self,形成环。解决方法是使用__weak typeof(self) weakSelf = self,在多行异步操作时还需要考虑weakSelf是否已释放,要用strongSelf重新持有一下保证执行期间对象不释放。第二类是delegate,代理属性一般声明为weak,但如果把代理写成了strong,代理者和被代理者互相持有就形成了环。第三类是NSTimer,timer被添加到RunLoop后会强持有target,如果target又持有timer,就循环了。iOS 10之后有block版本的timer API可以避免这个问题,或者使用weakProxy中间层。第四类是闭包中的相互捕获,特别是在Swift中,两个类互相持有对方的属性和闭包,容易形成更隐蔽的环。

排查工具这块,我强烈推荐Debug Memory Graph。在Xcode中运行到可疑场景后,点击Debug Navigator里的内存图图标,系统会把当前对象引用关系以图的形式画出来,循环引用会以一个“环”的形式可视化展示。另外Instruments的Leaks模板也能检测循环引用导致的泄漏,但要注意Leaks检测的是真正的泄漏,有些循环引用如果还能被外部访问到,不一定会被判定为泄漏。

实操经验:检测循环引用最有效的办法不是等Instruments报出来,而是在写代码的时候遵守规则。我个人的习惯是,所有delegate和dataSource一律用weak;所有Block里捕获self的地方一律过一遍“是否真的需要强持有”;所有Timer在使用完之后必须invalidate,最好在viewWillDisappear或deinit里统一处理。这种防御性编程习惯,比事后排查省心一百倍。

2.3 面试题三:Autoreleasepool在ARC下还有用吗

这题很多人会答“现在都用ARC了,Autoreleasepool没用了”,这是大错特错。

Autoreleasepool在ARC下依然有明确用途,核心场景是管理自动释放对象的生命周期和峰值内存。系统在事件循环、RunLoop迭代的各个节点会创建自动释放池,如果在这个周期内产生大量临时对象,池子不手动释放就会一直累积到事件结束,内存峰值可能飙得很高。

典型场景:for循环内创建大量临时对象,尤其是图片数据、字符串拼接等重内存操作。如果循环1000次,每次循环都产生几个大的临时对象,不手动加Autoreleasepool的话,内存会瞬间飙升;加上之后,每一轮迭代结束就自动释放,内存曲线会平稳得多。

代码层面要注意,@autoreleasepool本身是有开销的,它不是免费的。它是根据当前线程的autorelease栈创建了一个新的pool,压栈和出栈都需要操作,如果滥用也可能带来性能损耗。所以并不需要每个循环都包一层,但要敏锐识别到“大量临时对象集中产生的场景”。另外,在主线程RunLoop自身的自动释放池两次pop之间,如果某个方法比较耗时,又产生了大量临时对象,那这块内存就会一直被占用,这种情况可以手动包一层Autoreleasepool。

Swift里也有类似机制,比如withExtendedLifetime可以控制一个对象的生命周期,但Swift里大部分值类型不涉及引用计数,所以Autoreleasepool更多还是用在OC或混合开发场景。

3. 多线程与并发:GCD、锁与线程安全

并发问题是中级面试的分水岭。问得最多的就是GCD的队列、任务、死锁场景,以及锁的选型。

3.1 面试题一:并发队列和串行队列怎么搭配使用

GCD有两大核心概念:队列(Queue)和任务(Block),任务有同步/异步之分,队列有串行/并发之分,组合起来有四种情况,理解这四种情况是基础中的基础。

面试会问“在并发队列里执行同步任务会怎样”,答案是不会开新线程,会在当前线程直接执行,但要注意,如果当前线程就是该并发队列中的某一条工作线程,而同步任务又在等待该线程上的其他任务完成,就可能拿不到可用线程导致死锁,这个场景比较隐蔽,工程上一般不这么用。

“在串行队列里执行异步任务会怎样”,答案是多开一个线程,但任务仍然一个一个执行,顺序有保证。常见的场景是网络请求后再回主线程刷新UI,或者用一个串行队列保护共享资源。

说实话,GCD本身的API不难,难的是能不能想清楚同步异步、串行并发的组合所带来的执行顺序。面试官经常让候选人用纸笔画一个场景,比如主队列里执行async到全局并发队列、然后回主队列更新UI,输出顺序是什么。画错了的,基本就告别下一轮了。

实操经验:我写代码的时候,网络回调回主线程更新UI,不会直接用DispatchQueue.main.async,而是先做一个弱网和超时统一处理,再统一回调主线程。这个统一处理的封装同时会规避掉很多线程切换的坑,比如在后台线程操作非线程安全的UI对象导致崩溃。面试官问“为什么你的APP不会闪退”时,这种细节能得分。

3.2 面试题二:GCD死锁的经典场景和原理

死锁这题几乎是必考。最经典的场景是同在一个串行队列里执行同步任务:

dispatch_queue_t queue = dispatch_queue_create("com.example.serial", DISPATCH_QUEUE_SERIAL); dispatch_async(queue, ^{ dispatch_sync(queue, ^{ NSLog(@"inner"); }); });

这段代码必死锁。原因是外层async把block派发到串行队列后,队列开始执行该block;block内部又调用了dispatch_sync到同一个队列,同步任务需要等队列中的任务全部完成才能执行,而队列中当前正在执行的任务又在等这个同步任务结束,互相等待,形成死锁。主线程上执行dispatch_sync到主队列是同样的逻辑,所以也必死锁。

解决思路是避免在同一个串行队列里嵌套同步调用。如果需要并发处理,用并发队列;如果需要串行但又不阻塞当前线程,用async;如果必须同步回调并且当前不在目标队列,可以先用dispatch_queue_get_label或者dispatch_precondition判断当前是否已在目标队列上。

面试官如果追问“并发队列里嵌套同步会不会死锁”,回答是:只有在当前线程恰好是该并发队列的工作线程且唤醒策略不允许新线程时才会,一般不会死锁,但在信号量、条件锁等复合场景下依然可能出现。这个属于偏纲的追问,能答出来很加分。

3.3 面试题三:iOS中的锁怎么选

锁是并发考点里仅次于GCD的高频题,面试官可能会问“你项目里用过哪些锁,为什么选它”。这里给一个选型表:

锁类型性能特点适用场景
OSSpinLock极快自旋锁,iOS 10后被废弃(优先级反转)不再推荐
os_unfair_lock极快取代OSSpinLock,无优先级反转短临界区
pthread_mutex通用互斥锁,灵活性高通用场景
NSLock对pthread_mutex的OC封装中小项目
@synchronized可重入,语法简单简便但不追求性能
dispatch_semaphore信号量,需控制wait/signal限制并发数、同步等待
NSCondition条件锁,处理等待/通知生产者消费者模式

实际写代码,我个人优先用dispatch_semaphore做信号量控制,用pthread_mutex做资源互斥,避免用@synchronized因为它在runtime层面可能涉及全局缓存,性能一般。很多面试题特别喜欢拿@synchronized来问,因为它本质是递归锁,可重入。如果你能把这个细节说出来,说明你真用过它调过问题。

除了API层面的锁,面试官还会问“atomic是否线程安全”。答案是否定的,atomic只保证属性的原子性读和写,不保证对象内部的复合操作安全。比如atomic修饰的NSMutableArray,两个线程同时读和修改数组内部元素,照样会崩溃。可变容器本身不是线程安全的,这是一个经常被误解的面试点。

4. 系统机制与网络层:生命周期、响应链与HTTPS

这一节考察的是你对iOS系统运作机制的理解深度。这些问题光靠背API是答不好的,得结合项目经验。

4.1 面试题一:App冷启动流程和生命周期

这道题现在几乎必问,因为启动优化就是性能优化的第一站。面试官想知道你有没有真的做过启动分析。

从进程层面拆解,冷启动包含两个大阶段:pre-main阶段和UIKit初始化阶段,最后才是生命周期回调。pre-main阶段做的事情包括:加载动态库(dyld)、进行rebase和bind(修正ASLR后的符号地址)、启动ObjC运行时(注册类、category)、执行+load、生成自动释放池。这个阶段可以被测量,在Xcode的Scheme里设置DYLD_PRINT_STATISTICS=1可以输出每个小项的耗时。

main之后进入UIApplicationMain,加载主storyboard或SwiftUI App协议入口,然后触发viewDidLoad等生命周期回调。面试官常见的追问是:“启动优化你会怎么做”。回答思路分为硬件和软件两个维度:硬件层面减少动态库数量,用静态库、合并动态库、砍掉不必要的初始化;软件层面把+load中的逻辑后移到+initialize,懒加载各种第三方SDK的配置,避免主线程做耗时操作。还有更细致的操作,比如减少setter方法调用的KVO触发次数,或者先用placeholder视图再异步加载真实内容。

生命周期这块还有个近两年的新考点:iOS 13及以后的UIScene生命周期。老项目很多还是基于UIApplicationDelegate的生命周期,但新项目走SceneDelegate。面试官会问“两者有什么区别,你怎么适配”。重点是UIScene后,多个窗口实例拥有独立的生命周期,这在iPad分屏场景下非常关键——两个scene同时存在,每个scene的active/resign状态是独立的。你能把这个讲明白的话,说明你真的适配过多窗口。

4.2 面试题二:事件响应链和hitTest

响应链问题考察的是UIKit的触摸事件分发机制,看起来简单,真正能说清楚的候选人不多。

触摸事件的旅程是:系统进程把触摸事件打包,通过IPC传给我们的App进程,由UIApplication分发到keyWindow,window会找到最适合响应的view,这个过程会调用hitTest:withEvent:方法。hitTest的默认实现是,从后往前遍历subviews,先检查frame是否包含触摸点,如果包含则递归调用子view的hitTest,最后返回pointInside为YES且层级最上层的那一个view。

面试官喜欢问:“如果某个子view超出父view的Bounds,还能不能点击到?”这个要看layer的masksToBounds以及hitTest的实现细节。正常情况下,子view超出父view部分是可以响应点击的,因为hitTest遍历子view时使用的是子view自身的坐标系。但如果你给父view设置了clipsToBounds=YES,视觉上被裁掉了,点击超出部分也不会生效,因为父view的pointInside判断为NO。

响应链的传递方向则是从hitTest结果开始,沿着nextResponder链向上传递,view → superview → viewController → window → UIApplication → UIApplicationDelegate,每一层都可以通过重写touchesBegan或target-action拦截。理解了这条链,你在处理UITableViewCell中的按钮点击冲突、ViewController不响应事件等bug时就有方向了。

4.3 面试题三:HTTPS握手过程和iOS网络库选型

网络层面试的重头戏是HTTPS,因为iOS所有网络请求都要求ATS(App Transport Security)开启,强制HTTPS。一道经典的开放式问法是“从输入URL到拿到数据,中间发生了什么”,这里要串起DNS、TCP、TLS、HTTP请求、CDN等多个环节。

TCP三次握手不多说,重点讲TLS握手。以TLS 1.3为例,客户端先发送ClientHello,包含支持的加密套件和随机数;服务端返回ServerHello、证书和密钥交换参数;客户端验证证书链(通过系统CA库)后,双方再交换各自密钥参数,生成会话密钥。之后所有应用数据都用会话密钥加密传输。iOS里对证书信任还有额外的挑战——企业内网自签名证书,这时可以配置SessionDelegate里对challenge的处理,选择信任特定证书,但要注意不要直接无条件信任所有证书,否则会有中间人攻击风险。

网络库选型方面,现在新项目基本都是直接上URLSession的原生能力,配合async/await写起来很舒服,不再推荐大量使用AFNetworking/Alamofire这类第三方库,除非需要复杂重试、缓存策略等额外能力。如果面试官问“为什么现在不依赖AFNetworking”,你要提到URLSession的downloadTask、backgroundSessionConfiguration、断点续传这些系统能力已经非常成熟,第三方库的优势只剩封装便利性。

实际开发中,我习惯在自己项目里封装一个轻量网络层:统一配置baseURL、超时、header、日志、错误码映射,底层仍然是URLSession,但上层接口简化成类似request(path, method, params)的调用方式。面试时能把“为什么这么封装”讲清楚,比背十个库名有用得多。

5. 架构设计与性能优化:不只是八股文

架构设计题是所有面试中主观性最强的部分,没有标准答案,但考察点非常明确:你的设计思路是否清晰,能否在约束条件下做出合理取舍。

5.1 面试题一:MVC、MVVM、VIPER你选哪个,为什么

这道题切忌答“我们项目用的是MVVM所以我就用MVVM”,面试官想听的是你做过对比。

MVC在iOS中是最基础的架构,苹果自带的UIKit本身就是MVC的体现,但问题是Controller容易变成“上帝类”,几百行代码堆在一起,难以维护。MVVM引入了ViewModel层,把视图的状态和业务逻辑从Controller中抽离出去,配合双向绑定(或者单向绑定),Controller瘦身效果显著。但MVVM的问题也很明显:ViewModel与View的绑定关系如果处理不好,会带来调试困难,因为数据流不透明。

VIPER把职责拆得更细,View、Interactor、Presenter、Entity、Router五个角色各自独立,可测试性是三种架构中最高的,但也是样板代码最多的。对中小型团队来说,VIPER可能过于重量级,一个简单的页面要写一堆协议和类。

我的观点是,没有最好的架构,只有最合适的架构。对于快速迭代的业务线,MVVM配合Coordinator做页面路由,是性价比很高的选择;对于逻辑特别复杂、需要大量单测的模块,可以考虑VIPER。面试中还会追问“你怎么保证ViewModel和View的松耦合”,这时候就可以讲依赖注入和协议抽象。

另外,SwiftUI下的架构又不太一样,MVVM在SwiftUI里是官方推崇的方式,因为@State、@ObservableObject这些机制本身就是为绑定设计的。你能把这些新旧结合的点讲出来,说明你有面向未来架构的思考,这在面试里很加分。

5.2 面试题二:组件化怎么做,路由怎么设计

大厂面试特别喜欢问组件化,因为他们的业务线多、团队大,代码解耦是刚需。这个问题回答得好的核心不是背路由框架的名字,而是讲清楚“你们为什么需要组件化、怎么落地、边界在哪里”。

组件化的第一步是明确目录分层:基础组件层(网络、存储、工具)、业务组件层(登录、订单、支付)、宿主壳层(App主入口)。基础组件不依赖业务,业务组件之间不互相依赖,宿主层负责组装。这样做的核心意义是提高编译速度、支持并行开发、实现动态部署(部分场景)。

路由设计方面,最主流的是URL路由。通过注册URL和对应页面或服务,调用方通过openURL或者Router的enter方法打开目标页面,参数以字典或对象传递。这种方案容易和H5的导航联合,也方便远程推送跳转。另一种是Target-Action方式,以调用本地方法为主,编译期安全性和运行时灵活性都更好。两种方案各有优劣,面试中能说出各自适用场景即可。

组件化最容易翻车的点是“过度设计”。小团队几个模块就几十个pod依赖,为了解耦而解耦,反而增加了维护成本。我在实际项目中会先判断团队规模和业务复杂度,超过一定规模才做组件化改造,否则保持单工程、多目录结构,反而效率更高。这个“适度”的判断力,是面试官真正想考察的高级能力。

5.3 面试题三:启动优化和卡顿优化怎么做

性能优化是初中级工程师简历里的重灾区,很多人写了“优化启动速度30%”,但一问怎么优化就支支吾吾。写简历之前,一定先确认这些问题你真的做过。

启动优化的核心是“减少主线程的耗时任务”。优化手段包括:第一,动态库改静态库,减少动态链接阶段时间,缺点是需要重新编译注意稳定性;第二,裁剪+load方法,把延迟初始化逻辑放到+initialize或者首帧渲染之后;第三,主线程I/O操作移到异步,比如读缓存文件、更新数据库;第四,减少首屏视图层级和自动布局(AutoLayout)的计算量,特别是复杂的UIStackView嵌套。

卡顿优化的核心思路是“检测RunLoop每一帧的耗时”。通过在RunLoop的kCFRunLoopBeforeSources和kCFRunLoopBeforeWaiting等观察点注册RunLoopObserver,统计一次循环的耗时,超过阈值(比如16.7ms或100ms)就认为是卡顿,采集主线程堆栈并上报。具体实现可以借助第三方库,比如基于RunLoop的方案或基于CADisplayLink的方案。面试的时候,能把RunLoop监控的原理讲明白,是性能优化的高分答案。

我记得有一次现场面试,被问到“FPS低和CPU占用高有关系吗”,这题其实在考察你是否理解iOS渲染管线。回答是:FPS低不一定是主线程CPU的问题,有可能是GPU渲染压力大、离屏渲染过多、图层混合多等导致。要结合Instruments的Time Profiler、Core Animation模板去定位,不能只盯着CPU。这个点能够展开,基本就能顶住后续的连环追问了。

6. 近期高频新题与冷门细节:分屏、抓包、自动化与上架

除了上面这些经典面试题,最近两年面试官越来越喜欢问“新的iOS特性你接触过吗”。这一节我结合最近高频热搜词里出现的冷门考点做一次梳理。

6.1 面试题一:iPad分屏适配你做过吗

iOS分屏(Split View、Slide Over)从iOS 9开始就有,但真正普及是iPad Pro普及之后。面试官问这个,是想确认你是否处理过多窗口下布局异常和生命周期变化。

分屏的核心是Size Classes和traitCollection的变化。当应用从全屏切到分屏时,horizontalSizeClass会从regular变成compact,布局要根据这个变化自适应。如果你的布局是纯Auto Layout + Safe Area的,通常问题不大;如果是基于固定frame的计算,就容易出现内容被截断或白边。

生命周期上,由于iOS 13引入了Scene生命周期,分屏会导致两个scene同时运行。你是否正确处理了scene的didDisconnect等回调?如果没处理,分屏退出时有可能会产生数据丢失或UI卡住。项目需要支持分屏的话,建议把所有UI状态都放在scene会话里管理,不能依赖AppDelegate的applicationWillResignActive这类旧回调。

6.2 面试题二:你怎么抓包,怎么调试协议

“你用Charles抓过HTTPS包吗”是网络题常见追问。很多人只会打开Charles、装证书,一遇到问题就懵了。

正确流程是:先在手机和电脑上安装Charles的CA证书,配置代理,然后在手机上安装描述文件并在设置里信任该证书。iOS的ATS要求HTTPS,所以现代App一般都能直接抓到HTTPS明文。抓不到包的情况分两种:一种是你没在设置里信任证书,系统默认不信任自签CA;另一种是App在客户端做了证书校验,只信任特定服务端证书,这时候MitM会被拒绝。

冷门知识点:iOS 14及以上,系统会弹出本地网络权限,如果用户关闭了这个权限,部分网络调试工具会失效。这是很多面试问题也能带出来:你怎么排查网络请求不通的问题?排查顺序一般是先看设备是否连上代理、再看证书是否信任、然后看抓包工具是否显示请求、最后用模拟器看系统日志。这个排查日志,面试官会认为你有实打实的线上问题排查能力。

6.3 面试题三:iOS的自动化测试你了解哪些

“你们项目有自动化测试吗”这个问题越来越常见。老实说,很多中小团队没有沉淀完整的自动化测试体系,但面试者至少要了解基本框架和适用场景。

iOS自动化分几个层次:单元测试用XCTest,用来测纯逻辑和模型层;UI测试用XCUITest,可以模拟用户点击、输入、滑动;性能测试用XCTest的measure块,可以记录时间或内存消耗。CI/CD方面,常见方案是Xcode Server、GitLab CI或Jenkins配合fastlane,跑完测试后生成报告。

面试官如果问“你写过哪些有价值的测试”,不要只回答“跑了一遍全部通过”,要说你为了设计可测试性做了哪些重构。比如把业务逻辑从ViewController里抽出来变成一个纯Swift类,这样才方便写单元测试。可测试性设计本身就是架构能力的重要体现。

6.4 面试题四:开发者模式和证书过期怎么处理

“iOS开发者模式”和“证书更新”这两个点看起来是运维问题,但在面试中经常被问到,尤其是涉及到上架和真机调试的岗位。

开发者模式是iOS 16之后新增的开关,开启后才可以安装开发包进行真机调试。很多人刚升级Xcode后真机一直连不上,就是因为没有在手机上打开开发者模式。证书这块,Apple开发者账号的开发和分发证书通常有效期为一年,过期后App无法正常安装和调试。遇到证书过期的处理流程是:在Apple Developer后台把过期证书revoke或者renew,然后在Xcode里重新生成描述文件(Provisioning Profile),重新打包。注意,证书对应的私钥在Mac钥匙串里,丢失私钥的话会导致证书在别的机器上无法使用,这是一个频繁踩坑的点。

面试官如果问“上架审核被拒过吗”,这在技术上没有标准答案,但你可以讲自己处理过4.3(重复APP)申诉、2.1(大数据量内容)回复等常见问题。回答时突出“回复审阅的抽象准确、修改截图和演示视频、必要时提供测试账号”这些实操细节,会更有说服力。

7. 面经实录:技术面、二面、HR面的节奏和侧重点

最后一部分,我结合自己当面试官和求职者的双向经验,说说面试整个过程的节奏把控和应对思路。

7.1 一面和二面分别考察什么

一面通常由团队核心技术骨干来面,关注点很实在:基础是否扎实、能不能写代码、有没有实际解决问题的能力。所以一面问的题大多是内存管理、多线程、网络、数据结构、UITableView优化这类基础题,一般是从浅入深追着问。

比如一道“UITableView卡顿怎么解决”的开放性题目,它考察的其实是综合能力:你先说出cell复用(这是基础),再讲图片异步加载和缓存(这是经验),再提预排版和异步绘制(这是进阶),最后可能还聊到栅格化shadowPath优化(这是深度)。能一口气由浅入深讲完的人,一面大概率能过。

二面更多是技术Leader或架构师来面,关注点是架构设计、系统设计、代码质量、团队协作。二面常考一道“如果让你从零设计一个IM消息列表页面,你会怎么设计”,这是典型的架构开放题。回答思路要从需求出发:消息的数据库存储、增量拉取、消息同步、UI更新策略、多端同步、离线推送、安全校验、性能优化,每一步都要有依据。二面的核心是让面试官相信你有独立负责模块的能力。

7.2 项目经历怎么讲才不拉胯

技术基础再好,项目经历讲砸了,offer也会飞。项目介绍有三个常见误区:只讲功能不讲挑战、只讲技术栈不讲结果、只讲自己做了什么不讲为什么这么做。

我建议用STAR法则来组织项目描述。情境(Situation):项目背景是什么,为什么需要做;任务(Task):你负责的具体模块和目标是啥;行动(Action):你怎么设计方案的,做了哪些选型、踩了哪些坑;结果(Result):最终效果如何,数据上发生了什么变化。比如“做启动优化”,不要只讲“我用DYLD_PRINT_STATISTICS统计了pre-main时间”,而是说“冷启动从800ms优化到450ms,原因是削减了三个+load、把初始化移到了首帧后,并设计了插件化的启动任务调度器”。

项目经历里最容易被追问的点是“如果再让你重来一次,哪里会改进”,这个回答如果空泛如“我觉得设计得不好”,是不行的。要具体到方案迭代后的可替换性,比如“现在看当时选的是Target-Action路由,没有做URL注册,导致跨模块跳转必须写死类名,如果重来我会先用URL路由做一层映射”,听上去就很有深度。

7.3 HR面和技术终面有哪些坑

HR面看似轻松,其实暗藏陷阱。常见问题包括“你的职业规划是什么”“为什么离开上一家公司”“如何看待加班”“你的优点和缺点”。这些问题没有标准答案,但绝不能踩红线。

“为什么离开上家”,千万不要抱怨公司或leader,哪怕真的关系很僵。更好的表达是“希望有更大的技术挑战”“团队的技术方向和我个人成长方向比较一致”。HR听到这种回答,一般不会深挖。“职业规划”则要结合技术方向说,比如你想从iOS开发往全栈或技术管理方向发展,同时强调当前阶段还是要深耕iOS底层和性能优化。这样既表达了上进心,又切合岗位。

技术终面通常是总监或CTO来面,他们要确认的不是你能不能写代码,而是你的技术边界、学习能力、沟通协作能力和价值观。总监经常问“业界有什么技术趋势是你比较关注的”,可以结合Swift并发、SwiftData、混合开发(ArkTS/Flutter)来讲。能给出自己的判断和依据,比随大流回答要好。

写在最后:一些个人经验

这个栏目我会一直做下去,计划覆盖iOS面试中所有核心高频题型,并配合新特性更新。我做了几年iOS开发,也参与过一些校招社招的面试评审,最大的感受是:面试并不是在考知识库的广度,而是在考你在真实开发中形成的问题意识和决策能力。

如果你目前正在备战面试,我的建议是,不要抱着“背题就能过”的心态,而是每道题都试着用“是什么、为什么、怎么做、有什么坑”的框架去组织答案。可以先自己出声讲一遍,看看能不能讲顺。讲不顺的地方,就是你知识的盲区,也是最值得花时间补的地方。后续每一期更新,我都会按这个思路来写。祝大家都能拿到心仪的offer。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询