
京东2019校招笔试ios开发工程师笔试题在当年的iOS圈子里讨论度不算低。原因倒不是题目有多偏多怪而是它的考察范围非常典型大厂既有语言底层原理又有实际业务场景设计算法题也占了不少比重。我当年完整参加了这场笔试后来也帮学弟学妹复盘过这套题的出题思路今天趁这个机会把整个考察逻辑和核心知识点拆开揉碎聊一聊想投电商类大厂iOS岗的同学可以参考。先说结论这类笔试并不是在考你会不会写Swift而是在考你作为一个iOS开发懂不懂系统底层、能不能处理复杂业务、有没有基本的算法功底。下面我按当年的题目类型和考察侧重点逐一展开。1. 京东iOS校招笔试的考察逻辑与整体印象1.1 试卷结构并不复杂但覆盖面非常广2019年的京东校招iOS笔试试卷整体分为几个固定模块OC语言与Runtime底层、内存管理、多线程、网络与数据持久化、UI与性能优化再加上一部分算法和逻辑题。有个很直观的感受题目几乎不涉及Swift全部以Objective-C为主。这跟2019年京东主App的开发语言底色完全一致即使当年Swift已经出了好几个大版本但电商核心业务线为了稳定依然大量使用OC维护。笔试考什么基本映射了业务线正在用什么。从难度梯度来看前30%属于送分题考察的是是否真的写过iOS代码比如strong和weak的区别、GCD和NSOperation的区别这类中间40%开始区分熟练度需要你理解消息转发流程、自动释放池的底层结构、运行时方法替换的坑最后30%是拉开差距的题往往结合JD的电商业务场景比如海量商品列表的滑动优化、复杂网络请求的架构设计还要手写算法。整体来看这套题的目标不是筛掉不会的人而是直接定位能干活且懂原理的人。1.2 电商大厂笔试的特点业务场景和技术深度挂钩很多同学刷了几个月LeetCode觉得算法题刷明白了笔试就稳了但京东这种电商大厂的iOS笔试有个明显的倾向数据结构和算法的题目本身不难难的是嵌在业务场景里。比如实现一个商品SKU组合的快速匹配逻辑设计一个订单列表的分页加载缓存策略这类题核心还是数据结构但你需要同时考虑OC的语言特性和iOS的UI刷新机制。我当时做笔试时有个很深的体会大量题目都喜欢用App启动流程首页Feed流加载购物车数据同步这些电商App特有的场景去包装一个普遍的原理问题。所以备考时如果只看纯理论不动手模拟业务场景很容易在二选一的选择题里纠结。这背后其实反映了京东iOS团队的用人标准不仅希望你来就能上手写业务更希望你具备从问题场景反推技术方案的能力。下面我按照笔试题目里出现频率最高的几个方向逐个拆解当年到底考了什么、底层原理是什么、以及我踩过的坑。2. OC语言与Runtime京东笔试对运行时机制的偏爱2.1 对象、类对象与元类的关系不是背会isa就完了京东笔试的单选题里关于Runtime的第一类高频题就是对象、类、元类之间的关系。很多人以为这题考察的是实例对象的isa指向类对象类对象的isa指向元类这张图但实际的坑点往往藏在细节里元类的isa指向谁根元类的superclass指向什么class方法返回的是类对象还是元类我当时在这类题上栽过一次因为只看结论没推演。笔试里变着法问对一个实例对象调用class方法和对类对象调用class方法返回结果分别是什么前者返回类对象后者返回自身。但如果你想当然地以为class方法走的是元类的消息查找流程就容易忽略NSObject对这个方法的特殊处理。这种题考察的不是记忆而是你推导消息查找路径的能力。2.2 消息转发机制这几乎是必考的大题有一道分值很高的简答题让描述OC的消息转发过程并要求说明每种转发阶段适合做什么业务。完整的消息转发分三个层次动态方法解析调用resolveInstanceMethod:或resolveClassMethod:允许你动态给类添加方法实现。快速转发路径forwardingTargetForSelector:把消息直接转发给另一个对象处理这也是很多代理模式、多继承模拟方案的底层基础。完整转发路径methodSignatureForSelector:配合forwardInvocation:先把消息打包成NSInvocation对象再决定由谁处理、如何处理。笔试答题的时候只写三段式是不够的要结合实际场景说明。我当时补充了JSPatch这类热修复方案利用消息转发做方法替换的思路以及在组件化架构里用消息转发实现远端路由降级的例子。2.3 KVO和KVC的底层原理附带高频衍生题KVO底层是通过动态生成子类并重写setter方法实现的_NSSetBoolValueAndNotify这类方法会被动态插入并在setter里调用willChangeValueForKey:和didChangeValueForKey:。这道题本身不算超纲但京东笔试会有个进阶问法手动触发KVO怎么做什么时候KVO会失效最容易答错的是KVO为什么在直接修改成员变量时不会触发。因为直接走_ivar赋值根本不经过生成的子类setter方法通知机制自然不会被激活。我建议备考时实际写个Demo验证一下比单纯背结论扎实得多。KVC的考察则集中在valueForKey:的查找顺序上先找getter方法再找实例变量最后才走accessInstanceVariablesDirectly的兜底逻辑很多人把顺序记反了。2.4 Category和Extension、load和initialize基础但极其容易混淆京东笔试对Category的考察几乎每年都有。除了Category能否添加成员变量这个老生常谈的问题真正的分水岭是Category添加的方法和类本身的方法谁在先谁在后为什么多个Category的方法调用顺序不稳定根源在于Category的方法并不会替换掉类里的原始方法而是被拼接到了方法列表的前面。多个Category编译顺序不确定方法列表中它们的顺序也就无法稳定所以最后一个编译的Category方法会生效这种说法其实不严谨。load和initialize也是高频选择。load是在类被加载进运行时环境时调用不依赖调用方即使没有任何代码引用该类也会执行initialize则是首次向该类发送消息时才触发。注意子类和父类之间的调用顺序问题笔试里经常拿子类没有实现initialize时调用子类会触发父类的initialize吗这种场景来区分是否真正理解。3. 内存管理与多线程看过源码和没看源码答题深度完全是两个级别3.1 ARC下还有没有内存管理题答案是不仅有还很难很多备战校招的同学以为ARC普及之后retain和release不用手动写了内存管理题就该消失了。事实恰恰相反京东笔试在内存管理上的考察比很多公司都深只不过重点从手动管理引用计数转移到了理解引用计数的底层机制。比如weak弱引用为什么对象销毁后weak指针会自动置nil因为Runtime在对象销毁时会遍历SideTable中的weak表把所有指向该对象的weak指针全部清零。这个机制本身不难但笔试会考逆向场景如果有一个__weak修饰的局部变量它的引用计数会不会因为赋值操作而增加答案是会因为编译器底层为了保证弱引用的安全性会临时retain一次作用域结束后再释放这就是为什么很多人以为weak不会改变引用计数但用_objc_retainAutoreleasedReturnValue之类的工具能看到计数变化的原因。3.2 autoreleasepool的边界条件答错的基本都没看过源码autoreleasepool的考察点集中在它和RunLoop的关系是什么主线程RunLoop在什么时机自动释放池子线程是否有自动释放池兜底我在一本面试题集里看到过autorelease对象什么时候释放的标准答案是当前RunLoop循环结束时这个说法太粗略了。严格来说是在当前autoreleasepool作用域结束时释放。主线程的RunLoop在每次事件处理前后会自动创建和销毁autoreleasepool所以表现上像是一次循环结束就释放。但如果你在子线程里手动创建了一个autoreleasepool作用域结束点完全由代码结构决定。笔试里用一个for循环内大量创建临时字符串内存峰值为什么容易暴涨的场景来考你需要答出如果不加局部autoreleasepool临时对象会被最外层的池统一管理直到外层作用域结束才释放而手动加局部池可以及时释放中间变量。3.3 GCD死锁的经典场景以及信号量在面试代码题里的用法多线程部分京东笔试题里GCD的比重远高于NSThread和NSOperation。最经典的必考题就是死锁dispatch_queue_t queue dispatch_queue_create(com.jd.test, DISPATCH_QUEUE_SERIAL); dispatch_async(queue, ^{ dispatch_sync(queue, ^{ NSLog(deadlock); }); });这段代码必然死锁外层block提交到串行队列在等待执行时内层又通过dispatch_sync往同一个串行队列提交任务这个新任务必须等外层block执行完才能开始而外层block又必须等内层sync返回才能结束互相等待。笔试如果只要求判断是否死锁很多同学能答对但如果接着问把串行队列换成并发队列会怎样把外层async换成sync会怎样就有人开始乱了。并发队列下dispatch_sync往自己内部提交任务不会死锁因为不需要等前面的block完成就能并发执行但外层也是sync时依然会死锁因为当前线程也在等待。信号量dispatch_semaphore_t在笔试里更多是跟异步转同步结合着考dispatch_semaphore_wait会让当前线程阻塞直到信号量值大于0。这里有个细节信号量超时等待的时间单位是dispatch_time_t用dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC))表示3秒很多人背了api但不会换算笔试里容易栽在传参类型上。3.4 多线程安全atomic真的线程安全吗这道送分题其实是个坑atomic不等于线程安全这已经是iOS面试的常识了但京东笔试的变态之处在于它会继续追问对一个atomic修饰的数组属性执行addObject操作是否线程安全答案是否定的。atomic只保证了getter/setter的原子性你拿到数组对象之后对这个可变数组做修改不在atomic的保护范围内。同理nonatomic的拷贝属性为什么适合NSString因为copy修饰符涉及的是setter内部对传入值的拷贝策略跟多线程无关。面试题发展到今天已经很少有人还停留在atomic就是绝对安全的认知上但如果真问到如何设计一个线程安全的数组容器很多人会卡住。我当时答题的思路是别一上来就加锁而是先分析读写比和实际使用场景。如果读多写少用读写锁或者dispatch_barrier_async配合并发队列如果写频繁且单写多读用pthread_rwlock如果追求简单直接用synchronized但要注意性能损耗。笔试不要求你写完整代码但需要你能对比不同锁的适用场景。4. 网络层与数据持久化电商App的链路稳定靠的就是这些基础知识4.1 HTTP/HTTPS的握手细节京东笔试的思路是从一次购物车同步说起电商App里最常见的网络请求场景就是提交订单、同步购物车、刷新首页Feed流。京东笔试特别喜欢拿这些业务场景包装网络题。第一类高频题是HTTPS的握手流程要求写出TLS握手的完整步骤并说明客户端如何校验证书。我建议答题时不要只写客户端用CA公钥验证服务端证书要展开客户端收到证书后先检查证书链是否可信再检查域名是否匹配最后用证书里的公钥协商出一个对称加密密钥。对iOS开发而言还需要补充AFNetworking/NSURLSession默认的证书校验策略以及如果碰到自签名证书或SSL Pinning应该怎么做。当年笔试考了一道App抓包相关的问题大意是为什么配置代理后HTTPS请求能看到明文数据这其实牵涉到中间人攻击的原理客户端如果信任了代理服务器的根证书就会把代理当作合法服务端代理再和服务端建立真正的TLS连接从而解密并转发数据。4.2 DNS解析的坑电商大厂必问的本地DNS与HTTPDNS京东这种超大流量AppDNS问题几乎是所有网络层的核心痛点。笔试里问DNS解析的流程如果你只写从本地DNS缓存查到权威服务器就太浅了。我当时在答题里补充了运营商DNS劫持和LocalDNS解析失败的问题以及HTTPDNS通过ip直连解决传统DNS缺陷的思路。电商App的域名经常有多个CDN节点不同地区用户解析到的IP可能不同如果走了失效的IP初始化网络层时需要自动降级和重试机制。这个知识点在笔试里可能只占一道选择题但如果你准备面试几乎必考。4.3 数据持久化选型从NSUserDefaults到SQLite再到Core DataiOS的数据持久化方案笔试常以不同场景应该选哪种方案的形式出现。京东的购物车、浏览历史、缓存商品列表这些功能对持久化需求完全不同题目会让判断存储用户偏好设置、登录token首选NSUserDefaults支持轻量级读写但注意不要让大数据量走这个通道。存储结构化数据且需要复杂查询SQLite或用FMDB封装适合商品列表、订单历史这类大规模数据。存储对象图且层级复杂Core Data更适合但学习和调试成本高。关于SQLite的一个高频细节题为什么要用预处理语句prepared statement答案是防止SQL注入、提高重复执行效率、避免重复解析SQL语句。数据库升级时的表结构迁移也是一个考点笔试可能会给一个V1到V2的字段变更场景问你怎么写migration逻辑这对应了FMDB升级方案里的user_version判断。4.4 大文件下载与断点续传电商App更新包下载的隐藏考点京东商城App体积不小应用内更新和资源包下载是真实存在的需求。笔试里关于断点续传的题目通常这样出客户端下载一个100MB的更新包下载到60%时网络中断如何实现续传而不重新下载核心是HTTP头部的Range字段客户端记录已下载的字节数再向服务端请求剩余字节。服务端返回206 Partial Content同时带Content-Range标识当前返回的字节范围。这个过程中需要处理本地已有数据的拼接和校验如果服务器不支持断点续传要能自动降级为全量下载。5. UI与性能优化京东这种数据量极大的App如何保证FPS稳定5.1 Auto Layout和Frame布局的取舍笔试里换了个说法京东笔试关于UI的题目很少直接问Autolayout和Frame哪个好而是会给一个具体场景一个商品卡片需要根据图片高度动态变化同时要适配不同屏幕尺寸你会选择哪种布局方式Auto Layout的优势在于适配性强但它的约束计算是有成本的尤其是在大量Cell复用的情况下如果约束非常复杂CPU计算约束的时间会直线上升。Frame布局的优势是性能可控且计算直接但要自己处理屏幕适配和旋转问题。笔试的正确答案通常不是二选一而是混合使用。常规Cell部分用Auto Layout保证快速开发对于超大列表中的复杂Cell可以考虑预计算Frame缓存到内存模型里。我当时有段心得在UITableViewCell的prepareForReuse里清理约束有时候比优化约束本身更有效。5.2 UITableView优化是京东笔试绝对的重点没有之一首页Feed流也好、商品列表也好电商App的核心交互都集中在UITableView或UICollectionView上。笔试里性能优化那道大题我几乎能猜到标准答案的几个方向但能不能说得完整就看积累Cell复用机制注册重用标识符避免每次创建新Cell。避免Cell布局的重复计算提前计算好行高并缓存。尤其是iOS 11之后Self-Sizing Cells虽然方便但高度计算放在主线程的话上下滑动依然会有卡顿。异步绘制把文本的绘制、图片的decode从主线程挪到子线程只把绘制结果渲染到Layer上。减少离屏渲染尽量避免圆角阴影maskToBounds组合如果必须使用圆角考虑用贝塞尔曲线裁剪或者直接让UI切图。图片加载与缓存使用SDWebImage或YYKit注意图片的尺寸要与显示尺寸匹配不要加载一张大图直接丢给UIImageView先做Downsampling。这些点都比较常规但京东笔试会加一道如果列表卡顿请你给出从主线程工具入手排查的具体步骤的题。你需要回答通过Instruments的Time Profiler找到卡顿函数查看主线程的调用栈观察是否有耗时的文件IO、解压、布局计算再进行针对性优化。5.3 离屏渲染的本质从GPU和CALayer的角度理解离屏渲染这个概念在笔试里反复出现。很多同学能背出设置圆角、阴影、maskToBounds会触发离屏渲染但解释不了为什么。其实离屏渲染的本质是图层无法直接在当前渲染缓冲区完成合成需要额外创建离屏渲染缓冲区和上下文先渲染到离屏缓冲区再拿到结果这个过程有大量GPU上下文切换开销。我建议答题时从CALayer的绘制流程说起CALayer有自己的contents和backgroundColor属性正常情况下GPU可以把layer直接合成到屏幕上的帧缓冲区。但当你给layer设置了mask、圆角、阴影等效果时GPU需要先渲染出一个中间结果再一次合成到屏幕这个过程就需要离屏缓冲区。频繁的离屏渲染会直接影响FPS。如果笔试里有优化题你可以提到用shouldRasterize来缓存已经绘制好的layer但要注意它也不是万能的如果layer频繁变化反而会浪费GPU内存。5.4 启动时间优化电商App启动快慢直接影响GMVApp启动优化的题目在2019年后的校招笔试里频率越来越高。京东的App启动涉及的场景包括读取本地缓存、初始化网络SDK、注册路由、配置日志系统、恢复登录状态。启动优化的两个关键阶段是main函数之前和main函数之后。main函数之前的优化方向比较有限减少动态库加载、合并静态库、减少load方法、削减二进制体积。main函数之后的优化方向更多将非首屏需要的SDK延迟初始化、把UI首次渲染需要的数据提前预加载、避免首帧时做大量主线程IO。笔试会出一个首屏Feed流从点击图标到出现商品列表整个过程有哪几个耗时节点的题你如果只回答启动网络请求Cell布局就太泛了需要细化到进程创建、动态库加载、runtime初始化、main函数调用、application:didFinishLaunching回调、首页VC加载、请求发送、数据解析、Cell显示。6. 算法与数据结构笔试里常见的几类题以及怎么用OC写不别扭6.1 字符串类题目最长回文子串、字符串反转、大数相加京东笔试的算法题不会特别难但都集中在高频类型。字符串处理是必须准备的。我记得有一道题是让用OC实现一个字符串反转并说明时间复杂度。看似简单但很多人会忽略OC里NSString的不可变性和字符编码问题。如果用C语言的char数组逐个交换要注意中文等多字节字符的处理如果直接用OC的substringWithRange:做反转倒也能完成但要注意循环里反复创建子字符串的性能浪费。大数相加是另一个常见题两个很长的数字字符串相加不能用LongLong直接算。正确做法是模拟手工加法从最低位开始逐位相加处理好进位。这道题测的是逻辑清晰度和边界条件处理我见过不少同学在一次遍历里因为进位标记没复位导致答案错一位。6.2 链表和二叉树OC里面手写数据结构需要刻意练习OC写链表和二叉树确实比C或Java要绕一些主要是因为nil和NULL的边界处理、对象类型的强引用和弱引用问题。笔试里链表题通常是反转链表、链表环检测、合并两个有序链表。二叉树题则集中在层序遍历、最近公共祖先、路径总和这几类。用OC写的时候有个细节特别容易被忽略节点类里的next指针如果用strong修饰容易在处理环形链表时造成循环引用和内存泄漏。所以我写这类代码时节点属性会刻意用assign或weak并在需要断开链接时手动置nil。6.3 Top K问题和海量数据的降级策略电商场景的算法包装Top K问题经常被包装成从10万个商品中找出价格最高的100个商品从百万级浏览记录中找出被访问最多的10个页面。最经典的解法是用大小为K的最小堆这样每次只需要和堆顶比较大于堆顶就替换并调整堆时间复杂度是O(N log K)空间复杂度是O(K)。如果用排序做时间复杂度O(N log N)在大数据量下明显吃亏。笔试如果给出内存限制比如10亿个数找最大的100个内存只能容纳1000个数你需要答出分治和堆结合的思路先把数据分片每片求出各自的Top K再做归并。这种题其实不太考验OC语法更考验你是否具备海量数据处理的工程思维。6.4 动态规划和贪心不是重点但不能完全不会京东笔试的算法题整体偏应用动态规划的比例不像字节那么高但也会出现简单到中等的题目比如爬楼梯、最大子序和、零钱兑换。贪心作为辅助类型也偶尔出现。我倾向于把这类题放在最后做先保证字符串、链表、二叉树、Top K这些高频题目全对再回头啃DP。动态规划从笔试得分角度来看重点不是你能不能在10分钟内AC而是你能不能描述清楚状态转移方程。我当时在答题纸上先写dp[i]表示前i个元素能组成的最大子序和转移方程是dp[i] max(dp[i-1] nums[i], nums[i])再写代码这样即使代码有小bug阅卷人也知道你的思路是对的。7. 备考京东iOS笔试我踩过的坑和最终总结京东2019校招笔试整体难度中等偏上相比纯刷题型大厂它更像技术原理 业务场景 算法基本功的综合测试。我当年准备时走过不少弯路分享几个关键教训。第一不要只刷题不读源码。Runtime、内存管理、GCD这些部分只看别人总结的面试题永远搞不透。建议至少读一遍objc源码里关于对象生命周期和弱引用的实现哪怕不追求每个函数都懂把关键流程的骨架弄清楚笔试里那些为什么类题目就能答出层次感。第二笔试前一定要动手写几个完整的UI性能优化Demo。不要纸上谈兵用Instruments跑一遍卡顿和离屏渲染检测看真实数据。京东笔试里如果出优化题你可以引用自己测试出来的数据比如我手动测试过用异步绘制之后列表帧率从45提升到58这种答题比空洞的背条目有说服力十倍。第三算法题准备要有侧重点。如果目标明确是电商大厂多关注字符串、数组、链表、二叉树、Top K、动态规划这几个类型。OC语言的语法特性会让算法实现比C稍微繁琐建议在笔试前把常用数据结构都用OC手写一遍尤其是链表类和二叉树类避免考场上因为语法卡壳影响思路。第四审题要仔细尤其是那些以下说法正确的是和以下说法错误的是这种选择题。京东特别喜欢在一道题里埋两个反向选项比如atomic能保证多线程访问绝对安全和atomic只能保证getter/setter的线程安全看起来很像但答案完全不同。我当时有两次都差点选错最后是靠对细节的精确把控救回来的。最后想说笔试只是校招的一道门槛真正决定offer的往往是后面的技术面。但笔试里考察的知识点恰恰是技术面深挖的起点。把笔试准备扎实了后面的路会顺畅很多。如果这篇文章对你有帮助或者你对其中某个知识点有不同理解欢迎在评论区交流一起查漏补缺。