尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

爱奇艺iOS校招笔试全复盘:核心考点、解题思路与备战策略

爱奇艺iOS校招笔试全复盘:核心考点、解题思路与备战策略 开篇一次校招笔试为什么值得专门复盘先交代一下背景。2018年秋季爱奇艺启动了当年的校招iOS工程师岗位分为多场进行第三场笔试是面向投递较晚、或者第一批笔试没赶上的同学。我当年完整参加了这场笔试后来也带着这份经验带过不少学弟学妹准备校招所以想把这套题背后的考察逻辑、每类考点的准备方式、以及我在考场上踩过的坑完整整理出来。先说说这篇文章适合谁看。如果你是正在准备iOS校招、或者准备跳槽大厂iOS岗位的工程师这篇文章能帮你理清爱奇艺这类视频业务大厂到底想考什么。如果你只是刚学iOS不久还没到面试阶段也可以把这份复盘当作学习路线图——笔试中反复出现的知识点就是你接下来半年最值得投入精力去啃的内容。有意思的是距离2018年已经过去好几年但把当年的笔试题拿出来看核心考点并没有过时。内存管理、多线程、网络请求、UI布局、基础算法这些到今天依然是iOS面试的高频区。变化的只是技术的具体形态——比如现在面试更爱问Swift Concurrency当年问的是GCD和OperationQueue现在都在聊SwiftUI当年还在死磕Auto Layout和frame布局。但底层的计算机基础和工程思维始终没有变过。所以这篇复盘我尽量只讲那些换一身外壳依然会考的东西。1. 整体考察思路与备考策略1.1 爱奇艺校招笔试想筛选什么样的人先说结论这批题目不是单纯考你背了多少iOS API而是在考你有没有软件工程师的基本素养。整个笔试分为两个大部分——一部分是客观题和基础编程题一部分是iOS相关的专题。客观题覆盖了数据结构、计算机网络、操作系统、Objective-C/Swift基础专题部分涉及iOS特有机制比如RunLoop、内存管理、多线程、界面布局、网络层封装。这和很多公司的校招笔试结构相似但爱奇艺的题目有个明显特点偏向实际业务场景而不是纯粹的理论默写。举例来说它不会直接问你什么是引用计数而是给你一段有循环引用的代码让你找出内存泄漏发生在哪一行不会直接问什么是死锁而是给你三个并发任务让你判断这个调度方式会不会卡死主线程。这种套路明显是希望招进来的人能直接上手写业务代码而不是还要先教一遍工程习惯。另外很关键的一点是爱奇艺是视频业务公司播放页、首页信息流、弹幕、评论这些场景在题目中反复出现。如果你有视频类App的开发经验或者至少想过一个视频列表页应该怎么做内存管理在审题的时候会天然有优势。1.2 覆盖range与时间分配两个小时怎么拿分我记得那场笔试的总时长是120分钟题量不大但每道题都有一定的深度。最忌讳的做法是平均分配时间——基础选择题每道花三五分钟结果后面的大题只剩二十分钟写到一半就被迫提交。我当时的时间分配策略是选择题和填空题控制在40分钟内完成中间的主观题比如让你补全代码、解释输出结果控制在30分钟最后的综合设计题和编程题留下至少50分钟。综合题通常分值最高而且它的评分是按思路分步给的即使最后没跑通只要思路清晰、代码结构完整也能拿到60%到70%的分数。如果前面拖太久最后的大题直接空着那基本就没希望了。现在回看这个策略仍然适用于绝大多数公司的笔试不管是iOS岗还是其他开发岗。笔试的分数密度不是均匀分布的最后的核心大题才是拉分的关键前面的题保证正确率即可不需要做到完美。1.3 别把宝押在刷原题上我知道很多人在笔试前会去找爱奇艺iOS校招原题来背。但实际经历过就知道除非是特别近期的题目否则很难遇到完全一样的原题。大厂的题库通常是在一个大的题目池里随机抽取组合你背到的可能是某道题的一个变体选项顺序变了、数值变了、甚至考察角度直接从让你写出代码变成让你指出代码哪里有问题。更有效的准备方式是把知识点本身的原理吃透。比如你理解了autoreleasepool在什么时候释放对象的原理无论题目以什么样的姿势去包装它——是面试题、是选择题还是代码补全——你都能识别出来。这也是我这篇复盘这么强调为什么的原因。2. 核心考点拆解与解题思路2.1 内存管理以ARC为背景的循环引用分析爱奇艺笔试对内存管理的考察几乎可以确定是绕不开的。考察的角度通常不是直接问什么是循环引用而是给你一个对象关系图让你判断某个对象是否被正确释放或者给你一段闭包代码让你指出闭包里捕获了哪些变量会不会造成循环引用。那时候iOS已经全面进入ARC时代所以题目不会让你手动管理retain和release但你需要理解ARC到底做了什么。ARC本质上是编译期插入retain/release调用的机制它只能在编译期确定对象生命周期。遇到闭包、delegate、NSTimer这些场景编译器无法自动推断就可能会出现循环引用。举一个当年考过的类似场景一个ViewController持有UITableViewUITableView的delegate和dataSource都指向这个ViewController。与此同时这个ViewController在viewDidLoad里创建了一个闭包并且把它保存到自己的一个属性里闭包内部又用到了self。问这个ViewController会不会被正确释放。答案是如果闭包属性是强引用默认的strong闭包捕获的self也是强引用那么就会形成 self - closure - self 这一条循环引用链导致dealloc永远不会被调用。解决办法是在闭包里使用[weak self]捕获列表或者在外面用weak var weakSelf self。这类题看似简单但特别容易在细节上丢分。比如很多人知道闭包要用weak但当闭包是DispatchQueue的block时如果没有被self持有其实不会循环引用。笔试中需要仔细判断闭包是否被self间接持有而不能看到闭包就用weak。2.2 多线程与并发同步/异步、队列、死锁多线程是另一个重量级考点。2018年的时候Swift 4已经发布了但大部分iOS项目的核心代码还是Objective-C与Swift混编。所以笔试题通常会同时考察两个层面的内容一是GCD、NSOperationQueue的使用二是并发模型背后的执行顺序判断。常见的出题方式是给你一段代码里面有DispatchQueue.main.async、DispatchQueue.global().async、以及一个信号量或者DispatchGroup让你判断最后的输出顺序。这类题目表面看是考API实际上在考察你是否真正理解同步/异步和串行/并发这两组维度的组合。很多没准备好的同学一看到dispatch就会晕。这里我分享一个我当时总结的心法先判断执行方法sync还是async再判断队列类型串行还是并发然后逐步推理不要凭感觉作答。只要严格按这个顺序去分析大部分GCD题目都是纸老虎。还要注意另一个高频考点——死锁。经典题目是在主队列里调用DispatchQueue.main.sync会发生什么答案是因为主队列是串行队列当前block正在主队列上执行sync又向主队列提交了一个新的block而当前block必须等sync返回新的block又必须等当前block结束两个任务互相等待于是死锁。笔试里这个场景经常被包装成用户点击按钮后执行某个操作的形式出现考察你是不是真的明白sync对串行队列的阻塞行为。2.3 网络层从HTTP到TCP的层层追问视频类公司对网络层的考察一定会比普通App公司更深。我当时遇到的不只是GET和POST的区别而是从HTTP请求的完整生命周期开始问起DNS解析过程、TCP三次握手、TLS握手、HTTP头字段的作用、HTTP/2的新特性、断点续传的实现原理。其中有一个考察方向让我印象很深如何设计一个支持断点续传的视频下载模块。这道题涉及到HTTP Range头字段、文件IO、内存管理、以及任务状态机的设计。即使放在今天这也是一个很优秀的综合题因为它同时考察了网络协议理解和客户端工程能力。我当时的思路是这样下载模块维护一个任务队列每个下载任务有三个状态——等待中、下载中、已完成。下载开始时先向服务器发送HEAD请求或者带Range的GET请求从响应头里拿到文件总大小。然后根据一定的分片大小比如1MB把文件切分成多个range每个range发起独立的下载请求写入文件时通过NSFileHandle的seekToFileOffset定位到对应的偏移量去写入。所有分片完成后验证文件完整性再把任务状态置为已完成。这个方案当然有很多可优化的细节比如断点续传时如何记录已下载的分片信息、下载过程中如何应对网络切换。但在笔试中只要你能把这个框架搭建出来再补上几个关键边界条件的处理就已经能拿到很好的分数了。2.4 UI与布局从frame到Auto LayoutUI布局在爱奇艺笔试中占的比重也不小。2018年的时候Auto Layout已经成为主流但笔试对这种纯代码写布局的题目仍然很感兴趣。常见出题方式有两种一种给你一段手动计算frame的代码让你指出在什么情况下会出问题另一种给你一个目标布局的示意图让你用Auto Layout实现出来。第一种考察的是对不同屏幕尺寸的适配意识。手动计算frame时如果硬编码了某个宽度和高度在iPhone SE和iPhone Xs Max上就会出现不同的表现。正确做法是让frame基于superview的bounds去计算或者直接用Auto Layout。笔试中很多同学会忽略一点即使你用Auto Layout也需要注意约束的优先级和固有内容大小否则在内容长度变化时一样会出问题。我还记得有一道题和UIStackView有关。2018年正好是UIStackView开始普及的阶段爱奇艺的题目很赶时髦让考生说出UIStackView的几种distribution的区别——fill、fillEqually、fillProportionally、equalSpacing、equalCentering。如果只是用过UIStackView但没深入理解过很容易把fillEqually和fillProportionally搞混。简单来说fillEqually是让每个子视图获得相同尺寸fillProportionally是根据子视图的固有内容大小按比例分配两者在子视图内容不一致时的表现完全不同。2.5 算法与数据结构视频列表场景下的实战题算法题是筛选门槛但爱奇艺的算法题不会很偏主要是链表、二叉树、字符串处理、动态规划这类经典题型。和纯算法岗不同iOS岗的算法题经常会套一层业务壳。比如给定一个视频列表每个视频有开始时间和结束时间如何计算最大同时播放数这本质上是一道扫描线算法题再比如实现一个LRU缓存用于图片加载这本来就是iOS开发中的高频场景。我印象最深的是一道和字符串相关的题目大概意思是给定一个字符串找出最长的不含重复字符的子串长度。这道题在LeetCode上属于中等难度但如果在笔试的iOS试卷里出现很多人会愣一下——因为刷题时一般不会把这道题和iOS关联起来。但如果仔细想在视频搜索、弹幕关键词过滤这些场景中字符串处理本来就很常见。算法题考的不是你背了多少题解而是你有没有在业务代码中锻炼过思维。准备这类题目我的建议是不要把精力花在那些偏难怪的算法上而是把经典的数据结构操作写熟。链表反转、二叉树遍历、快排、二分查找、动态规划的经典模型这些写熟之后无论算法题怎么变体你至少能写出一个可运行的版本而不会在考场上卡死。3. 实操过程与核心环节实现3.1 用代码演示循环引用的检测与修复笔试中经常要求你直接写代码来证明你理解了循环引用。最好的做法是写一个可以运行的小demo同时在注释里标注出修复的关键点。我整理了一个典型的示例你们可以当模板来用。class VideoPlayer { var title: String var onPlayFinished: (() - Void)? init(title: String) { self.title title } func startPlay() { // 模拟播放结束后回调 DispatchQueue.global().asyncAfter(deadline: .now() 1.0) { [weak self] in guard let self self else { return } self.onPlayFinished?() } } deinit { print(\(title) deallocated) } } class PlayerManager { var currentPlayer: VideoPlayer? func setup() { let player VideoPlayer(title: 爱奇艺独家) // 关键点闭包内部使用 [weak self]避免 self - player - closure - self 的循环引用 player.onPlayFinished { [weak self] in self?.handlePlayFinished() } currentPlayer player player.startPlay() } func handlePlayFinished() { print(播放完成更新UI) } }在笔试时我建议在关键代码行旁边用注释写出为什么这里要weak以及如果不weak会发生什么循环引用链。一方面方便阅卷人快速看到你的思路另一方面也是提醒自己不要漏掉关键步骤。这道题除了考察weak之外顺带考察了DispatchQueue.global().asyncAfter的用法一箭双雕。3.2 用代码演示GCD 输出顺序判断的标准分析流程有一类经典题是给一段代码问你输出顺序我直接把一个高频示例贴在这里let queue DispatchQueue(label: com.example.serial) queue.async { print(1) queue.sync { print(2) } print(3) } print(4)很多同学第一次看到这段代码第一反应是输出1 2 3 4。但真实结果是queue是一个串行队列外层async代码块运行在queue上当执行到内部queue.sync时它向同一个串行队列提交了一个新任务而当前任务必须等新任务执行完才能继续新任务又必须等当前任务执行完——和主队列的main.sync死锁是一样的道理所以程序会在执行到queue.sync时直接崩溃或者卡死。分析这类题目时我强烈建议你们严格按照队列类型 执行方法这个顺序来推理不要凭直觉。第一看看这段代码是在哪个队列上执行第二看看调用sync/async的队列是不是同一个第三判断是否有等待关系。只要这三点理清了就不会出错。3.3 手工推演断点续传下载模块的设计这是当年综合题里最接近实际业务的一道我详细说说我在笔试时的解答结构。首先是需求分析用户观看视频时可能中途退出再次点击观看时希望从上次的位置继续播放而不需要重新请求整个文件。这背后需要支持HTTP Range请求也就是在HTTP请求头里带上Range: bytesstart-end服务器返回206 Partial Content并携带Content-Range响应头。然后是客户端设计我分了几个模块任务管理模块维护一个下载任务的集合每个任务有唯一标识比如视频ID任务状态包括waiting、downloading、paused、finished、failed。这个模块的核心是状态转换的合法性判断防止从failed直接跳到finished这种非法状态。分片下载模块将文件按照固定大小比如1MB分成多个分片每个分片发起独立的HTTP请求。每个分片下载完成后将数据写入文件对应的偏移位置。使用NSFileHandle的seekToFileOffset来定位写入位置避免用NSData拼接整个文件减少内存占用。进度记录模块每完成一个分片就把分片的完成信息写入本地plist或数据库。这样即使App被杀死下次启动时也能从断点恢复。记录的信息包括文件总大小、已完成的分片序号集合、文件在沙盒中的路径。这个设计放到今天的面试中依然能打因为它体现了一个iOS工程师对整个下载链路的理解。笔试中不用写出完整代码但画出模块图、给出关键代码段、说清楚状态机设计就已经很出彩了。3.4 工程习惯笔试代码里也要体现规范很多同学觉得笔试代码只要能跑就行其他不重要。但实际上笔试代码的规范性会影响阅卷人对你的整体印象尤其是在大厂校招这种简历多、需要快速筛选的场景下。我见过不少代码虽然能通过测试用例但函数命名随意、魔法数字到处都是、没有任何注释这类答案在分步给分时很吃亏。我建议在笔试时养成三个习惯第一用有意义的命名比如downloadVideoTaskWithID:而不是func a()第二把关键的边界条件和设计意图写成注释哪怕只是两三行第三尽量把逻辑拆成独立的小函数保持每个函数的长度在20行以内。这三个习惯在面试手写代码环节也同样重要。4. 常见问题与排查技巧实录4.1 笔试环境与工具链先解决跑不起来的问题当年的笔试是在线OJ系统完成的支持C、C、Java、Objective-C等语言但Swift的支持还不够完善。很多同学习惯用Xcode写代码结果到了OJ环境发现没有自动补全、没有语法高亮、编译器版本也和本地不一样一下子就不适应了。我先说一个通用的应对思路提前去目标公司的笔试平台做模拟题。绝大多数校招笔试平台都提供往年的模拟题或者练习入口花半小时熟悉一下代码编辑器的操作方式能避免考场上浪费大量时间在怎么把代码粘贴进去System.out.println应该怎么写这种低级问题上。另外建议答算法题时优先选择熟悉的语言。如果你平时iOS开发用Objective-C但笔试OJ对Swift支持不好可以尝试熟悉一下C或Java的语法因为大多数OJ对这两门语言的支持最完善。不要临时换语言硬写很容易在语法细节上翻车。4.2 选择题里的陷阱别被细节带偏客观题是拿分的基础但选择题的出题人经常会故意设置一些看似正确、实则错误的选项。比如遇到API相关的题目两个选项只差一个关键词比如copy和strong、atomic和nonatomic、lazy和assign如果不熟悉的话真的很容易选错。我的建议是遇到不确定的题先用排除法排除明显错误的选项再对比剩余选项之间的差异点。如果是在内存管理类题目中把每个选项背后的引用计数变化画出来比凭感觉选要可靠得多。画图虽然耗时但在内存管理和多线程题目中画图往往是一步到位的最快方式。4.3 编译不过怎么办第一个要查的不是语法笔试中经常出现的情况是时间只剩20分钟代码写完但编译不过提示一个看不懂的错误。这时候很多人的第一反应是盯着错误信息看半天改来改去还是不对。我总结了一个更高效的排查顺序第一检查括号是否匹配。手写代码最常见的错误就是多了一个右括号或者少了一个右括号尤其是嵌套较多的方法调用时。第二检查是不是少了import或头文件引用。OJ环境不会像Xcode那样自动帮你导入框架忘记#import UIKit、import Foundation这种低级错误相当常见。第三检查变量名是否拼写一致。手写代码时变量名前后不一致的坑比想象中更频繁。如果以上三项都排查完还是编译不过直接放弃这道题把时间留给后面的题目。笔试是限时赛纠结在一道题上后面的大题可能一分都拿不到。这一点我特别要提醒追求完美的同学——拿到70%的分数远比追求100%却做不完要强得多。4.4 崩溃和超时在线OJ的两种典型反馈在线OJ的反馈一般有两种运行崩溃和超时。运行崩溃通常意味着代码在某个输入下访问了非法内存比如数组越界、空指针调用、重复释放。超时则说明算法时间复杂度过高。针对这两种反馈最有效的做法是在本地先用Xcode调试一遍。把OJ给出的测试用例拿过来在Xcode中单步执行观察哪一行出了问题。即使没有Xcode环境也可以在代码里加一些打印语句输出关键变量的值定位问题区间。遇到超时问题优先检查是不是用了递归但没有终止条件、或者是不是有死循环。如果代码逻辑没问题那就要考虑优化算法比如用哈希表替代线性查找、用缓存记录中间结果。爱奇艺的视频业务中列表页肯定会涉及大量数据的排序和过滤这一块的算法优化能力笔试中也会间接考察。4.5 答题顺序与心态管理两个小时的真实节奏最后聊一个看起来和技术无关、但实际影响很大的话题——答题节奏。很多同学一上来就看到最后的大题很难心里一慌开始从难题做起结果前面的基础题也来不及做了。我的经验是先通读一遍所有题目标记出每道题的预估难度和分值然后从自己最有把握的题开始做。这能确保你先把能拿到的分数拿到手。遇到一道卡了超过10分钟的题先空着往下做后面如果有时间再回来补。心态上始终提醒自己校招笔试不是要考满分而是要在所有人当中排到前面。稳定发挥、拿满基础分你的排名通常就已经很可观了。最后分享一个我自己的小习惯现在回头看这场笔试最让我受益的不是背下了哪道题而是在准备过程中养成的一个习惯每学一个iOS知识点都会问自己“这个机制如果出成笔试题会以什么形式考我”。把RunLoop、ARC、GCD、Auto Layout这些知识从“会用”变成“能讲清楚”再用笔试题来验证自己的理解程度这套方法在后来的面试里也帮了大忙。如果你正在准备iOS校招不妨也试试这个思路——找一两套大厂的真题不看答案先自己计时做一遍然后把做错的题对应的知识点找出来回归源码和官方文档去弄懂原理。这个过程虽然慢但对基础能力的提升是最扎实的。祝你们笔试顺利。
返回列表