
2018年秋季校招爱奇艺的iOS工程师笔试分了好几场进行。我当年投的是客户端方向做到第二场的时候明显感觉到这套题不是靠背几个API就能应付的——它把“会写iOS代码的人”和“真正在iOS工程里待过的人”筛选得很干脆。整张卷子覆盖了从语言基础、内存管理、多线程、布局适配到网络调试、签名上架的全链路很多题看起来是选择或简答实际上考的是你有没有在真机上跑过、有没有被线上崩溃折磨过、有没有认真想过一个视频类App在弱网和复杂机型下是怎么活下来的。这篇文章把这场笔试背后涉及的考点和备考逻辑完整拆一遍。不管你明年想投哪家公司的iOS岗这套知识体系都可以直接拿来自查因为校招题的底层逻辑基本一致——基础扎实、体系完整、有工程意识的人分数一定不会低。1. 一场笔试把“能写代码”和“会用 iOS”分开了1.1 第二场笔试的定位技术深度筛选很多公司校招会设定多轮笔试第一场往往是基础行测或通用能力筛选到了第二场才真正进入技术笔试。爱奇艺的这场也不例外。从岗位需求来看iOS工程师要面对的是千万级日活的视频App这意味着技术考察不可能停留在“你用没用过UITableView”这种层面而是会追着“你用UITableView的时候有没有想过复用、离屏渲染、滑动卡顿”往下问。第二场笔试的题量适中但每道题的信息密度都很高。选择题会故意放几个模糊选项简答题则要求你写出“为什么”而不只是“是什么”。这种出题风格和LeetCode那种纯算法题完全不同它更接近实际开发中的场景判断——给你一段代码问你有没有问题给你一个线上问题问你从哪开始排查。1.2 爱奇艺这类视频App对iOS工程师的独特要求视频类App和普通工具类App对iOS工程师的要求有很大区别。第一是播放器相关技术栈播放器内核、视频渲染、音画同步都属于底层能力笔试会通过内存和多线程题目来间接考察——因为播放器是内存和线程消耗大户。第二是网络环境复杂秒播率、卡顿率、弱网策略都是核心指标所以网络层相关题目必然出现。第三是性能优化视频App的包体积、启动时间、发热耗电直接决定用户留存这类题目会从内存、布局、线程等多个角度交叉出题。理解了这个背景你再看那些校招题就不会觉得它们零散了。每个考点背后都对应着真实业务场景中的某一个痛点面试官想招的不是“能跑通Demo的人”而是“能解决线上问题的人”。1.3 一次笔试完整的备考范围怎么划综合来看iOS校招笔试的知识地图可以画成四层。最底层是语言基础Objective-C和Swift至少要掌握一种并且理解两者差异。第二层是系统机制内存管理、多线程、RunLoop是重点。第三层是UI与工程实践Auto Layout、UITableView优化、组件化、包管理工具都属于这个范畴。第四层是上线与运维能力证书签名、上架流程、崩溃分析、性能监控这些看似偏运营的考点也会出现在笔试里。考察层级核心内容典型题目方向语言基础OC属性修饰符、Block、KVC/KVO、Swift值类型代码纠错、输出结果系统机制ARC、循环引用、GCD、RunLoop给一段代码分析内存/线程问题UI与工程Auto Layout、UITableView、UIStackView适配方案、性能优化上线运维证书、描述文件、上架流程、崩溃分析流程类题目、场景排查这张表基本上就是我备考时的自查清单。每一项不敢说精通但至少保证提到任何一点都能说上三句话并且能举出实际工程中的例子。2. OC 和 Swift 混编时代语言基础题到底在筛什么2.1 2018年的iOS语言生态2018年正是Objective-C存量巨大、Swift快速普及的年份。爱奇艺这类大厂的App核心代码基本还是OC为主但新业务模块已经开始用Swift尝试。笔试对语言的考察就很有意思——它不偏向任何一方而是考“你清不清楚两种语言的区别”。比如一道典型的选择题NSString和Swift.String有什么区别如果只背过“一个是类一个是结构体”就太浅了。深入一点NSString是引用类型赋值只是指针拷贝Swift.String是值类型赋值会发生内容拷贝再深入一点Swift的String是可变长度的字符集合内部做了COWCopy-on-Write优化只有在需要修改时才会真正拷贝内存。这种题目考察的是你有没有在真实工程中注意过值类型在数组、字典中传递时的性能影响。2.2 属性修饰符校招笔试的常青树OC的属性修饰符几乎是必考题我不止一次在校招卷里看到类似这样的代码property (nonatomic, copy) NSString *name; property (nonatomic, strong) NSArray *tags; property (nonatomic, weak) idSomeDelegate delegate; property (nonatomic, assign) NSInteger count;题面会问为什么name用copy而不是strong为什么delegate用weak为什么count用assign这个题目背后的逻辑其实是考察你有没有踩过可变对象的坑。NSString用copy是因为调用方传入的可能是NSMutableString。如果属性内部只做strong持有外部把可变字符串改了属性值也会跟着变这是一个隐蔽且难查的Bug。用copy会把可变对象拷贝成不可变对象切断外部修改的影响。2.3 KVC、KVO 和消息发送机制OC的动态性也是高频考点。KVO的使用要回答几个关键问题KVO基于什么实现答案是isa-swizzling系统动态生成一个子类并重写被监听的setter方法。KVC的查找顺序是什么先找setKey:/_setKey:方法再找_key/_isKey等成员变量最后调用setValue:forUndefinedKey:报错。这些题目单独看都不难但放在一起考就是在考察你是否对OC的运行时机制有整体理解。面试官希望听到你把这些机制串起来讲OC是一门动态语言方法的调用本质是消息发送所以才有objc_msgSend、才有方法交换Method Swizzling、才有KVO的isa-swizzling实现。如果你只是背答案一追问就会露馅。3. 内存管理与循环引用ARC 不是免死金牌3.1 ARC 解决的是什么问题ARCAutomatic Reference Counting是iOS开发绕不开的机制。很多同学以为有了ARC就不需要管内存了这是最大的误解。ARC只是在编译期帮你插入retain/release调用它解决不了循环引用——因为循环引用会导致引用计数永远不为0对象无法释放。笔试里最常见的场景就是Block循环引用。比如这样一个网络请求回调self.manager [[NetworkManager alloc] init]; [self.manager fetchDataWithCompletion:^(NSData *data) { self.result data; }];self持有managermanager持有completionBlockcompletionBlock又捕获了self三者形成引用环。ARC不知道这个环没人需要所以它不会帮你打破。3.2 weak-strong dance 的标准写法正确的解法是打破环中的一条引用链。最常见的做法是在Block外声明__weak引用在Block内再转回__strong使用__weak typeof(self) weakSelf self; [self.manager fetchDataWithCompletion:^(NSData *data) { __strong typeof(weakSelf) strongSelf weakSelf; if (!strongSelf) { return; } strongSelf.result data; }];为什么要在Block里再定义一个strongSelf因为weakSelf是弱引用在Block执行期间self可能已经被释放weakSelf会自动变成nil。如果直接用weakSelf访问属性会在中间某一行变成nil导致逻辑中断。先转成strongSelf保证Block执行期间对象不会被释放这是一个非常实用的技巧。在Swift里对应的写法是用捕获列表NetworkManager.shared.fetchData { [weak self] data in guard let self self else { return } self.result data }3.3 定时器是循环引用的重灾区除了BlockNSTimer也是循环引用的高发区。NSTimer被加入主RunLoop时RunLoop会强持有timertimer会强持有target也就是self而self又持有timer形成self - timer - self的环dealloc永远不会执行。解决方案有几种。iOS 10之前标准的做法是引入一个中间对象或者用__weak加Block封装。iOS 10之后系统提供了block版本的定时器API可以直接用__weak捕获self来解决。另外一个建议是在viewWillDisappear或适当的时机主动invalidate定时器不要依赖dealloc来释放因为dealloc很可能永远不触发。笔试里如果遇到“这个对象为什么没有被释放”的题先往循环引用方向想。释放不掉的深层原因一定是有环不管这个环是通过Block、Timer还是Delegate形成的。3.4 线上内存问题的排查工具这部分是我自己的经验。真正到了工程里不可能靠眼睛看代码来找内存泄漏必须用工具。Instruments的Leaks模板能够检测循环引用导致的内存泄漏MLeaksFinder能够在开发阶段快速定位UIViewController和UIView的泄漏。面试时能说出这些工具的使用场景会显得你确实在实践里处理过内存问题而不是只会背概念。4. 多线程题从 GCD 队列模型到线上死锁诊断4.1 队列模型别把线程和队列混为一谈多线程是iOS笔试的必考项而GCD是在iOS中使用最广泛的并发方案。很多初学者搞混的一个概念是队列不等于线程。队列是任务的组织结构线程是任务的执行者。串行队列的任务按顺序执行并行队列的任务可以同时执行但并行队列并不保证一定会开多个线程系统会根据CPU核心数和任务负载动态决定线程数量。GCD的核心创建方式就两个dispatch_queue_create和系统提供的全局并发队列。系统全局队列分五个服务质量等级userInteractive、userInitiated、default、utility、background。视频App里UI刷新和用户交互事件需要放到userInteractive视频预加载、数据解析放到userInitiated日志上报、缓存清理放到background。4.2 死锁是怎么来的笔试里有一道经典题在主队列上同步执行一个任务会发生什么dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(这里不会执行); });答案死锁。因为主队列是串行队列当前正在执行的代码就在主队列上dispatch_sync会阻塞主线程等待新任务执行而新任务排在主队列末尾要等当前任务结束才能执行——当前任务又等在dispatch_sync返回形成互相等待。还有一道稍微进阶一点的在一个自定义串行队列里异步执行一个任务任务内部再对这个队列做同步派发同样会死锁dispatch_queue_t serialQueue dispatch_queue_create(com.example.serial, DISPATCH_QUEUE_SERIAL); dispatch_async(serialQueue, ^{ // 当前就在 serialQueue 上 dispatch_sync(serialQueue, ^{ // 死锁 }); });要理解为什么死锁就要理解同步派发和异步派发的本质区别。同步派发意味着“我不干别的就等你执行完再继续”但如果等待的目标队列和自己的执行队列是同一个串行队列就好比一个人排在自己的后面永远轮不到自己。面试官拿死锁题目来考你不是真的让你在工作中写这种代码而是考察你对队列调度的底层逻辑是否清楚。4.3 线程安全和数据竞争多线程题目还有一个方向是数据竞争。比如多线程同时修改一个NSMutableArray会导致崩溃这几乎是每届校招都会讲到的话题。解决方案有几种用synchronized加锁简单但性能一般用NSLock或NSRecursiveLock需要手动加解锁用串行队列做读写隔离读操作同步执行写操作异步执行用dispatch_barrier_async实现多读单写视频App里比较典型的模型是播放器状态、下载任务列表这类共享数据都需要做并发控制。笔试里如果能写出dispatch_barrier_async的解决方案并解释为什么比直接加锁性能更好会是很加分的回答。4.4 信号量控制最大并发数另外一个高频考点是信号量。dispatch_semaphore_wait和dispatch_signal用来控制并发执行的任务数量。比如同时最多允许3个下载任务dispatch_semaphore_t semaphore dispatch_semaphore_create(3); for (NSInteger i 0; i 10; i) { dispatch_async(globalQueue, ^{ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); [self downloadTaskAtIndex:i]; dispatch_semaphore_signal(semaphore); }); }这个写法在笔试中经常出现它的用途是防止一次性创建过多线程导致应用卡顿或内存飙升。视频类App的离线下载功能如果同时开太多任务会对网络和磁盘造成很大压力信号量是一个简单有效的流量控制手段。5. UIStackView 和布局适配题布局细节是代码品味的照妖镜5.1 为什么笔试爱考布局很多同学觉得布局很难笔试化因为它更多是写代码的实践操作。但爱奇艺这场笔试反其道而行之通过选择题和简答题来考察布局功底。这类题有很好的区分度只用过Frame布局的人和真正理解Auto Layout、safeArea的人答案完全不一样。视频类App的布局痛点非常明显播放器页面要横竖屏切换竖屏时上下要显示标题栏和底栏横屏时所有UI要隐藏给视频让路iPhone X开始出现刘海屏布局要适配safeArea不同尺寸的iPad布局要能自适应。这些问题不考复杂的算法考的是你有没有系统性地思考过iOS的布局体系。5.2 UIStackView 在 iOS 9 之后的价值UIStackView在iOS 9引入2018年时已经是iOS开发中的常客。它的核心价值在于把“多个视图之间的约束关系”从代码中抽离出来用栈的方式统一管理分布和排列。UIStackView *stackView [[UIStackView alloc] init]; stackView.axis UILayoutConstraintAxisVertical; stackView.distribution UIStackViewDistributionFillEqually; stackView.spacing 12; stackView.translatesAutoresizingMaskIntoConstraints NO; for (NSInteger i 0; i 4; i) { UIView *itemView [[UIView alloc] init]; [stackView addArrangedSubview:itemView]; } [self.view addSubview:stackView]; [NSLayoutConstraint activateConstraints:[ [stackView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:16], [stackView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-16], [stackView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor], [stackView.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor] ]];这里有个容易忽视的点用UIStackView时子视图是不需要设置高度约束的因为它由distribution属性决定。如果你还用旧思路给子视图手动加约束反而会导致约束冲突。笔试里如果给一段用UIStackView但子视图还有额外约束的代码问会不会有问题答案多半是会冲突。5.3 手动约束和 safeArea 适配手写约束同样是笔试容易涉及的题目。2018年那时候Masonry和系统原生约束都有人用但面试官显然更关注你是不是理解NSLayoutAnchor这种新API。safeAreaLayoutGuide这个API在iOS 11引入用于替代老旧的topLayoutGuide和bottomLayoutGuide。笔试中可能会考的细节是如果你用Y坐标去适配比如写self.view.frame.size.height - 100这种代码当iPhone X出现之后底部就会被Home Indicator遮挡。正确做法是[self.bottomView.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor];5.4 横竖屏切换的思路视频App的横竖屏适配是一道综合题。答这道题的逻辑链是用系统旋屏回调或viewWillTransitionToSize:监听尺寸变化横屏时隐藏导航栏和状态栏调整播放器视图的约束让它撑满全屏横屏状态下不受safeArea约束全屏铺满竖屏时恢复safeArea约束笔试里考这题重点是看你有没有想到“用纯代码切换屏幕方向”的机制以及UITransitionCoordinator做动画衔接。这些属于工程经验书本上不太会直接写。如果面试官用伪代码让你写实现思路你只需要把流程写清楚并且点出横屏约束切换时可能出现的frame跳动问题就能让面试官觉得你确实做过分屏和全屏。5.5 布局选项的性能对比我还想提一个经验笔试里选择布局方案的题目最好从维护成本和性能两个角度回答。Frame布局最直接但不利于适配不同屏幕尺寸AutoresizingMask只能做部分自动适配Auto Layout能力强但有约束计算开销UIStackView简化了约束但嵌套过深同样会影响性能。一个视频列表页的Cell如果嵌套了大量UIStackView和复杂约束滑动时会因为约束计算开销导致掉帧。优化思路是用preferredFrameRateRange、减少约束数量、或者在性能敏感的Cell中使用Frame布局。能答出这层取舍关系布局题基本就稳了。6. 网络层题目Charles 抓包才是工程能力的试金石6.1 视频App的网络层有多拼视频App的秒播率、卡顿率、流量消耗全部依赖网络层的工程质量。笔试里的网络题表面考的是HTTP/TCP基础实际上考的是你有没有真正面对过弱网问题。一道基础题HTTPS的握手流程是什么简化版的回答是客户端发送ClientHello服务器返回ServerHello和证书客户端验证证书后生成对称密钥并发送给服务器双方确认后开始加密通信。这个流程叫TLS握手理解了这个机制才知道为什么HTTPS能防窃听和防篡改。6.2 弱网模拟和流量分析能力光靠背协议是过不了网络题的。笔试和面试中真正拉开差距的是你有没有“定位线上网络问题”的方法论。这里就不得不提Charles——iOS开发中非常实用的HTTP调试工具也是我在这篇里想重点讲的部分。Charles可以通过监听本机端口把手机端App的HTTP/HTTPS请求转发到电脑上做查看和分析。它的核心用途有几个查看每个请求的URL、请求头、请求体、响应体分析每个接口的耗时和状态码模拟弱网环境比如限制带宽到100kbps打断点并修改请求或响应内容用于测试异常分支实操时手机和电脑连同一个Wi-Fi手机Wi-Fi设置里把HTTP转发指向电脑IP和Charles的端口然后在手机上信任Charles的SSL证书就可以解密查看HTTPS的明文内容了。6.3 用 Charles 排查问题的一个真实场景举个例子。播放器在部分Wi-Fi环境下总是加载失败线上反馈很多但测试阶段没有复现。这时候用Charles模拟“丢失出站数据”和“极高的延迟”很容易在本地复现类似卡死现象。从抓到的请求看视频CDN返回了302重定向而重定向后的地址被客户端的IP直连策略拦截了。这就不难理解问题的根源客户端在部分网络下无法完成CDN调度。用这个例子回答案面试题“你怎么排查线上网络问题”比单纯说“抓包看一下”要有说服力得多。面试官可以从这个回答里判断出你有没有完整地走过“现象-假设-复现-验证-修复”的排查链路。6.4 HTTPDNS 和缓存策略网络层笔试还有一个加分项理解视频类App的DNS痛点。普通DNS解析在跨网、跨国场景下可能返回距离很远的IP导致视频加载慢。所以很多视频App会自建HTTPDNS服务用HTTP请求直接获取最优IP。笔试中提到HTTPDNS时能说清楚背景和基本原理并说明业务收益就足够体现你对网络层有系统认识。缓存策略也是网络题常考的。视频App的接口缓存一般分三层内存缓存、磁盘缓存、HTTP缓存ETag/Last-Modified。笔试如果问“用户断网时怎么能看到历史记录”答案就是优先读取磁盘缓存并在界面上标记数据时间。这题考的不是Api而是你有没有主动做过缓存设计。7. 签名、描述文件与上架被低估的工程化考点7.1 为什么校招笔试会问上架流程很多同学觉得上架是发布的事笔试不会考。但爱奇艺这场笔试里就出现了和证书、签名相关的题。原因是iOS工程师不只是写业务代码还要负责App的构建、真机调试、测试包分发、上架发布。如果不懂签名机制很容易出现“真机跑不起来”“打不了包”“证书过期了不知道怎么处理”这种问题。7.2 证书和描述文件的关系Apple的签名体系有三个核心概念证书、描述文件、App ID。证书是身份证明基于RSA密钥对生成。描述文件是把“哪个App ID、哪些设备、哪个证书”绑在一起的配置文件。开发证书用于开发阶段描述文件里需要包含真机设备的UDID发布证书用于上传App Store描述文件不依赖设备App ID即应用的Bundle IdentifierXcode 8之后如果没有特殊需求直接用“Automatically manage signing”就行Xcode会帮你完成证书和描述文件的创建。但如果公司内部的证书体系比较复杂还是要手动管理。理解了这套机制才能明白为什么换了电脑之后真机调试失败原来是私钥没有导入新电脑。7.3 证书过期和签名失败怎么排查“开发者证书更新”是每年都会遇到的问题。证书有效期一般是一年过期后需要去开发者中心重新创建或续期。曾遇到过的情况是开发者中心显示证书有效但Xcode提示Code Signing Error。排查步骤是检查钥匙串里证书和私钥是否配对删除Xcode本地的过期描述文件在开发者中心重新下载在Xcode的Signing Capabilities里刷新签名状态确认Bundle ID是否和描述文件完全匹配这套排查步骤最好在面试前自己完整走一遍因为只要跟着做一次你就能理解签名的每一步在干什么。纸上谈兵和真动手面试官一问细节就分出来了。7.4 上架流程的完整链路2018年那个时间点上架流程是Xcode里Archive - Upload to App Store Connect - 在App Store Connect里填写元数据 - 提交审核 - 审核通过后手动或自动发布。中间还要配置TestFlight先给内部测试人员分发Beta版。校招笔试如果考这个流程分数都集中在细节上。比如“Archive之后工程里有多套签名配置选错了会导致上传失败”这就是一个很实际的问题。面试官想通过这类题确认你确实完整走过从代码到上架的全流程而不是只是在模拟器里跑通。8. 项目复盘与软技能最后拦在你前面的不是技术8.1 笔试之后综合面会怎么问第二场笔试通过后通常还有一两轮技术面和一轮HR面。技术面很多问题都是从笔试里延伸出来的。笔试考了循环引用综合面就会问“你在实际项目里遇到过循环引用问题吗怎么发现的”笔试考了UIStackView综合面就会问“UIStackView在复杂列表里性能怎么样你做过对比吗”所以笔试答题不能只背结论要准备好一个“真实经历”来支撑你的答案。8.2 把一个项目讲到面试官点过头的套路校招简历上写“完成了一个iOS App”是不够的要把项目拆成几个维度讲清楚。背景这个项目为什么做用户是谁我的角色负责哪些模块是独立开发还是多人协作技术难点整个项目里最难解决的技术问题是什么解决过程你用了什么方案有没有对比过其他方案可量化结果启动时间降低多少崩溃率降到多少比如“视频播放器做了边下边播”这个项目不要只说“我实现了边下边播”。要说因为用户播放视频等待时间长所以做了缓存策略。对比了AVAssetResourceLoader和缓存到本地后再播放两种方案最终选了AVAssetResourceLoader因为它能实现伪流播。实现过程中遇到线程安全问题通过自定义队列进行读写隔离解决了。最终起播等待时间降低了30%。这样的表述方式面试官能快速了解你的技术深度和解决问题的思路比罗列十个技术名词有效得多。8.3 反问环节怎么问出信息量面试最后一般会问面试官问题。建议不要问“加班多吗”“薪资怎么样”而是问这三个方向团队目前iOS技术栈的演进方向是什么遇到的最大技术挑战是播放体验还是性能优化一个应届生入职后最需要补足的是什么反问环节也算分问出高质量的问题说明你确实对这份工作有真实兴趣。当年我这一轮问的就是“爱奇艺的视频播放器内核是自研还是基于系统框架的”面试官很意外因为这个问题既体现了我对视频技术的了解也说明我真的研究过爱奇艺的产品形态。8.4 代码规范和协作能力也是隐性考点爱奇艺这种体量的公司代码规范和团队协作直接决定工程效率。笔试里虽然不直接考Git但综合面的项目问题中会间接考察你在团队里用Feature分支还是直接推主干Code Review一般关注哪些问题如果两个人改了同一个文件导致冲突会怎么处理这些问题没有标准答案但它们考察的是你有没有在真实团队里工作过的经验以及对工程质量的态度。建议在准备阶段多想一想这些非技术问题。9. 一份可以照着执行的备考冲刺清单最后把我自己当时用过的备考节奏整理出来仅供参考。我的基础属于中等偏上所以这个计划更适合已经能独立完成一个小项目、但还没系统梳理过知识体系的同学。第1周过语言基础OC属性修饰符、Block、KVC/KVO、通知机制、Swift和OC互调第2周过系统机制内存管理、循环引用、GCD、NSOperation、RunLoop相关源码分析第3周过UI和工程实践Auto Layout、safeArea、UITableView优化、离屏渲染、包体积优化第4周集中做题把往年面经按知识点分类整理每道题写出三层答案结论、原因、工程示例第5周项目复盘把简历上每一个项目都按背景-难点-解决-结果的结构写一遍第6周模拟面试让朋友按技术面节奏问问题练习口头表达还有一个容易忽略的细节把爱奇艺App本身当作一次产品和技术调研对象。用视频App时留意首屏启动速度、播放器的横竖屏交互、离线缓存入口、弱网提示设计然后思考这些功能在iOS端可能用到了哪些技术。这种换位思考的能力在校招面试里是很大的加分项。就我个人体会来说校招笔试的题目本身不会太偏难的是在有限时间内把每个知识点的深度和体系感体现出来。如果你认真过一遍上面这些内容再把项目经历梳理成有层次的故事那么这场笔试和后续面试你会比大多数应聘者准备得更充分。