ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

欢聚时代iOS校招笔试题B卷复盘:核心考点与答题技巧

欢聚时代iOS校招笔试题B卷复盘:核心考点与答题技巧 前阵子整理旧资料翻到一份“欢聚时代2017校招笔试题目iOS工程师类B卷”的扫描件。那会儿我还在准备秋招对这类一线互联网公司的iOS笔试题又爱又恨爱的是考点很集中恨的是不少题目表面上在问语法实际考的是底层理解。这几年我带过几个实习生也经常帮新人复盘笔试再回头看这份B卷发现它其实很能代表那个阶段大厂对iOS工程师的期待。这篇文章就以上面这份B卷为引子结合我自己的作答经验以及后来面试别人时看到的典型问题把高频考点一个个拆开讲。无论你是正在准备校招的学生还是工作后想换环境的iOS开发应该都能从里面捞到点东西。1. 欢聚时代iOS笔试题B卷的整体画像1.1 校招笔试到底想筛什么样的人先说结论这类笔试不是要你拿满分而是要你在有限时间内展现出“可培养的工程思维”。欢聚时代当年在做直播和音视频相关业务非常看重iOS端的基础功底所以B卷里没有太多偏门语法反而是对Foundation、UIKit、Runtime这些日常用到的框架反复追问。换句话说如果你连property的修饰符都说不清楚后面UI写再多也没用。这份卷子给人的第一感觉是知识点覆盖广但深度并不夸张。选择题大多考概念辨析比如ARC下strong和weak的区别、KVO在什么情况下不触发、RunLoop的mode有什么用。简答题开始上强度会给你一段代码问你里面的循环引用在哪里、怎么改。最后一道编程题往往是场景设计比如让你实现一个图片缓存库。整套卷子做下来其实是在模拟一个iOS开发者的日常先判断问题再动手写方案。1.2 B卷的结构与时间分配据我回忆B卷大概是三类题选择题、简答题、编程题时间在90到120分钟。选择题考概念辨析简答题考“为什么”编程题考手写能力。我当时给自己定的策略是选择题10到15分钟搞定简答题控制在40分钟左右剩下的时间全部给编程题。因为选择题里经常有几道“陷阱选项”一旦犹豫就会挤占后面的大题时间。这里要提个醒如果对某道选择题完全没把握不要空着先填一个最合理的并在草稿纸上记下来等做完编程题再回头复查。我自己就遇到过一道关于weak底层实现的题第一次选错了检查时发现“对象释放后weak指针会被置为nil”这个选项更合理才改对。这种“先跳过再回来”的策略在时间紧张的笔试里很管用。2. iOS核心考点逐个拆解2.1 内存管理从MRC到ARC再到循环引用B卷里一定会有一道内存管理题常见场景是block或者NSTimer。现在很多项目都上了ARC不需要手动retain/release但笔试问的是底层原理strong对应retainweak对应assign加自动置nilunsafe_unretained和weak的区别是前者释放后不会置nil。我印象很深的是NSTimer循环引用。很多人的答案会说在dealloc里invalidate但这个说法是有问题的。因为timer加入RunLoop后RunLoop会强持有timertimer又强持有targettarget的dealloc根本不会触发。正确做法是先理解生命周期在viewWillAppear里创建timer在viewDidDisappear里invalidate或者用block形式的API并弱引用self。比如这样写__weak typeof(self) weakSelf self; self.timer [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) { __strong typeof(weakSelf) strongSelf weakSelf; if (strongSelf) { [strongSelf update]; } }];这里有个细节在block内部先把weakSelf转成strongSelf是防止执行过程中对象被释放导致后续方法调用不稳定。虽然在大多数场景下weakSelf直接用也没问题但写成strongSelf是更严谨的习惯。笔试时把这个细节写出来面试官会觉得你踩过坑。2.2 block与对象生命周期block也是B卷的常客常见问题包括“为什么block属性要用copy”“为什么在block里修改外部变量要用__block”。要答到点子上在MRC时代block默认在栈区copy可以把它拷贝到堆上保证作用域结束后仍然可用ARC时代虽然很多情况下编译器会自动处理但属性修饰符写成copy仍然是标准做法。更深一层的问题是对循环引用的理解。block会捕获外部变量如果block被self持有block内部又用到了self就会形成环。标准答案是用weak-strong dance。笔试时经常给出这样一段代码self.block ^{ [self doSomething]; };然后问哪里有问题。除了用weakSelf之外还应该提到DispatchAfter、UIView动画的completion block这些场景同样存在引用环。说明你对block的捕获机制不是死记硬背而是真的理解了“谁持有谁”的关系。2.3 KVC/KVO与属性修饰符KVC/KVO在B卷里出现频率很高因为这类题目能同时考察消息发送机制和动态特性。KVC要理解setValue:forKey:的查找顺序先找setter方法找不到再看成员变量再找不到才调用valueForUndefinedKey:。KVO则要知道它底层是通过isa-swizzling动态生成子类来实现的而且触发条件是setter方法被调用。如果你直接修改成员变量KVO不会触发。和属性修饰符结合起来最常见的问题是“atomic是否线程安全”。答案是否定的。atomic只保证getter/setter方法的原子性不保证对象内部属性的线程安全。比如一个NSMutableArray声明为atomic多个线程同时对它进行addObject操作仍然可能崩溃。笔试里把这个区别讲清楚比单纯背属性修饰符能拿更多分。2.4 RunLoop与事件响应链直播类App非常依赖RunLoop所以B卷考RunLoop一点也不意外。最经典的问题NSTimer在UIScrollView拖动时为什么不准答案是因为RunLoop在滑动时会切换到UITrackingRunLoopMode默认的NSTimer只被加入了NSDefaultRunLoopMode滑动时就不会执行。解决办法是把timer加到NSRunLoopCommonModes这样它就能同时在默认模式和追踪模式下生效。事件响应链也是常考的从hitTest:withEvent:开始找到最合适的view然后沿着nextResponder链向上传递直到UIApplication。题目可能问某个button的父view设置了userInteractionEnabledfalse点击button会发生什么答案是hitTest直接跳过这个父view事件不会传到button。这类题其实是在考察你对UI事件机制的理解而不只是背一个流程图。3. 多线程、网络与架构设计从“会调用API”到“会设计策略”3.1 多线程方案对比GCD、NSOperation、锁简答题里经常问“你一般用GCD还是NSOperation为什么”。标准答法是GCD更轻量适合一次性任务NSOperation能设置依赖关系、取消操作、控制最大并发数适合复杂任务队列。但如果你只答到这里说服力不够。B卷里有过一道题给出一段在主队列执行dispatch_sync的代码问会不会死锁。答案是会死锁因为主线程被当前任务占用同步提交的新任务又在等主线程执行互相等待。代码示例dispatch_async(dispatch_get_main_queue(), ^{ dispatch_sync(dispatch_get_main_queue(), ^{ // 这里不会执行主线程已经卡死 }); });锁方面可能会问有哪些常用的锁以及各自的性能和坑。OSSpinLock由于存在优先级反转问题已经不建议使用OSSpinLock的替代方案可以是pthread_mutex、os_unfair_lock或者是GCD的信号量。笔试时不一定要求写出具体读法但至少要能说出“自旋锁忙等、互斥锁休眠”这个核心区别以及为什么直播类应用里不适合长时间自旋。3.2 网络层从第三方封装到自研策略欢聚时代当时做了不少音视频业务网络层是重点。B卷可能不会让你写完整的网络请求代码但会问“如何设计一个网络请求框架”。这个问题看似开放实际考察的是你对整个请求生命周期的把控。要提到基于NSURLSession封装把请求配置、响应解析、错误处理和缓存策略分开。关键点包括请求池管理多个相同请求只发一次超时设置合理区分连接超时和读取超时失败重试需要设定最大重试次数并配合指数退避避免雪崩。HTTP和TCP/UDP的区别也是高频题。这里不要只背“TCP可靠、UDP不可靠”要结合直播场景说明为什么实时音视频会优先选用UDP或基于UDP的协议因为TCP的拥塞控制和重传机制会导致延迟升高。笔试中如果能提到“弱网下的音频视频平滑策略”会明显增加印象分。3.3 组件化与架构分层从MVC到MVVM校招笔试不太可能让你手写一套完整架构但会通过简答题让你“说说你理解中的MVC各角色的职责”或者“如何解决ViewController过于臃肿的问题”。我当时的回答是ViewController既承担视图生命周期管理又承担业务逻辑很容易越写越大。可以拆出ViewModel层把网络请求、数据加工、状态管理都放到ViewModel里View只负责展示和事件上报。后来再看这份B卷面试官真正想看到的是你对“数据流”的理解而不是一定要求选择某个架构。比如你用MVVM要能说清楚View和ViewModel之间的绑定机制你用MVP要能说清楚Presenter如何驱动View更新。只要能自圆其说并且能指出每种架构的适用场景这道题就稳了。3.4 性能优化与内存泄漏排查性能优化题通常是给一个卡顿场景问你怎么排查。标准流程先复现路径再查看FPS并确认卡顿范围然后用Instruments的Time Profiler定位耗时方法。很多人一上来就打开Xcode跑工具但如果没有一个可复现的操作路径工具抓出来的数据其实很随机。另外还要注意主线程是否有大量IO操作比如在tableView:cellForRowAtIndexPath里写文件、解码图片这些都会引发卡顿。内存泄漏方面除了用Instruments的Leaks也可以通过Xcode的Memory Graph来检查循环引用。如果发现内存持续上涨先看是否有timer、block、通知监听等对象没有释放。B卷里这种题不算难但需要你了解基本的排查工具。笔试时如果问“你怎么做内存优化”不要只会说“用Instruments查Leaks”要把从怀疑到验证的完整思路讲出来。4. 编程题实战思路不背题但要有框架4.1 字符串与数组类题目B卷编程题里出现过字符串反转之类的基础题。这类题考的是代码基本功但iOS岗的编程题往往会有隐藏要求比如“不使用系统库函数”。我印象里有道题和字符反转差不多难点在于处理Emoji等复杂字符如果直接按UTF-16的length来遍历会把一个Emoji拆成两个字符导致结果异常。正确思路是使用NSString的rangeOfComposedCharacterSequencesAtIndex或者enumerateSubstringsInRange按字符簇来遍历。这里给出一个反序拼接的思路- (NSString *)reverseString:(NSString *)input { NSMutableString *result [NSMutableString string]; [input enumerateSubstringsInRange:NSMakeRange(0, input.length) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) { [result insertString:substring atIndex:0]; }]; return result; }笔试时能写出这个逻辑说明你注意到Unicode组合字符的坑。如果只是用C语言的char数组反转虽然也能跑但放到iOS场景里就显得粗糙了。4.2 链表与二叉树校招笔试喜欢出链表反转、二叉树层序遍历这类题因为能快速判断代码功底。链表反转有递归和迭代两种写法递归虽然简洁但面试官经常会让你再写一遍迭代。二叉树层序遍历用队列实现BFS的思路很直接。需要注意的是笔试题要求手写代码很多人会在方法命名和缩进上丢分。我见过不少人在纸上写代码逻辑是对的但变量名全是a、b、c阅卷人看起来非常吃力。做题的另一个技巧是先在草稿纸上画一下输入输出尤其是边界条件比如空链表、单节点链表、只有右子树的二叉树。笔试时写代码不一定要所有边界都覆盖但至少要把核心判断写出来。很多时候面试官看的是你的解题思路是否清晰而不是要求一次AC。4.3 场景设计题图片缓存库这道题我认为是B卷最亮眼的部分。题目类似“如果要为App设计一个图片缓存库你会怎么做”。这已经接近实际项目了。我当时回答是分成三层内存缓存、磁盘缓存、网络下载。内存缓存用NSCache磁盘缓存用文件系统网络层用NSURLSession。要强调缓存淘汰策略内存缓存可以按照成本来限制磁盘缓存可以按文件大小和最后访问时间清理。再往深处需要提到底层的线程安全。NSCache本来就是线程安全的但磁盘缓存的读写操作仍然需要控制并发可以用一个串行队列来保证同一时间只有一个文件读写。另外还要考虑请求合并如果同一张图片同时被多个地方请求应该只发起一次下载回调分发到所有请求方。这些设计思路比某一个具体API更值钱面试官能从中看出你是否真的写过网络框架。5. 答题时的典型错误与复盘发现5.1 代码规范方面的失分点有几个非常可惜的失分点不写分号、变量命名用a2、方法名不表达语义。笔试题量不大阅卷人看代码时这些会很影响印象分。尤其是iOS开发命名习惯几乎是第一印象。就算算法逻辑是对的方法名写成handleClick而不是handleLoginButtonClick也会被扣一点印象分。另外一个容易被忽视的规范是如果代码用了系统框架最好在注释里说明版本适配。比如“iOS 10以上可用os_unfair_lockiOS 9及以下需要走pthread_mutex”这类信息写上去会显得你平时有关注兼容性。虽然校招笔试不一定要求你考虑那么多但主动写出来就是加分项。5.2 概念题的回答结构很多人在简答题上只给结论不给推导过程。比如“为什么weak修饰的属性不会导致循环引用”不能只回答“weak不增加引用计数”。更好的回答是weak变量会注册到SideTable当对象释放时会通过弱引用表把指向这个对象的weak指针全部置为nil从而避免悬垂指针。这个回答包含了机制和结果逻辑更完整。答题时可以顺着“是什么、为什么、怎么用”来组织。哪怕考场上一时想不起特别底层的内容也要把已知的步骤分条写出来。校招笔面试官普遍认为能分步骤解释原理的人比单纯背结论的人更有培养价值。我后来参加面试时也会特意观察候选人能不能把“内存管理”这个问题讲出层次感。5.3 常见问题排查技巧速查表这里整理一份实用的排查速查表覆盖笔试和日常开发都常见的现象。笔试阶段你也可以把这些现象当成考点来回顾。现象可能原因排查思路NSTimer不回调timer被加到DefaultMode滑动时mode切换改成CommonModes或使用block API界面滑动卡顿主线程有耗时操作或离屏渲染用Time Profiler定位检查圆角阴影内存持续上涨NSTimer/block造成循环引用用Leaks和Memory Graph检查block里self报警捕获self并形成循环引用使用weak-strong dance网络请求不回调URL或参数没有正确编码检查query的百分号编码和超时设置这个表格不是标准答案但能帮你快速定位问题方向。笔试时如果考到“线上App卡顿如何排查”把表格里的点展开说就已经覆盖了主要得分点。5.4 笔试和面试的衔接笔试结束后通常很快就安排面试面试官可能还会拿着你的笔试卷子追问。所以交卷前最好把拿不准的题目在草稿纸上记一下。我当时养成的习惯是笔试结束后立刻把每道题回忆出来标上“确定对”“可能错”“完全不会”当天就去搜答案。因为在面同一家公司时这份卷子上的题往往就是面试题的线索。如果你是在校生建议提前用真实机型跑一跑自己写的代码不要只在模拟器里点一点。2017年那会儿模拟器和真机在内存警告、定位权限、相机调用上的表现差异已经非常明显现在也一样。笔试和面试考的是你有没有“遇到过问题并且解决过问题”的体感而不是单纯的知识储备。最后再分享一个小技巧。笔试前一周不要刷偏题怪题把iOS开发里那些“自认为最熟悉”的知识点重新问自己一遍比如“为什么block用copy”“NSNotificationCenter在哪个线程回调”。很多面试官不会刻意出难题但会在你答得飘的时候追问底层。欢聚时代这份B卷放在今天看依然不过时因为它考的是iOS工程师的通用底线能力。如果你能把上面这些考点都吃透再去投其他一线互联网公司的校招也会从容很多。
返回列表