尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

从RunLoop到内存管理:欢聚时代iOS校招笔试A卷考点复盘

从RunLoop到内存管理:欢聚时代iOS校招笔试A卷考点复盘 说实话欢聚时代2017年这套iOS校招笔试A卷放在当年的环境下确实能筛掉一批只会“拖控件”的选手。我记得很清楚当时身边几个人考完出来脸色都不太好看不是题目有多偏而是它考察的层次和平时刷的题完全两个方向。2017年正好是iOS 11刚发布、Swift 3到4过渡的节点面试官想找的不是熟练背API的而是真正理解iOS运行机制的人。这套题放到现在看有些技术点已经变了但考察的逻辑没变基础扎不扎实、有没有深度思考过系统怎么干活、遇到线上问题有没有排查思路。这篇文章我就把这套A卷的考察逻辑、我印象中的典型题目和考点逐一拆开结合自己当年和后来带人时的心得聊点能直接用的东西。1. 笔试整体布局一套卷子到底想筛什么人1.1 题目结构与分值分布欢聚时代2017年校招的笔试A卷题型很典型主要分四块选择题、填空题、简答题、编程题。选择题大概占30%填空题20%简答题30%编程题20%。这个分值分布很有意思它说明公司不只是看你能不能把代码写出来更在乎你知不知道代码背后的原理。选择题考察的是iOS基础知识覆盖面很广。Objective-C的内存管理、property属性的关键字组合、分类与扩展的区别、KVC和KVO的机制、多线程方案、RunLoop的工作流程、UIKit的生命周期这些基本是必考的。填空题更恶心一点会考一些平时写代码根本不会注意的细节比如viewDidLoad和viewWillAppear谁先谁后、dispatch_once的token应该是static还是global、字符串用copy还是strong修饰有什么坑。简答题是真正拉开差距的部分考的是对某个机制的理解深度。比如“简述iOS中RunLoop与autoreleasepool的关系”“解释KVO的实现原理以及会带来什么问题”“NSOperation和GCD的适用场景分别是什么”。这些问题如果你只是背过八股文没有自己画过图、看过源码很容易答得泛泛而谈拿不到高分。编程题反而不是特别难考的都是经典算法链表反转、二叉树层序遍历、字符串处理、LRU缓存设计这类。但注意它要求用C/C/Objective-C写而且时间只有一小时左右要在白纸上手写代码这本身就是个考验。1.2 考点背后的岗位画像从这套题的设计往回推能看出来欢聚时代当年招iOS工程师最看重三件事底层原理的掌握程度、工程落地能力、排查问题的思路。为什么这么看重底层原理因为欢聚时代的核心业务是直播和音视频这类App对性能、流畅度、内存占用要求非常高。如果你的App在普通使用场景下就卡顿、闪退用户早就跑了。所以笔试里会大量考察RunLoop、内存管理、多线程这些直接关系到线上稳定性的知识点。工程落地能力则体现在简答题和编程题的组合上。简答题会考你怎么设计一个网络层、怎么处理大文件下载、怎么优化列表滑动卡顿。这些题目没有标准答案但能看出来你平时是真做了事情还是只写了一堆demo。我觉得这套题最值得借鉴的地方就是它的考点非常集中基本没有偏题怪题。所有题目都在围绕“一个合格的iOS工程师拿到一个线上App能不能保证它稳定、流畅、可控”这件事展开。2. 语法与内存管理Objective-C基础关2.1 必考的内存管理选择题2017年的题目里关于内存管理的选择题至少有五道基本都绕不开引用计数的加减、autorelease机制、循环引用这几个核心概念。我记得有一道题大概是在MRC环境下执行下面这段代码最后obj的引用计数是多少。代码如下NSObject *obj [[NSObject alloc] init]; [obj retain]; [obj autorelease]; [obj release];很多考生在这里栽了跟头。分析一下alloc之后引用计数是1retain之后变成2autorelease表示延迟到当前autoreleasepool销毁时再释放计数暂不变化release之后变成1。所以答案是1。但如果你把autorelease和release的顺序弄反或者不清楚autorelease到底是立即生效还是延迟生效很容易算错。这题的考点本质是你知不知道autorelease的实际释放时机。光背“自动释放池会在RunLoop休眠时销毁”是不够的你还需要理解在ARC环境下编译器会在合适的位置自动插入release和autorelease调用以及为什么某些方法返回的对象可以直接使用而不需要手动释放。还有一类高频题是循环引用。比如block里面用了self但没有使用__weak会不会造成循环引用。很多新手以为只有block直接持有self才会循环引用忽略了self持有block的情况。其实关键在于“谁持有谁”如果block被self的属性持有block内部又强引用了self这才会形成环。如果是临时创建的block、没有对外部self形成强持有就不会有问题。2.2 那些容易踩坑的细节题填空题里对属性关键字的考察也很有意思。比如问你声明一个“只读、原子、strong”的属性关键字应该怎么写。答案是property (nonatomic, readwrite, strong)加上readonly分类扩展或者直接property (atomic, readonly, strong)。但如果你把nonatomic和atomic搞混或者不清楚readonly为什么不能和readwrite同时出现就会出错。还有一个高频坑字符串类型用copy还是strong修饰。正确答案是copy原因很简单如果用strong当一个NSMutableString赋值给这个属性时外部后续修改这个可变字符串属性值也会跟着变数据的一致性被破坏。而用copy赋值时会做一次不可变拷贝切断了外部和属性之间的引用关联。这个知识点看着简单但能说明你有没有踩过“可变对象被意外修改”的坑。我当时答题的时候还遇到过一个填空synthesize和dynamic的区别。synthesize是告诉编译器自动生成getter/setterdynamic是告诉编译器不要自动生成由运行时或手动实现。这个知识点在2017年还算常见但现在基本不用了因为自动合成已经是默认行为。不过如果你在面试中被问到能说出它背后的机制还是有加分的。2.3 扩展Swift混编初现2017年Swift已经发布了三年多A卷里也出现了关于Swift和Objective-C混编的题目。虽然不是重点但也有选择题会问Swift中let和var的区别、Swift中如何桥接NSDictionary与[String: Any]、objc关键字的作用。这些题对当时的学生来说有点超纲因为很多学校还在教Objective-C。但如果你提前了解过Swift答起来会很轻松。我的建议是无论你准备哪一年的iOS面试Swift是必须提前预习的即使岗位描述里写的是Objective-C方向面试官也会默认你懂一点Swift因为新项目的技术选型大概率是Swift或者混编。3. 运行机制与系统框架iOS工程师的分水岭3.1 RunLoop从一道简答题看消息循环这一章是A卷里最体现功力的部分。简答题里有一道是“简述RunLoop与autoreleasepool的关系”。很多人看到这个题就懵了因为平时写代码根本不会直接和RunLoop打交道。RunLoop可以理解成iOS里的“事件循环大脑”它负责接收各种输入源触摸事件、定时器、网络回调等然后在没有事件时让线程休眠、有事时唤醒处理。而autoreleasepool的释放时机恰恰就绑定在RunLoop的某个节点上。具体来说主线程的RunLoop在每个循环周期结束时会销毁上一轮创建的autoreleasepool并创建新的。所以你在一个事件处理里创建了大量临时对象它们并不会立刻释放而是要等到这个RunLoop周期结束。这就是为什么在主线程执行一个很长的同步任务时内存峰值会很高——因为autoreleasepool一直没有机会释放。这道题的完整答法应该包含三部分第一RunLoop的基本机制包括它的几个mode和source第二autoreleasepool的释放时机与RunLoop周期的关系第三一个具体场景比如在子线程中需要手动创建autoreleasepool否则对象不会自动释放。如果你能结合例子讲清楚面试官就会觉得你是真的理解而不是背答案。3.2 KVC/KVO与动态特性关于KVC和KVO的题目A卷里至少有一道选择题和一道简答题。选择题一般考的是KVC的取值顺序比如调用setValue:forKey:时系统会先查找setKey:方法找不到再去调用_key或key的实例变量而KVO则是基于KVC实现的机制。简答题里有一道是“解释KVO的实现原理以及使用KVO时需要注意哪些问题”。这个题目我最喜欢因为它的答案深浅都能说但说深的人很少。KVO的本质是系统在观察对象时动态生成一个子类比如NSKVONotifying_XXX并重写被观察属性的setter方法。当你修改属性值时实际上是调用了这个子类的setter它会在修改前后分别调用willChangeValueForKey:和didChangeValueForKey:从而触发观察回调。但要注意如果你直接操作成员变量比如_name xxx不走setterKVO是不会触发回调的。这就是一个很典型的坑。另外KVO还有个问题是需要手动移除观察者在iOS 11之前忘记移除会导致崩溃后来系统优化了这一点但依然建议配对使用。这个问题答得好的话能看出你是不是真的用过KVO调过一些疑难杂症而不仅仅是知道API怎么拼写。3.3 多线程方案对比GCD、NSOperation、NSThread多线程是iOS笔试里的常客A卷也不例外。我记得有一道简答题是“对比GCD和NSOperation说明它们的适用场景”。这道题看起来简单但想答得高分并不容易。简单答法GCD底层基于C语言API使用方便性能好NSOperation基于GCD的封装支持取消、依赖、KVO等高级特性。但实际上面试官想听的不只是这些口号。更好的答法是结合具体场景。比如一个下载任务需要在用户退出页面时取消你用GCD的dispatch_block_t也能取消但需要额外处理而NSOperation直接把取消的逻辑内置了而且可以通过依赖关系控制执行顺序代码结构会更清晰。再比如一些简单的一次性任务、延迟任务用GCD的asyncAfter就够了没必要引入NSOperation的复杂度。还有一道填空题是dispatch_once的token声明为static为什么其实是因为dispatch_once_t要求这个变量必须是一个全局的、生命周期贯穿整个程序运行的值如果声明成局部变量每次调用都重新初始化dispatch_once就会失效。这个细节不写一次错误代码真的很难记住。4. 网络与数据处理线上环境的基础素养4.1 HTTP/HTTPS与TCP/UDP的基础题欢聚时代的业务是直播所以网络相关的题目特别重要。A卷的选择题里考了HTTP状态码的含义、HTTPS的握手过程、TCP和UDP的区别。有一道题我记得很清楚问的是“TCP三次握手的作用是什么”。很多人脱口而出“确认双方连接建立”但这只说对了一部分。三次握手最核心的作用是让通信双方确认彼此的发送能力和接收能力是正常的。第一次握手客户端确认了服务端的接收能力第二次握手服务端确认了客户端的发送和接收能力第三次握手客户端确认服务端的发送能力也正常。通过三次握手双方都能确认“我发的你能收到你发的我也能收到”。除此之外还有一道题考了HTTP和HTTPS混合内容的问题。2017年正是App Store强制要求ATSApp Transport Security的时候很多开发者发现自己的App因为用了HTTP请求被拒审所以这道题还挺与时俱进的。这题的考点是HTTPS连接建立时证书信任链和公钥加密怎么配合以及为什么全站HTTPS比只给登录接口加HTTPS更安全。4.2 数据解析与容错处理简答题里有这么一道“从服务端返回的JSON中取出一个字段你一般怎么写”看起来简单但考察的是你的健壮性意识。初级答法[dict objectForKey:name]然后直接使用。中级答法dict[name]然后判空。高级答法判断返回的数据结构是否合法、字段是否存在、类型是否正确再决定是否使用默认值或上报异常。我记得有一道填空题就是专门考这个的给定一个NSDictionary要求安全取出data字段下的list数组如果data不是字典或者list不是数组应该返回空数组而不是崩溃。这题写起来代码量不大但很考验你对类型判断的敏感性。这里我可以提供一个通用的小工具方法- (NSArray *)safeArrayForKey:(NSString *)key inDict:(NSDictionary *)dict { if (![dict isKindOfClass:[NSDictionary class]]) { return []; } id value dict[key]; if ([value isKindOfClass:[NSArray class]]) { return value; } return []; }这个方法在线上环境里很实用因为服务端返回的数据格式经常会发生意想不到的变化一个字段从数组变成字典、从字符串变成数字都是常态。如果你没有做类型判断线上崩溃率会高得吓人。4.3 网络层设计初步A卷的简答题里还有一道“设计一个网络层的思路”的题。这个问题没有标准答案但能看出来你有没有真正封装过网络请求。当时我答题的思路是这样网络层要分成三层。最底层是基础请求层负责把NSURLSession或NSURLConnection的请求发出去并处理回调中间层是业务请求层负责组装每个接口的URL、参数、和请求方式并做公共参数的拼接最上层是数据解析层负责把返回的JSON转成对应的模型并做错误码的统一处理。此外还要考虑到统一的失败重试机制、请求取消机制、请求去重机制、网络状态监测、缓存策略、日志上报。这些点如果你只是把AFNetworking包了一层就觉得自己设计过网络层那是不够的。面试官想听到的是你曾经真的处理过“同一个接口被并发请求两次”、或者“请求失败后短时间内重复点击导致重复提交”这类线上的真实问题。网络层设计这道题其实是在考察你是否具备“系统设计”的思维。它不是让你背API而是让你思考在一个真实的App里网络模块如何和业务解耦、如何做统一异常处理、如何方便排查线上问题。有了这个意识你在考场上就能答出和别人不一样的东西。5. 架构设计与工程实战从“能写”到“会写”5.1 MVC/MVVM辨析与选择大概从2016年开始MVVM在iOS圈子里火了起来所以2017年的笔试题里出现架构相关的辨析题并不意外。选择题会问MVC和MVVM的区别简答题会让你谈谈对MVVM的看法。MVC是苹果官方推荐的模式但也因为“Controller太重”被吐槽了很多年。MVVM的核心是引入了ViewModel层把原本Controller里的数据整理、业务逻辑、展示数据准备这些工作抽离出来让Controller只负责视图和用户交互。这个模式在iOS里的实现困难在于KVO和block实现的双向绑定机制不好维护不如前端框架成熟一个ViewModel里堆了太多逻辑也会变成“第二Controller”。我记得当时答这道题的时候专门提了一句“架构模式没有银弹关键看团队和项目的规模”。面试官后来在二面时还问了我一句“那你在实际项目中会把什么逻辑放进ViewModel、什么逻辑留在Controller”这个问题现在也值得每个iOS开发者自己想一想。我现在的习惯是网络请求、数据转换、表单校验放ViewModelUI更新、生命周期管理、跳转逻辑留在Controller。这样至少能保证Controller不要超过一千行。5.2 大文件下载与弱网优化实战题如果说前面的考题是在考基础那这道简答题就是在考你是不是一个“真正写过东西的人”。题目大概是“一个视频App需要支持大文件下载你会怎么设计下载功能需要注意哪些问题”看题的时候我就知道面试官不想听到“用AFNetworking的下载方法就行了”。他真正想考核的是你有没有考虑过这些细节第一断点续传。下载了一半断网了重连之后能不能接着下载而不是重新下整个文件。这就涉及到NSURLSession的downloadTaskWithResumeData:方法以及如何保存、恢复resumeData。第二后台下载。App切到后台后下载任务是否还能继续。这里需要用到后台下载会话和管理器并注意在系统限制时间内不一定能全部完成。第三磁盘空间。下载前有没有检查磁盘剩余空间如果空间不足怎么提示用户下载过程中临时文件的清理机制是怎样的第四文件校验。下载完成后有没有通过MD5或大小校验文件的完整性大文件在传输过程中损坏的概率不高但一旦出错用户看到的就是一个无法播放的视频。这些点都答上面试官基本就能确认你是有过真实项目经验的而不是只在学校里写过注册登录demo。弱网优化也是直播类App的核心话题。A卷里有一道选择题问的是“弱网环境下优先推荐采用哪种策略”选项包括加大缓冲区、降低视频清晰度、增加重试次数、开启预加载。这题其实没有绝对的标准答案选“降低清晰度预加载”的优化组合是视频类App比较成熟的方案。但这道题更重要的考点是你是否理解弱网优化不是某一个技术点而是一整套策略的组合。5.3 工程化与性能优化题性能优化方面A卷里面有一道十分接地气的简答题“TableView滚动卡顿你怎么排查和优化”这个题目即使放到今天依然是iOS面试的经典问题。当年这套题考它说明他们真的在意列表流畅性毕竟直播App里弹幕、评论、礼物动效都是高频列表场景。我的答题思路是分层次排查的首先用Instruments的Core Animation工具查看滚动时的帧率确认卡顿是发生在主线程的哪些函数上。其次检查cell的构建和复用比如有没有在cellForRowAtIndexPath里做耗时操作、有没有错误地使用自动布局导致大量计算。然后检查图片加载和缓存是否使用了合适的尺寸解码有没有用UIImageView加载大图。最后检查有没有在滚动时频繁触发离屏渲染比如圆角、阴影、透明度叠加。这个思路其实可以提炼成一套通用的性能排查方法论先定位瓶颈再有针对性地优化而不是一上来就改一堆代码。很多考生死记硬背“优化列表要复用cell、要异步加载图片”但根本不知道怎么定位为什么卡。这就和医生不看检查报告就开药一样碰巧对了是运气碰不对就是事故。关于离屏渲染我想多说一句。当年这道题很多人的回答停留在“避免设置圆角”这个层面。其实圆角不一定都会触发离屏渲染只有当cornerRadius和maskToBounds同时使用、并且layer没有快速绘制时才会。如果你能把这个细节讲清楚面试官会明显对你另眼相看。类似的还有阴影的shadowPath优化、shouldRasterize的使用场景和副作用。6. 踩坑复盘与备考建议兼谈我个人对这些考点还会不会变的判断6.1 当年考生的常见失分点我参加过那场笔试之后后来也帮朋友的孩子看过类似的题目他们踩的坑和当年我们几乎一模一样。我整理了几个最高频的失分点第一答题时只写结论不写理由。比如问“应该用copy修饰NSSting属性”如果你只写“因为防止可变字符串被篡改”没有把NSMutableString赋值给copy属性时的内存变化过程写出来得分就会很有限。面试官想看到的是推理过程不是答案本身。第二RunLoop和相关机制分不清。很多考生背了一堆RunLoop的mode、source但一问到autoreleasepool和RunLoop的关系就卡壳。这是因为只会背概念没有把几条线索串成一个完整的故事。第三编程题在变量名和边界条件上丢分。校招笔试的编程题算法本身通常不难但手写代码时很容易出现空指针、数组越界、死循环这类问题。我记得A卷里有一道“反转链表”的题有一半考生写出来的代码都有循环链表或者丢失next指针的问题。这说明很多人的代码是在编译器/IDE的帮助下才能通过脱离工具就原形毕露了。第四架构题答得空泛。谈到MVVM或网络层设计翻来覆去就那几句话没有任何自己的实践体会。面试官最反感这种答案因为它无法判断你是真的思考过还是刚背完菜鸟教程。6.2 短期内如何高效备考如果你想用这套题作为iOS面试的刷题样本我的建议是分三步走。第一步把基础题全部过一遍不要只看答案要能自己讲出原理。比如内存管理、Property属性、KVO、GCD、RunLoop这些每个知识点都要能画出简单的关系图能举例说明“什么时候会出问题”。第二步针对简答题写自己的答案模板。不是让你背八股而是根据你真实做过的项目给每个问题准备一个“我遇到过”的例子。比如面试官问“怎么优化列表卡顿”你就说“我上个项目里出现过什么现象我用Instruments定位到是什么原因后来怎么改的”这样的答案比任何通用模板都强。第三步编程题一定要动手在白纸上写或者用纯文本编辑器写不要用带自动补全的IDE。每天练两三道经典题重点训练边界条件的处理。链表类、树类、字符串处理、动态规划这四类是校招笔试里出现频率最高的。另外有一个技巧拿到一套真题后先不要看答案先自己模拟考试做一遍再对着答案修订。这个过程很痛苦但收获也是最大的。你做过一遍的题比你看十遍答案都记得牢。6.3 我个人对这些考点还会不会变的判断虽然这篇文章聊的是2017年的题目但我不建议你把目光完全锁在那年卷子上。技术是会过时的但考察的底层能力不会。比如内存管理这块ARC大行其道的背景下和MRC相关的题目确实会变少但循环引用、autorelease时机、block捕获变量这些底层机制依然是重中之重。再比如RunLoop虽然我们平时写的业务代码几乎不直接操作RunLoop但只要你做性能优化、做卡顿监控、做线程保活就绕不开它。2017年考它现在面试也在考它未来还是会考。网络和数据解析同样如此技术栈从NSURLConnection到NSURLSession再到Swift的async/await但HTTP状态码、HTTPS握手、JSON容错处理、断点续传这些问题的本质从来没变过。面试官用一种新的形式包装旧的问题就是为了考察你能不能透过现象看到本质。我觉得这套A卷真正厉害的地方不在于题目有多难而在于它通过一份卷子把“理论功底”“工程经验”“代码能力”三个维度全部覆盖了。你如果能把它的考察逻辑吃透自然就能举一反三应对大部分iOS岗位的笔试面试。当年我在答这套题时最明显的一个感受是平时那些“代码能跑就行”的偷懒在笔试里全都会暴露。很多知识点我以为自己会了但等到需要用文字把原理讲清楚的时候才发现自己其实一知半解。后来我养成了一个习惯每学一个新知识点都要尝试用一两句话把它讲给一个完全不懂的人听。讲不明白就说明还没真正懂。这套题大概也是类似的用意吧。
返回列表