
2020年那个秋招季网易的iOS开发工程师笔试题目在网上讨论度很高。我作为一个从春招就开始刷iOS题过来的人后来也和几个同学复盘过这套题有一些体会想和你分享。这套笔试整体和面试的沟通性质不同它更像一道没有感情的知识滤网在120分钟内把候选人的基础知识储备、逻辑推理能力和工程意识全都测一遍。很多人以为iOS笔试就是考语法、考API其实真正决定你能不能进入面试环节的是面对陌生工程问题时展现出的底层理解力。可以说笔试是秋招第一道真正的分水岭值得花一整篇来复盘。无论你是正在准备校招的应届生还是打算跳槽但没经历过系统性笔试的开发者这篇内容都会对你重新审视自己的iOS知识体系有帮助。1. 网易2020校招iOS笔试一场“基础为王”的筛选1.1 笔试形式与时间分配的直观印象网易2020校招正式批的笔试整体采用在线笔试的方式全程机考严格计时。整套题分为客观题选择/判断、简答/分析题和编程题三大块。时间总长一般是120分钟具体到iOS岗位题量不算特别大但内容跨度很惊人从C语言指针、操作系统基本概念一直到iOS底层的消息转发、内存布局都有涉及。先说一下我对这套题的整体印象它不是那种刷题App里常见的“八股文”套题而是明显带着“要找一个真正理解iOS底层机制的人”的目的来出题。很多题目表面在考用法实际在考原理很多题目看似是语法细节实际是在考察内存管理、编译链接、运行时机制。这一点从网易历年技术岗位的招聘风格也能看出来校招笔试做的就是一道“漏斗”先把基础不扎实的人筛掉面试环节再进一步看工程能力和项目沉淀。时间分配上我建议按比例倒推客观题大约占40分钟简答分析题占40分钟编程题留出35分钟最后5分钟用来检查。编程题不是每道都要求满分但至少要保证交出一份能编译、过了部分测试用例的代码这种性价比策略在时间紧张时特别重要。1.2 题目难度梯队哪些题决定你能不能进面我身边也有几个不同学校的同学一起投了网易笔试后大家相互对过题发现这套卷子的难度梯队非常清晰。第一梯队是“送分题”比如strong和copy的区别、frame和bounds的区别、UITableView的复用机制等。这些题只要复习过iOS基础基本都能做对属于拿分保底的部分。但送分题也不是全无陷阱比如“copy修饰的NSMutableArray属性如果传入一个NSArray赋值之后再往这个属性上添加元素会发生什么”这是我印象里网易特别喜欢考的变形题很多人只看过结论就跳过了结果栽在“可变与不可变类型的真实类型”这个细节上。第二梯队是“拉开差距题”集中在内存管理、多线程、RunLoop这几个方向比如Block捕获变量的方式、GCD死锁的原因分析、RunLoop在滚动和点击事件中的状态切换。这部分题目完全摆脱了“背概念”的阶段需要你画得出内存变化图、能推演出执行顺序。能把这部分做对的人基本就能进面。第三梯队是“压轴题”一般是手写算法或简短的方案设计。那一年网易的算法题整体偏常规但第3道题会故意把场景包装成iOS相关的业务需求比如“设计一个图片缓存模块”或“实现一个支持取消下载任务的并发控制器”把数据结构、网络、多线程三个点串在一起考。这种题没有标准答案重点看你思路是否清晰、是否能落到代码层面。2. 语言基础与内存管理笔试里最“要命”的细节2.1 ARC、引用计数与循环引用的连环坑Objective-C的内存管理是iOS笔试永恒不变的重头戏。网易的题目风格喜欢把几层知识点套在一个问题里比如在ARC环境下以下代码是否存在问题若存在会导致什么后果property (nonatomic, strong) void (^myBlock)(void); - (void)setupBlock { _myBlock ^{ [self doSomething]; }; }这是一道非常经典的循环引用考题。多个知识点叠加在一起myBlock被strong持有而Block内部又捕获了self。在MRC时代Block可能在栈上也可能在堆上但在ARC环境下Block默认会复制到堆上此时捕获的self也会被强持有。于是self持有myBlockmyBlock又持有self形成一个引用环导致两个对象都无法被释放。解决办法是将Block的属性设为copy并配合__weak/__block修饰局部变量来打破环。如果只是回答“会有循环引用”这道题只能拿一半分。网易的批卷标准里期望你能分析出Block的三种内存位置_NSConcreteStackBlock、_NSConcreteGlobalBlock、_NSConcreteMallocBlock以及不同位置下捕获变量的语义差异。在ARC下显式写copy其实更多是语义提醒编译器会帮助完成拷贝操作但__weak是必须的。我当年复习的时候把循环引用的所有常见场景都列成了清单Block捕获self、Delegate用strong、NSTimer的target强引用、通知中心未移除观察者、父子视图的subviews强引用等。笔试中只要看到“是否导致内存泄漏”类题目往这个清单上套就能快速定位。2.2 属性修饰符与KVC/KVO的隐藏考点属性修饰符是iOS笔试最基础的考点但网易的题目会把修饰符的细节挖得很深。比如strong与copy很多复习材料只告诉你“NSString最好用copy”但笔试不会问得这么浅它会反过来问如果把一个NSMutableString赋值给一个copy修饰的NSString属性后续修改原NSMutableString属性值会变吗答案是不会变。因为copy会对传入对象执行拷贝属性持有的是一个不可变副本。但如果你用了strong属性只是持有了同一个对象引用原对象一变属性值也跟着变这在很多场景下会引发难以排查的Bug。网易这道题还喜欢加一个选项如果把NSMutableString赋值给copy修饰的NSMutableString属性执行[self.mutableString appendString:xxx]会不会崩溃答案是会因为copy返回的是不可变的NSString真正运行时类型已经不是NSMutableString了调用可变方法会报“unrecognized selector”。KVC和KVO也是笔试中的高频细节点。网易的考察角度不是问你“KVO怎么用”而是问“KVO的底层实现是什么”。这个问题的标准答案是KVO基于运行时机制当一个对象注册了观察者后系统会动态创建一个该类的子类NSKVONotifying_XXX。这个子类重写了被观察属性的setter方法在setter中会依次调用willChangeValueForKey:和didChangeValueForKey:。对象被观察后其isa指针会指向这个新子类因此调用object_getClass()能察觉到类的变化。手动触发KVO需要调用willChangeValueForKey:和didChangeValueForKey:自动触发则依赖setter所以直接对成员变量赋值不会触发KVO。我还记得有一道这样的题[obj setValue:x forKey:name]和obj.name x有什么区别本质差不多但KVC会先查找setName:找不到再查找_name、_isName再找不到时还会去accessInstanceVariablesDirectly返回的BOOL决定是否访问成员变量实在找不到会调用setValue:forUndefinedKey:这里又有一层玄机。笔试里细节决定成败这些链路平时不画一遍考场上很容易卡壳。3. 并发与RunLoop笔试里拉开分差的两个深水区3.1 多线程方案对比与经典死锁分析iOS提供了NSThread、GCD、NSOperationQueue三套多线程工具笔试最常考的是GCD。网易在2020年校招里围绕GCD出了一道经典的判断题相信很多同学都遇到过在主线程上执行以下代码会发生什么dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(block executed); });答案在熟悉GCD的人看来很简单死锁。但笔试时它不是简单让你回答“死锁”而是要你解释为什么会死锁、死锁的本质是什么。dispatch_sync在提交Block后会阻塞当前线程等待Block执行完毕而Block被提交到主队列后需要主线程去执行但此时主线程正被dispatch_sync阻塞着无法去执行队列里的Block。于是你等我、我等你就僵住了。这和一个线程里自己锁自己的互斥锁是一个道理只是GCD队列把这种相互等待包装得更隐蔽。网易的笔试喜欢在这个基础上继续追问而且追问方式很刁钻它会从串行队列切换成并发队列从主队列换成自定义队列换着花样考同一个原理在串行队列中执行dispatch_sync到自身队列是否死锁是。因为串行队列同一时刻只允许一个任务执行当前任务还在跑同步提交的任务永远排不上。在并发队列中执行dispatch_sync到自身队列是否死锁不会。并发队列允许多个任务同时执行新提交的同步任务可以立刻进入队列执行。dispatch_async会死锁吗不会。异步提交只是把Block加入队列当前线程不等待所以不会阻塞。多线程那边还常考NSOperation和GCD的区别网易的题会要求你从“任务取消、依赖关系、最大并发数控制、KVO监听状态”几个维度对比。对于笔试最稳妥的答法是一个表格对比维度GCDNSOperationQueue任务取消不支持主动取消支持cancel任务依赖需要自己用dispatch_barrier等方式模拟原生支持addDependency:最大并发数控制需要信号量或dispatch_group等配合直接设置maxConcurrentOperationCount状态监控无支持KVO监听isFinished、isExecuting等面试官想看到的不是“哪个更好”而是“什么场景选哪个更合适”。笔试逻辑题也一样要展示你是在做工程判断不是在背定义。3.2 RunLoop的运行机制从Mode到事件响应RunLoop是iOS面试笔试的高频难点网易2020年的题目也毫不意外地出现了。它通常以两种形式出现一种是概念选择题一种是跟实际场景结合的简答题。概念题里几乎必考的就是RunLoopMode。考题大概是这样当你在UIScrollView上拖动时如果使用scheduledTimerWithTimeInterval:创建一个NSTimer定时器会怎样为什么答案定时器会暂时失效。原因是scheduledTimerWithTimeInterval:创建的Timer默认被添加到了NSDefaultRunLoopMode而用户拖动ScrollView时RunLoop会切换到UITrackingRunLoopMode在这个Mode下找不到这个Timer所以Timer回调不会执行等滚动结束后回到NSDefaultRunLoopModeTimer才会恢复。解决办法是把Timer手动添加到NSRunLoopCommonModes或者用dispatch_source实现定时器后者还能规避Timer在后台线程不回调的问题。RunLoop的第二类考法更硬核会直接考察RunLoop内部的处理流程。完整掌握需要理解源码级别的逻辑但笔试层面的核心链路是这样的通知ObserversRunLoop即将处理Timer事件。通知ObserversRunLoop即将处理Source事件。处理Blocks。处理Source0非基于端口的输入源如触摸事件、performSelector。如果有Source1基于端口的事件跳转去处理对应消息。通知ObserversRunLoop即将进入休眠。进入休眠等待被消息唤醒。被唤醒后通知ObserversRunLoop刚从休眠中唤醒。处理Timer、Source1、提前唤醒条件等。循环回到第一步。真正在这个循环里被挂载的源往往决定了App能否及时响应事件、能否省电。把source0、source1、timer、observer四类对象搞清楚RunLoop相关的题目基本都能拿下。说实话这块知识在平时业务开发里用到的不多但它确实是面试官检验候选人对iOS底层掌握程度的一个试金石。想进大厂的iOS岗这关必须过。4. 界面与架构题不是背两句概念就能拿分4.1 从UI渲染到Auto Layout的细节追问网易iOS笔试里关于UI的部分不是考你“UITableView有哪些代理方法”这种照本宣科的题而是会横向串联很多知识点。比如有一个题目是这样说的在viewDidLoad中获取subview.frame发现width是0为什么这道题背后是iOS视图布局的完整链路viewDidLoad只意味着视图控制器的视图已被加载进内存但此时它还没被添加到窗口上也没有开始布局流程。frame只有在viewWillAppear之后、Auto Layout完成布局之后才是正确的。真正要拿到准确的frame应该在viewDidAppear或者viewDidLayoutSubviews里做。更精确的说法是只要布局发生变更比如旋转屏幕、约束被更新系统会调用layoutSubviews来重新计算frame所以viewDidLayoutSubviews才是布局完成且frame已确定的时机。网易还可能在这个基础上多问一步如果在viewDidLoad里设置完约束后立即给一个视图加动画为什么动画不生效这就牵扯到约束驱动布局的时机和Core Animation的隐式事务机制。Auto Layout与Frame的取舍也是一个经典讨论题。笔试里不会让你写具体代码但会给你一个场景“已知上左下三个约束右边约束随屏幕宽度动态变化你怎么设计约束”这其实在考察你对约束优先级、UILayoutPriority、自适应布局的理解。很多人背过“用Masonry布局”但对content hugging和compression resistance到底怎么影响约束冲突的解决理解并不深。网易的客观题确实会问这类概念一道题约等于四个知识点约束优先级、固有内容大小、hugging优先级、compression resistance优先级。除了布局还有一条主线是UIKit渲染和GPU/CPU的交互。2020年那套题出现过“为什么列表滚动卡顿请从CPU和GPU的角度分析原因”这样的题目。普通人会回答“图片太大、布局复杂”但高分的答法是把离屏渲染、图层混合、圆角与阴影、光栅化这些细节都画出来并给出对应的优化手段。这种题没有唯一标准答案但你在笔试时如果能写清楚“CPU负责布局计算、文本排版、图片解码GPU负责图层合成、渲染绘制离屏渲染会额外分配内存并切换上下文因此要避免在滚动中大量设置圆角阴影”这种层次绝对是加分项。4.2 架构设计题MVC、MVVM、组件化如何写出高分答案简答题里通常有一道架构设计题。网易2020年iOS岗的一道原题大意是请描述你熟悉的iOS架构模式说明它的优缺点并结合一个实际场景说明在这种架构下如果业务迭代变快你如何重构。这道题的考察点不只是“你知道几种架构模式”而是考察你是否真的在项目里思考过架构演进。我建议在笔试中用一个清晰的框架回答先交代背景再描述现状与痛点然后给出重构方案最后说明收益和风险。比如你选了MVC就先说清楚“ViewController臃肿”是MVC被诟病最多的点然后说明你会在项目中引入MVVM将业务逻辑抽到ViewModel把ViewController当成纯粹的视图组装层。笔试中如果能写出ViewModel不依赖UIKit、通过Block或RAC/Combine完成数据绑定这就是一个非常扎实的答案。如果还能进一步提到组件化、路由解耦、二进制化以及它们带来的增量编译收益那你在这场笔试的大题部分基本就站稳了。这里有一个很多人忽略的应试技巧架构题不要只写文字如果能画一张简单的模块关系图或者用代码展示数据流向阅卷效率会高很多。在线笔试系统虽然不支持画图但你可以用缩进关系或者伪代码来表现模块分层。这个思路尤其适合编程题以外的设计类题目。5. 算法与编程题这些模板在iOS场景中反复出现5.1 编程题的总体特征与答题策略网易2020校招笔试的编程题部分是3道题整体难度与LeetCode中等题相当但第三道题会有一定工程色彩。我在跟同学们对题之后发现一个规律第一道题基本都是字符串或数组操作比如反转字符串、去重、统计频率这类难度偏低目的是让你热身第二道偏数据结构可能是链表、二叉树的遍历、栈的应用第三道则可能是动态规划、贪心或设计题而且常常包装成业务场景。那一年网易笔试的第三道题就是一道典型的工程场景题。它要求设计一个图片加载缓存模块。这道题我当时印象很深因为它不像普通算法题那样问“反转二叉树”而是把数据缓存、文件存储、并发控制、取消下载这些真实业务里绕不开的点全都包在“加载图片”这个最常见的需求里。常规解法就是设计一个缓存类interface ImageCacheManager : NSObject property (nonatomic, strong) NSCache *memoryCache; property (nonatomic, strong) NSFileManager *fileManager; - (UIImage *)cachedImageForKey:(NSString *)key; - (void)cacheImage:(UIImage *)image forKey:(NSString *)key; end光写出一个NSCache缓存只是及格线要拿高分还得体现出几个细节缓存淘汰策略NSCache本身具备自动清理能力你要解释它和NSDictionary的区别说明为什么要用NSCache而不是普通字典。多线程安全图片下载回包可能在子线程缓存的读写需要确保线程安全你可以在memoryCache操作时使用dispatch_barrier_async或者直接用串行队列包一层。内存和磁盘两级缓存内存缓存用NSCache磁盘缓存用FileManager写入Caches目录读缓存时先查内存再查磁盘查不到再发下载请求。取消请求如果界面快速滑动应该支持取消未完成的下载任务这需要给下载任务一个cancel标记或者在ViewModel层持有Task并控制生命周期。这些点全部写在答案里分数自然不一样。笔试不是考你会不会写一个函数而是考你在真实工程里有没有应对复杂性的能力。5.2 手写多线程安全的单例与Block应用场景除了算法题网易的笔试中偶尔也会出现“手写代码”类的专项题比如让写一个多线程安全单例。这个题目是面试高频笔试同样会出现因为能用干净的代码考察出你对GCD和线程安全的理解。标准答案是dispatch_once_t (instancetype)sharedInstance { static dispatch_once_t onceToken; static id sharedInstance nil; dispatch_once(onceToken, ^{ sharedInstance [[self alloc] init]; }); return sharedInstance; }这里有个笔试加分点解释为什么dispatch_once是线程安全的。它的底层实现是用原子操作对onceToken进行判断同时配合信号量或自旋锁保证只有一个线程执行Block后续线程会阻塞直到Block执行完成。一旦执行完onceToken被置为已完成状态后续调用直接返回不需要再加锁因此性能极高。我见过不少同学在写单例时多写了一个synchronized这是没必要的。dispatch_once本身已经保证了线程安全再加锁反而降低性能。笔试时如果能指出这一点说明你不只是背了模板而是知道模板背后的原理。这正好踩中网易这种大厂笔试题的“评分偏好”。6. 回顾与建议如果重来一次我会这样复习6.1 针对笔试而非面试的高效复习路线准备网易这类大厂的iOS校招笔试和准备面试是两个思路。面试更侧重项目深挖、交流表达笔试更侧重知识广度、反应速度和准确度所以一定要在不同的节奏上做刻意练习。我建议用“三周刷题法”第一周基础知识全覆盖。把iOS核心知识点整理成脑图包括内存管理、多线程、RunLoop、KVO/KVC、消息发送、UI渲染、网络层、数据持久化、架构设计。每天固定两小时专门看自己最薄弱的模块。这里最重要的是用输出倒逼输入每看完一个知识点就用“给自己讲一遍”的方式复述能把原理讲清楚才说明真的理解了。第二周疯狂刷真题和面经。不是只看题目而是每一道题都尝试在30分钟内写出完整的答案框架。尤其是简答题要训练“结论先行、分点展开、附上代码或图示”的答题结构。笔试系统大多数是机器判分加人工复核这种结构化答案最容易拿到高分。第三周专项突破编程题。每天保证3-5道LeetCode中等题重点关注字符串、数组、链表、二叉树、动态规划这五类。iOS的笔试编程题不考偏题怪题把常规题型刷熟就够了。刷题时要用纸笔或白板进行心态模拟因为在线笔试的编译环境往往没有IDE那么友好提前适应“没有自动补全、没有Debugger”的环境很重要。6.2 实战中的时间管理、答题顺序与心态调整最后说一下考场上真正重要的三件事。第一答题顺序。拿到卷子先花三分钟扫一遍全部题目给每道题标记“必拿分”、“努力拿分”、“可以放弃”三个档位。客观题中一眼就能做对的先做简答题中有思路的先写编程题里最擅长的先动手。不要在第一道困难题上耗太多时间得不偿失。第二时钟管理。客观题建议用总时长的三分之一解决因为后面的大题才是拉开差距的地方。编程题即使一道题没完全做出来也要把思路写在注释里或者在代码中说明“如果剩余时间充足我会再优化xx”这些都有机会让阅卷人看到你的思考过程。第三心态。网易这种级别的笔试遇到完全没有头绪的题非常正常那部分本来就不是给你拿分的。我当时是把自己的目标定为“做对75%的题”而不是“做对全部”。你只要保证自己会做的题全部做对把该拿的分都拿到其实就已经超过绝大多数人了。最后再分享一个亲身经历的小技巧在线笔试前一定要提前把常用的iOS代码模板默写一遍比如单例、图片缓存器、网络请求封装、常用的GCD组合group、barrier、semaphore。不要小看这个动作真正上了考场时间高压之下你会忘记很多平时熟极而流的东西而提前写好模板就是给自己上保险。这种坚持最后在笔试分数上一定能看到回报。