欢聚时代iOS笔试题全解析:内存管理、多线程与性能优化考点深度拆解
2018年秋招季我参加了欢聚时代YY的校招笔试iOS方向的A卷。这套题当年在应届生圈子里口碑不错考察范围覆盖了iOS基础、内存管理、网络、架构设计甚至还有一部分计算机通用基础。这几年陆陆续续有不少学弟学妹私信问这套题怎么解、有没有答案。我一想与其零散回复不如把当年那套卷子涉及的考点和我的解题思路完整写出来。不管你目标是不是欢聚时代只要想投直播、音视频、社交类产品的iOS开发岗这套题的参考价值都很大。先说下整体感受欢聚当时作为直播赛道的头部玩家业务场景非常吃性能、吃流畅度、吃网络稳定性所以笔试题的审题重点很明确——不考死记硬背的API考的是你踩过多少坑、对系统机制的理解有多深。A卷大概分为单选、多选、填空、简答题和一道综合设计题题量大、覆盖面广两个小时内想全部写完美基本不现实必须有策略地分配时间。下面我按考点模块把整张卷子拆开讲。1. 欢聚时代这套iOS笔试题到底考什么1.1 出题逻辑与考察目标先看大方向。整张卷子给我最深的印象是它几乎没出“某某API的最新参数”这类纯记忆题而是反复围绕iOS开发里最容易出问题的几个核心机制出题。比如内存管理、多线程、网络、视图生命周期以及UI层面的渲染性能。这背后其实反映了直播类App的开发痛点。直播业务对App的要求非常苛刻长时间驻留、高频率UI刷新、弱网环境下的数据交互、大量图片和视频的加载、复杂弹幕图层叠加。任何一个环节出现内存泄漏、主线程卡顿、网络请求失控都会直接影响用户体验。所以欢聚出题的底层逻辑不是“测试你会不会调库”而是“测试你能不能写出在极端场景下依然稳定运行的代码”。比如那张卷子里有一道填空题问“在非主线程更新UI会怎样”。很多新手可能觉得“会崩溃”或者“会失效”。但正确答案要落到系统表现不一致上部分情况确实会崩溃、部分情况下UI刷新会延迟、还有极小概率出现难以复现的诡异界面状态。这考的就是一线开发者的实感。1.2 试卷结构和时间分配建议A卷的分值分布我没记全但大致结构是这样的单选和多选大概占总分30%填空占15%简答占35%综合设计题占20%。选择题覆盖面很杂从Objective-C语法细节、Foundation框架到UIKit和系统机制都有填空题偏重基础概念的记忆精度简答题是重头戏围绕内存、多线程、网络出开放题最后一道综合设计题我记得大概是让设计一个直播间的消息处理模块。整体来看这套卷子的难度在当年校招里属于中偏上比很多只考算法题的公司更贴近实际业务。我的建议是选择题控制在40分钟以内简答题留足60分钟综合设计题至少留20分钟。为什么因为简答和设计题才是拉开分数的地方选择题不会可以先过别翻车在时间分配上。2. 内存管理iOS笔试绕不开的第一座大山2.1 ARC到底解决了什么问题欢聚A卷在内存管理这块的题量是最大的光选择题就至少有5道。这很正常iOS开发里内存问题引发的崩溃占比相当高尤其是直播这种长时间运行的场景一次内存泄漏累积到一定程度App直接被杀掉。有一道选择题问“ARC在编译期做了什么”。不少人的第一反应是“自动插入release和retain代码”。大方向没错但不够完整。ARC的核心工作是编译期的引用计数管理也就是在合理的位置自动插入retain、release、autorelease和dealloc调用同时通过命名约定帮开发者推断内存管理语义。另外ARC还处理了weak、strong、copy这些属性修饰符的底层代码生成。更关键的一点ARC并不处理循环引用。这是面试题里最经典的陷阱。ARC只负责把该释放的对象释放掉但如果两个对象互相强引用它们的引用计数永远不会降到0于是泄漏。这个点A卷也考了问“下列哪种情况会造成内存泄漏”选项里就有NSTimer、Block、Delegate这几种经典组合。2.2 循环引用的三种典型场景与解决方案循环引用这种题每年校招必出欢聚A卷的简答题里直接让考生“写出三种循环引用的场景并给出解决方案”。我建议每个准备iOS面试的人都把这道题背到条件反射的程度Block循环引用对象持有BlockBlock内部又强引用了对象自身。解决办法是用__weak typeof(self) weakSelf self在Block内部再用__strong typeof(weakSelf) strongSelf weakSelf做一次保护。注意如果Block是单向调用强引用不会成环但如果是存储型Block那就必须weak。NSTimer循环引用ViewController强持有TimerTimer的target又强持有ViewController这就是一个环。更好的方案是iOS 10之后用block类型的Timer API内部以weak方式捕获target或者在viewWillAppear里创建、viewWillDisappear里销毁配合[timer invalidate]。Delegate循环引用如果delegate属性用strong修饰被代理对象又持有delegate指向的对象这就成环了。正确写法是property (nonatomic, weak) idXXXDelegate delegate。这里有个细节比特意提一下为什么Block里用了weakSelf之后往往还要再转一次strongSelf。因为weakSelf随时可能被置空如果在执行Block期间对象被释放了那么Block里的代码可能执行到一半self变成nil某些依赖self的方法调用就会出问题。转成strongSelf后Block执行期间对象不会被释放逻辑是安全的。这个点在回答简答题时主动提出来会加分很多说明你真的处理过线上问题而不只是背了答案。2.3 笔试中关于内存管理的进阶考点除了循环引用A卷还考了copy和strong修饰NSArray、NSString、NSDictionary的区别以及autoreleasepool在循环中的作用。关于copy和strong核心记忆点是集合类型用copy修饰后如果你把一个NSMutableArray赋值给这个属性实际赋值的是一个不可变副本。这样外部再修改原来的可变数组属性不会跟着变是“值传递”的效果。用strong的话指向的是同一个可变的数组对象外部一变属性也变。很多线上Bug就是这么来的数据无缘无故被改了查半天发现是某个属性的修饰符写错了。autoreleasepool的考点相对简单但我估计不少人丢分了。它本身不解决内存泄漏它解决的是延迟释放的问题。正确使用场景是大量临时对象被创建时比如循环1万次生成随机字符串不用autoreleasepool的话这一批临时对象会堆积到当前RunLoop循环结束才释放内存峰值会很高。在for循环内部加一个autoreleasepool每轮循环结束就释放一批峰值就降下来了。3. RunLoop、KVC/KVO与多线程基础中的基础3.1 RunLoop的常考角度RunLoop是iOS面试题里的“玄学”知识点平时开发用得不直接但一旦涉及卡顿优化、App启动、界面滑动流畅度就绕不开它。欢聚A卷在RunLoop上出了一道简答题描述一下RunLoop的几种运行模式Mode以及它们分别用在什么场景。这里的关键词是Mode。RunLoop一共有5种Mode但日常接触最多的是kCFRunLoopDefaultMode和UITrackingRunLoopMode。前者是App默认的运行模式后者是UIScrollView滑动时切换到的模式。很多人面试时答不上来为什么NSTimer在滑动页面时会停止就是因为Timer默认被加入了DefaultMode而滑动时RunLoop切换到了TrackingModeDefaultMode下的Timer自然就不执行了。解法也很经典把Timer加入NSRunLoopCommonModes这样它就能同时在DefaultMode和TrackingMode下运行。不过需要注意的是CommonModes不是一个具体的Mode而是一个“标记集合”把某个事件源加入CommonModes它就能在多个常见的Mode里被处理。顺带说一句如果简答题问你“如何监控App卡顿”可以从RunLoop的CFRunLoopObserverRef入手观察RunLoop从source0到source1再到observer回调之间的耗时超过阈值就认为卡顿。我记得那年的卷子没有直接出这道题但后来喜马拉雅、微博的iOS面试都问过类似的问题多准备一点没坏处。3.2 KVC/KVO的原理与触发条件A卷在KVC和KVO各出了一道题。KVC考的是取值赋值的底层顺序KVO考的是触发机制。KVC的全称是Key-Value Coding本质上是通过字符串Key来间接访问对象属性。默认的setValue:forKey:查找顺序是这样的先找setKey:方法找到就直接调用找不到再看类方法accessInstanceVariablesDirectly是否返回YES返回YES就按_key、_iskey、key、iskey的顺序查找实例变量还找不到最后触发setValue:forUndefinedKey:默认实现是直接抛异常。这个查找顺序是标准的我建议准备面试时的朋友把这一条背得一字不差。KVO的话题干是“如何手动触发KVO”。正确答案是在willChangeValueForKey:和didChangeValueForKey:之间修改属性值就能手动触发KVO回调。如果不调用willChangeValueForKey:只调didChangeValueForKey:系统会做一次检查发现值其实没有变化就不会触发回调。所以必须成对调用。这里补充一个当年很多人栽了的坑KVO监听的是属性值变化不是成员变量变化。直接修改成员变量是不触发KVO的。另外对同一个对象重复添加同一个keyPath的观察者会导致重复回调移除不存在的观察者会直接崩溃。所以现在大家普遍用Facebook的KVOController或者直接用系统新出的NSKeyValueObservation。后者在iOS 11之后才可用按标题里的时间线来看2018年校招时问到KVOController也不意外。3.3 GCD与NSOperation的选择逻辑多线程是校招笔试的绝对大头欢聚A卷至少出了4道。核心考点包括dispatch_queue_t的串行并发区别、dispatch_async和dispatch_get_main_queue的配合、dispatch_barrier、dispatch_group、NSOperationQueue的依赖关系。选择题里有一道特别容易错的问“以下哪种方式可以在后台线程执行耗时操作后回到主线程更新UI”。答案不是唯一的dispatch_async(dispatch_get_main_queue(), ^{})没问题[NSOperationQueue mainQueue] addOperation也没问题但直接在同步串行队列里嵌套主线程同步操作会导致死锁这个选项是坑。关于GCD和NSOperation怎么选我的建议是能上GCD就上GCD更轻量、更快。但如果你需要“取消操作”“设置依赖关系”“控制最大并发数”那就要用NSOperationQueue因为它把这些场景封装成了API。当年KVO触发那道题旁边考官其实想听你有没有“面向场景选工具”的意识而不是无脑全用GCD。还有一个高频考点是dispatch_barrier_async。它的作用是先执行它之前添加的任务然后执行它自己再执行它之后添加的任务。适合做“多读单写”模型多个读取并发进行写操作需要独占锁。如果你在面试中能主动说出“可以用barrier替代读锁性能更好”面试官一般会认可你的工程经验。4. 网络层、架构与性能优化拉开分差的关键题4.1 网络层设计从HTTP到DNS再到弱网优化直播类App的网络环境非常复杂主播的网络可能很烂观众的跨地域链路可能很长所以欢聚的笔试题在网络部分明显偏重弱网优化和HTTP协议细节。有一道简答题让我印象很深客户端网络请求超时时间一般设置多少怎么设置合理这道题没有标准答案考察的是你对业务场景的理解。我当时写的方案是普通接口请求超时设置15秒上传接口可能要60秒以上但下载大文件要区分“建立连接超时”和“数据传输超时”分别设置。还提到用NSURLSessionConfiguration的timeoutIntervalForRequest和timeoutIntervalForResource来做精细控制超时后自动重试一次重试前先检查当前网络状态。另外有一道选择题考了HTTP和HTTPS的区别以及TLS握手的基本流程。这个几乎是必考项需要记住重点HTTPS HTTP TLSTLS握手主要做三件事——身份验证证书、密钥协商交换会话密钥、加密通信。2018年还没有TLS 1.3的普及问题但现在准备面试的话建议把TLS 1.3的0-RTT机制也了解一下因为字节、腾讯都开始问了。网络部分还有一道关于DNS的填空题移动端HttpDNS相比传统LocalDNS有什么优势答案是避免LocalDNS污染、绕过运营商调度不均的问题、缩短解析时间。欢聚的直播产品对首帧延迟很敏感HttpDNS方案在它们的技术体系里非常常见。如果你在简答题里能把HttpDNS的原理和接入方式说清楚面试官会觉得你有实战经验。4.2 架构设计题的答题思路A卷最后一道综合设计题我记得是设计一个直播间的消息处理模块。这种题没有标准答案面试官看的是你拆解问题的思路和方案落地能力。我当时大致分了四层来讲数据层采用WebSocket维持长连接接收服务端推送的消息消息格式使用JSON按消息类型分发。WebSocket断线自动重连重连间隔指数退避。缓存层拉取最近消息使用本地缓存内存磁盘避免每次进入直播间都全量重新拉取提升冷启动速度。业务层按消息类型弹幕、礼物、进场提醒、系统公告分别处理。礼物和弹幕需要走独立的渲染通道避免互相影响。UI层采用异步渲染弹幕消息不在主线程创建富文本而是预渲染后缓存滑动时只做图层移动。这套思路后来被不少朋友当模板复用即使面对的是“设计一个IM消息系统”“设计一个信息流缓存模块”也能套上。关键点是每个层只说核心职责和关键技术点不要陷入细节太久。面试官其实想听的是你有没有宏观视野。4.3 性能优化类题目的答题框架性能优化是欢聚笔试另一个明显的倾向毕竟直播类App的流畅度是生命线。有一道简答题问“如何优化TableView的滚动流畅度”。我在答题时给了一个框架层级瘦身减少视图层级能用CALayer就不用UIView能用drawRect就不叠加层。提前计算提前算好行高并缓存不要在heightForRowAtIndexPath里临时计算。异步绘制文本和图片的绘制放到子线程绘制完成后再切回主线程添加到图层上。复用机制cell复用标识唯一避免在cellForRowAtIndexPath里频繁创建新对象。预加载提前加载相邻cell的图片或数据滑动到目标位置时直接展示。这类题的通用答题框架就是“检测—定位—解决—验证”四步。检测用什么工具Instruments的Time Profiler、Core Animation、系统自带的FPS监测定位是哪一层的问题AutoLayout的布局计算、离屏渲染、图片解码耗时对应解决最后用数据对比验证优化前后是否有提升。把这个流程说出来即使方案不是最优面试官也能看到你有系统性的排查能力。5. 从2018年笔试题看今天iOS面试的变化与不变5.1 那些“必考不动摇”的内容面试题每年都在变但iOS核心机制的考题非常稳定。内存管理是永远的第一关键词RunLoop、多线程、KVC/KVO、网络请求、视图生命周期这几个基础考点从2018年一直考到现在。原因很简单这些知识点直接决定了一个iOS开发者能不能写出稳定、流畅的App。算法可以临时刷题架构可以背诵模板但这些“底层机制理解”类的问题没有日常积累和踩坑经验根本答不深刻。我记得当年有一个选择题是问“viewDidLoad和viewWillAppear的调用顺序”看起来很简单但结合“从A页面push到B页面再pop回来哪些方法会被调用”这个场景一起考的时候有好几个人就在这丢分了。现在的面试题更卷会继续延伸问到“viewDidLoad调用时view的frame是什么状态”“viewWillAppear和viewDidAppear之间layoutSubviews会调用多少次”如果基础不扎实很容易答乱。5.2 现在的iOS面试更看重什么对比2018年现在的iOS校招笔试题出现了一个明显变化Swift比重显著增加Objective-C不再是唯一主角。但在欢聚这种老牌直播公司里OC代码存量非常大所以它们面试反而会更务实——问你OC代码在ARC下的内存管理问你如何处理已有的OC工程和Swift混编。另一个变化是对组件化和工程化的考察增多了。现在的笔试和面试经常出现“如何设计一个组件化方案”“CocoaPods和Swift Package Manager怎么选”“如何利用二进制化降低编译时间”这类问题这对应的是业务复杂度提升后对工程效率的追求。但核心的东西没有变把一个问题讲到多深取决于你到底做过多少。背答案只能过第一轮技术面二面三面一问细节就露馅。所以我的建议是准备iOS面试不要只看面经题解最好自己写小Demo复现那些典型问题真真切切把循环引用打出来、把KVO手动触发打出来、把TableView卡顿优化跑一遍印象和背题完全不同。6. 笔试之外的长期主义这套2018年的欢聚笔试题后来成了我iOS面试体系里的“真题库”每次学弟学妹让我推荐练习题我都会让他们先捋一遍这套题的考点。但说句实在话笔试只是起点真正考验人的是入职后面对线上问题时能不能快速定位、稳妥应对。我个人的体会是把每道真题当成一个入口顺着它往深处挖。比如笔试考了RunLoop你就去研究整个App启动过程里RunLoop做了什么考了KVO你就去了解KVO的底层实现是isa-swizzling考了弱网优化你就去找一本TCP/IP的书把超时重传、拥塞控制搞明白。这样一道题能吃透一串知识点面试时自然有底气。最后再分享一个小技巧准备任何一套笔试题时不要只看自己的答案拿给两三个同方向的朋友各做一遍然后对比解题思路。同一道题不同人给的方案差异会非常大这种碰撞往往能帮你打开视野。毕竟面试从来不是只有一个标准答案能自圆其说、能经得住追问才是真本事。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →