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

资讯详情

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

货拉拉iOS笔试题深层解析:考点拆解与答题策略

货拉拉iOS笔试题深层解析:考点拆解与答题策略 拿到一份“货拉拉2018秋招iOS工程师笔试题卷二B”的时候我第一反应不是去看题目难不难而是先回忆了一件事2018年秋季的iOS圈子里大家正在疯狂适配iPhone X的刘海屏手里的项目还在用Objective-C写底层的消息通知。B卷是A卷的姊妹卷一般公司笔试出两份卷子考点对齐题目顺序和个别小题做调整为了防止前后排互相抄。但作为求职者真正的问题不是“哪份卷子简单”而是“这一轮笔试到底想筛出什么能力”。这篇东西不保证你背完就能过任何一家公司的笔试但我会把这类卷子常见的设计逻辑、考点结构、答题策略、踩坑细节全部拆开讲一遍适合正在准备校招面试的iOS方向同学也适合准备跳槽的社招工程师用来查漏补缺。1. 这份卷子到底在考什么从业务反推考点1.1 货拉拉的业务模式决定了考点倾向货拉拉是一个同城货运平台司机端和用户端都有核心流程浓缩下来就是用户下单、系统派单、司机接单、在线沟通、路线规划、线下履约、订单结算。整个流程贯穿了地图SDK、长连接、消息推送、WebView混合页面、本地缓存、异常网络兜底这些东西。所以它的iOS笔试题不会只盯着UI控件和动画考而是会更偏向网络、存储、并发、运行时这类和真实App稳定性强相关的知识点。这不是货拉拉独有的现象几乎所有有真实业务压力的互联网公司出题思路都一样他们想招一个能维护线上复杂App的人而不只是会用Storyboard拉控件的人。我见过很多候选人备考时疯狂刷“iOS基础500题”背了不少冷门语法最后却在一道“如果tableView滑动卡顿你会怎么排查”的简答题上卡住。这说明题目本身不是单纯考记忆而是考你有没有把知识点串联起来解决问题的能力。这份卷子也是这样它看似是零散的几百个知识点实际上每一部分都指向一个业务场景数据同步、缓存优化、并发冲突、用户体验。所以看这份卷子时不要只盯“考了什么语法”要想“为什么考这一点”。1.2 2018年iOS生态的考核背景2018年这个时间点很重要。当时iOS 11已经普及iPhone X的刘海屏适配是一个高频考点很多题会问你safe area怎么处理导航栏在异形屏上的布局差异怎么解决。Swift 4已经发布但国内绝大多数商业项目的核心代码还是Objective-C所以笔试题通常以OC为主要求你至少能读懂Swift。第三方库方面AFNetworking、SDWebImage、Masonry、FMDB、YYModel这些是项目标配很多笔试简答题表面上问“AFNetworking和NSURLSession有什么关系”实际是在考你对网络请求生命周期的理解。现在回看这些考点其实并没有过时只是换了一层皮。比如2018年问“如何用Runtime给分类添加属性”现在可能会问“如何用Swift实现类似AOP的埋点方案”。原理还是那一套只是语言环境变了。所以这份卷子虽然是几年前的但对今天准备面试的人依然有非常高的参考价值。我根据历年这类笔试的出题规律把试卷模块分布整理成了下面的表后面每个部分都会按这个框架展开。题型考察方向建议用时单选/多选基础概念、内存管理、语法细节15分钟判断/填空概念辨析、零散知识点5分钟简答题原理描述、机制分析25分钟代码题手写实现、线程安全、内存管理30分钟算法与设计题逻辑设计、系统设计25分钟2. iOS核心机制考点逐个拆解2.1 内存管理引用计数、weak实现与循环引用内存管理是iOS笔试里出现频率最高的模块没有之一。问法千变万化但本质上绕不开三件事引用计数的机制、weak的实现原理、循环引用的经典场景。先讲引用计数。ARC时代你可能不需要自己调用retain/release但是必须清楚编译器在编译期做了什么。一个对象创建出来引用计数为1每次被强引用retainCount加1引用方释放计数减1减到0就执行dealloc。strong、copy这两个修饰符从语义上都是强引用区别在于copy修饰的属性在setter里会调用copy方法生成一个新的不可变对象从而避免外界修改影响到内部状态。很多人在笔试里把copy和strong混为一谈真到考场上被追问“为什么NSString属性一般用copy”就露馅了。weak是怎么实现的系统维护了一张全局的weak表底层是一个哈希表key是对象地址value是weak指针地址的数组。当对象的引用计数归零dealloc执行时系统会根据对象地址找到这张表把所有weak指针全部置为nil。这整个过程发生在objc_clear_deallocating阶段。很多候选人能回答到“weak指针会自动置nil”但说不清底层是哈希表这就是区分普通答案和高分答案的地方。再往下追问一层weak和unsafe_unretained有什么区别答案就是weak在对象释放后自动置nil而unsafe_unretained不会指针变成野指针再访问就是崩溃。这一连串问题你要是都能接住内存管理这关基本就过了。循环引用是必考的应用题。典型场景有三个block捕获了self、delegate属性用了strong、NSTimer的target强持有self。以block为例如果一个对象持有block属性block内部又使用了self那self-block-self就形成了一个环。解决办法是把self声明为__weakblock内部再用一个__strong的局部变量接收weakSelf防止执行过程中对象被提前释放。这种写法在面试时一定要写全光写__weak不写__strong是会扣分的因为weakSelf在block执行期间可能已经被释放成nil后面继续发消息全部失效。2.2 Runtime与消息传递机制Runtime是OC区别于普通静态语言的核心。笔试里最常见的题是“解释一下objc_msgSend的调用流程”这题答好了能镇住全场。当你在OC里调用一个方法比如[person eat]编译器会把它翻译成objc_msgSend(person, selector(eat))。Runtime在运行时拿到这个消息后会先通过person的isa指针找到它所属的类然后在类的方法列表里找对应的方法实现。方法列表是一个缓存和分级的结构如果类本身没找到就沿着superclass往上遍历如果最终找到了就调用方法的函数指针去执行如果整个继承链都找不到就会进入消息转发流程。消息转发的几个阶段动态方法解析resolveInstanceMethod、快速转发forwardingTargetForSelector、常规转发methodSignatureForSelector和forwardInvocation每一层都有对应的API可以做兜底处理。笔试中有一个高频变体怎么给分类添加属性。直接用property在分类里声明属性是不会自动生成成员变量和setter/getter的因为分类在编译期无法往类的结构体里添加ivar。正确做法是结合关联对象用objc_setAssociatedObject和objc_getAssociatedObject手动实现setter/getter。这算是一道典型的“Runtime综合应用题”。很多候选人知道关联对象这个词但说不清关联对象存在哪里、什么时候释放其实关联对象是放在一个全局的AssociationsHashMap里的对象dealloc时才会解除。再有就是Method Swizzling。面试官非常喜欢问它因为它既是Runtime的实战应用又隐藏了足够的坑。核心原理就是把两个方法的IMP互换。但要注意Swizzling应该写在load方法里要用dispatch_once保证只执行一次交换后要调用原方法实现时sel和imp的对应关系已经变化需要小心命名。这一块建议你自己动手写一个小Demo把交换前后的IMP指向打出来看一眼面试时表达会顺很多。2.3 Runloop事件循环、Timer与卡顿检测Runloop这个词在过去几年的面试里几乎成了必考。它的本质是一个do-while循环应用启动后主线程的Runloop会持续运行监听各种各样的输入源或定时器有事件就处理没事件就休眠以此实现“系统不卡顿也不空转”。面试时有一个很经典的类比Runloop就像一个前台的服务员没人来就趴在桌上休息有人来就起身接待处理完了再趴回去。Runloop有几种Mode常见的有DefaultMode、UITrackingMode和CommonModes。面试题喜欢问为什么NSTimer在滚动时不准时因为滚动时主线程Runloop会切换到UITrackingMode默认情况下Timer被注册在DefaultMode滚动过程中根本不会被执行。解决办法是把Timer加到CommonModes或者改用CADisplayLink。这个场景太经典了几乎每年都会出现。更隐蔽的坑是Timer会强持有它的target如果你在控制器里创建了一个Timertarget又指向self那控制器根本走不了dealloc必须要在viewWillDisappear或dealloc里调invalidate把Timer停掉。Runloop更深层的价值在于它是很多黑科技的底层基石。AutoreleasePool在什么时候释放一个Runloop循环结束时。App卡顿检测怎么做监听主线程Runloop的状态如果在BeforeSources或AfterWaiting阶段停留超过阈值就判定为卡顿把主线程的调用栈打出来。还有一些人用它做线程保活给子线程常驻一个Runloop并往里添加Source让线程不被销毁方便处理异步队列。笔试时如果你能主动从“Timer不准时”引申到“卡顿检测”和“TableView滑动优化”高度马上就上来了。2.4 多线程GCD、NSOperation与线程安全iOS里的多线程方案很多NSThread、GCD、NSOperationQueue但笔试最常考的是GCD和线程安全。GCD先把概念捋清楚队列分串行和并发任务分同步和异步。组合起来最关键的是理解“同步任务不会开启新线程运行在当前线程异步任务会开启新线程”。很多人分不清“串行队列同步执行”和“主队列异步执行”的死锁问题。主队列里执行同步任务会死锁原因是任务提交到主队列后要排队但当前线程被这个等待操作卡住了队列里的任务永远等不到被处理的一刻。这个坑在面试里被反复考一定要能用自己的话讲明白流程而不只是记住结论。信号量也有不少题。dispatch_semaphore_wait和dispatch_semaphore_signal成对出现常见用途是控制并发数、把异步操作变成同步等待。比如你要等待两个网络请求都返回后再合并结果就可以用dispatch_group_notify或者信号量实现。后者虽然在笔试中用得多但在真实项目中要谨慎因为如果放到主线程里等待很容易卡UI。面试官会追问“信号量设成0或设成1有什么区别”设0表示没有资源所有等待的线程都会阻塞设1表示一次只允许一个线程访问这就是互斥锁的原理。NSOperationQueue是比GCD更高层的抽象它最大的优势是可以设置最大并发数、依赖关系、取消操作。很多第三方库和项目核心模块都会用它来管理任务。笔试问“为什么有GCD还要用NSOperation”本质上考的就是抽象层次的区别GCD是C接口轻量、高效适合简单任务NSOperationQueue是OC对象性能略低但可管理性强适合复杂的任务编排。线程安全更是重点。比如手写单例得考虑加锁。iOS 12以后dispatch_once依然是最常用的方式因为它底层保证了多线程下只执行一次。但有些面试官会追问怎么保证一个可变数组在多线程下读写安全正确回答有几层加串行队列、加NSLock、用synchronized再高级一点是读写锁pthread_rwlock。你要能对比它们的性能和使用场景并且知道读写锁能让多线程同时读、写互斥。2.5 网络协议与数据持久化网络是业务型App的生命线必然出现在笔试卷子里。基础题会问TCP和UDP的区别、TCP三次握手的过程、HTTPS的加密流程。稍微进阶一点会问HTTP/2的多路复用、Cookie和Session的区别、DNS解析过程。对于iOS开发来说重点还在于你平时用的AFNetworking到底做了什么。AFNetworking本质上是NSURLSession的封装处理了请求序列化、响应反序列化、网络状态监听、SSL Pinning等一堆细节。在笔试里如果遇到“怎么保证网络请求的安全性”至少从三个层面答传输层用HTTPS证书层面做SSL Pinning数据层面做加密签名。但如果完全不了解底层网络协议光背AFNetworking的API是不够的。比如面试官让你解释HTTPS的握手过程你得说清楚客户端先拿到服务端证书验证证书合法性后生成对称密钥再用服务端公钥加密传回去后续通信全部走对称加密。数据持久化也会考但要结合业务场景。iOS常见的持久化方案有NSUserDefaults、plist、归档、SQLite、CoreData、Keychain。笔试喜欢问“什么情况下用FMDB而不用NSUserDefaults”。答案其实很简单数据量大、结构化查询、需要索引、需要事务的场景必须上数据库。NSUserDefaults适合存少量配置键值对而且它在性能上有全量加载的特点塞太多东西会影响启动速度。我把几个方案的适用场景整理成了下面的表答题时可以直接引用。方案适用场景特点NSUserDefaults少量配置键值对同步读取、全量加载、简单归档NSKeyedArchiver自定义对象序列化一次性写入、不适合查询SQLite/FMDB结构化数据、离线缓存支持查询、索引、事务CoreData对象图管理学习成本高、内存占用大Keychain敏感信息加密存储、由系统管理数据库设计题也是常客。比如设计一个离线缓存表至少要包含缓存的key、响应数据、创建时间、过期时间。查询的时候先查缓存判断是否过期过期就发起新请求没用过期直接返回缓存。这就是一个完整的缓存策略笔试时能把这个流程讲清楚比死背SQL语句有用得多。3. 典型题型与实战答题思路3.1 概念题把简答题答出层次感笔试卷里最占篇幅的是概念题比如“简述Category和Extension的区别”。这类题看起来简单但很多人丢分是因为答得太薄。我的建议是沿用“定义—原理—场景—补充”四层结构。拿这题举例定义层Category是分类可以在不修改原类的情况下扩展方法但不能直接添加实例变量Extension是类扩展编译时直接合并进原类可以添加属性。原理层Category的方法会通过Runtime在加载时被附加到类的方法列表中且分类方法优先级在编译时间靠后时会覆盖同名主类方法但同名分类方法之间的执行顺序不确定。场景层Category适合模块化、多端复用Extension适合在.m中隐去私有属性和方法。补充层给Category添加属性要用关联对象Swift里Extension不能覆盖已有方法。这个结构下来普通面试官会点头资深面试官也能看到你的思考深度。另一道高频概念题是“解释一下nonatomic和atomic的区别”。很多人只会背“atomic原子性、性能低、线程安全”实际上atomic并不等于线程安全它只保证属性的getter/setter方法本身的原子性不保证业务逻辑的线程安全。比如一个可变数组声明成atomic两个线程同时往里面addObject依然会出问题。这个辨析能说出来概念题基本就是满分水准了。3.2 代码题手写题必须注意的书写规范代码题是笔试里最容易拉开差距的题。写不写得对是一个问题写得好不好看是另一个问题。以一个高频手写题为例请用GCD实现一个线程安全的单例。正确答案长这样 (instancetype)sharedInstance { static MyClass *instance nil; static dispatch_once_t onceToken; dispatch_once(onceToken, ^{ instance [[MyClass alloc] init]; }); return instance; }注意几个细节静态局部变量要写在函数体内部保证全局唯一dispatch_once_t必须是static系统通过它判断代码执行过没有初始化代码不能省略alloc/init。如果你愿意在注释里写一句“dispatch_once在底层通过原子操作保证多线程只执行一次”这就是加分项。另一种常见代码题是手写Block的循环引用解决。至少要写出__weak typeof(self) weakSelf self; self.block ^{ __strong typeof(weakSelf) strongSelf weakSelf; [strongSelf doSomething]; };写到这里再附一句block内部如果要多次使用self最好转成strongSelf避免weakSelf在多次调用之间被释放。这种细节是真做过项目的人才写得出比背一整套模板有用得多。还有一类代码题是考API调用顺序比如“在viewDidLoad里创建一个UIScrollView如何正确设置自动布局约束”。这题很多人翻车是因为忘了把translatesAutoresizingMaskIntoConstraints设为NO加了约束也没效果或者约束设置完了没有调用layoutIfNeeded导致frame不对。这种题考察的其实就是日常工程的熟悉程度平时多动手写就不会慌。3.3 场景设计题从功能描述到技术方案设计题通常是压轴题给你一段业务描述让你给出技术方案。2018年那一批公司的出题方向很接近比如“设计一个图片缓存库”或者“设计一个即时消息模块的本地存储结构”。以图片缓存库为例答的时候要按层级拆。第一层是内存缓存NSCache天然支持自动清除适合做最近使用图片的高速缓存key可以用URL字符串value用解码后的UIImage或原始data。第二层是磁盘缓存建议用文件系统加数据库索引的方式文件按URL的md5作为文件名索引表记录创建时间和过期时间。整个读取流程内存缓存先查没命中查磁盘磁盘也没有就发起网络请求成功后再写回两层缓存。淘汰策略可以参考LRU把最近最少使用的数据先清掉。这样一套下来你已经表现出了完整的设计能力。设计题还有一个常见变体让你设计一个IM消息的本地存储结构。这种题考察的是对数据模型的理解你至少要拆出会话表、消息表、联系人表消息表要有消息ID、会话ID、发送者ID、内容、时间戳、状态字段。还要考虑分页加载所以消息表要按时间倒序建索引。能把这些讲清楚说明你平时不是只写页面的人。3.4 算法题iOS笔试中的算法范围不大iOS笔试题的算法部分一般不会为难人数组、字符串、链表是重点动态规划和贪心出现频率低。常见的有字符串反转、判断回文、链表中环的检测、数组去重、最大子序和。建议在笔试前把这几类高频题各刷十来道重点练边界条件。写算法题时时间复杂度要写清楚比如快排是O(nlogn)暴力双重循环是O(n^2)能给出更优解的时候别偷懒。写作格式上算法题要给足注释和关键步骤说明。即使算法不是最优解只要过程清晰、边界处理正确得分也不会太低。比如反转链表时要判断链表为空或只有一个节点的情况字符串处理要先判断nil和空串。这些细节是看得见的工程素养。4. 考场实操时间分配与答题顺序4.1 拿到试卷别急着动笔先花5分钟扫卷所有笔试都适用这条规则。拿到卷子先完整翻一遍判断题目的分值和数量。如果简答题分值高就先给概念题做标记如果后面有设计题你需要在心里预留至少20分钟。很多人在前面的选择题上磨蹭太久结果最后的大题草草写了三行这非常不划算。我见过一份答卷前面的选择填空写得极其工整最后的缓存设计题只有两句话这种分数结构一看就没有实战经验。合理的用时比例应该是选择填空30%、代码题30%、设计题40%。4.2 选择题的做题策略选择题考的是记忆和积累会就是会不会就是不会不需要犹豫。重点在排除法比如内存管理的题目先看有没有“weak指针销毁时自动置nil”这种明显正确项再排除明显错误项。遇到不确定的不要空着凭第一直觉选。另外注意题目里的“不正确”三个字iOS笔试往往喜欢反向提问每年都有同学把“以下哪个是不正确的”看成“正确的是”这种丢分太冤了。还有一种情况是选项里出现两个看起来都对的表述这时候要回到最基础的定义去分辨。比如strong和copy看的是属性被赋值时是否产生新的对象assign和weak看的是对象释放后指针还在不在。把定义吃透了选择题基本不会错。4.3 编程题的排版与步骤意识手写代码不要一行写到头注意换行和缩进。OC方法名长参数多写成一坨既不美观又容易漏括号。更重要的是代码题要展示思考过程。哪怕只能写出伪代码也要把关键步骤的注释写出来比如“这里需要加锁”“这里要做空值判断”。面试官阅卷时看的是你有没有能力完成这个任务而不是看你默写能力多强。我批卷子的时候看到一个人把整个函数结构都搭好了只差一个细节没想通这种答案我会给很高分因为说明思维是对的只是临场差一步。4.4 交卷前的检查清单交卷前留出3到5分钟从头到尾检查一遍每个简答题是否写了结论句代码题是否缺少返回值数组和字典的下标有没有越界风险有没有忘记写内存管理的关键字。一点小建议把所有代码题的每行都看一遍确定没有把strong和weak写混没有把assign用在对象上。这些小错误一旦出现印象分直接打折。5. 笔试复盘面试官视角的加分项和减分项5.1 最常见的丢分点根据我这些年看简历和面试的经验笔试丢分有三个高频原因。第一概念只背结论不背原理。比如很多人会背“assign用于基本数据类型weak用于避免循环引用”但问为什么assign修饰对象会不安全答不上来。因为assign是直接赋值不增加引用计数也不在对象销毁时置nil对象释放后指针变成野指针再次访问就是崩溃。只背结论的人是过不了追问的。第二代码题忽略线程安全和边界条件。写单例不加锁写数组操作不判断越界写递归不写终止条件这些都是丢分重灾区。笔试测试用例是机器或面试官肉眼看的只要有一个边界没处理答案质量就要大打折扣。我在阅卷时看到过一个很典型的例子手写快排主逻辑是对的但没处理数组为空的情况面试官想给高分都犹豫。第三简答题不分段、不写结构。整个答案一大坨丢在那里面试官很难抓到重点。哪怕知识点都覆盖到了排版混乱也会影响评分。建议养成分点作答的习惯第一点写结论后面写原因和例子。还是拿Category和Extension举例先写“两者本质区别在于编译期合并和运行时附加”再展开阅卷体验会好很多。5.2 容易被忽略的加分细节反过来也有些细节特别能加分。比如在简答题末尾主动写一句“这个机制在业务中的实际应用场景是……”这会立刻让面试官觉得你是一个有工程经验的人。再比如手写代码的时候顺手写一个防御性判断对可能为nil的参数做保护这种意识在阅卷时非常显眼。还有如果某道题你完全不熟悉试着写一部分和你已知知识的关联哪怕只是把你理解的相关概念写出来也比完全空白强。另一个加分细节是遇到设计题时主动给出方案的缺点和替代方案。比如你说用NSCache做图片缓存可以补一句“NSCache的LRU策略是不可控的如果需要精确淘汰策略可以考虑用YCMemoryCache这类自定义实现”。这种“既知道方案A也知道方案A的边界”的表达是资深工程师和初学者的分水岭。5.3 笔试通过后面试怎么衔接笔试只是第一道关卡。通常笔试通过后面试官会拿着你的答卷来问问题比如“你这里说runloop可以做卡顿检测具体是怎么做的”所以交卷前一定要记住自己写了哪些内容别写一些自己都解释不了的东西。我有一个习惯每写完一道简答或设计题会在草稿纸上记几个关键词等面试前翻一遍回忆自己当时的答题思路。很多候选人笔试分数很高但面试被问到自己写的答案时支支吾吾反而暴露出是背出来的。面试回答问题时还有个技巧表达要分两层先讲结论再展开细节。比如被问“什么是离屏渲染”先说“离屏渲染是指图层在未用于显示的情况下先在离屏缓冲区进行渲染”再说“触发场景包括圆角加masksToBounds、阴影、遮罩等”最后补一句“在TableView滚动时要尽量减少会触发离屏渲染的属性”。这套逻辑任何技术问题都适用笔试里也是同样的结构。说实话我自己也做过不少此类笔试题的阅卷工作。比起答案的完美程度我更在意一个候选人在题目面前表现出来的技术直觉。iOS开发这个领域知识更新很快今天背的API明天可能就废弃了但内存管理、运行时、并发、网络这些底层机制十年内不会过时。准备笔试最有效的方式从来不是去背某一份卷子的标准答案而是把每一个高频考点背后的代码写一遍、跑一遍、断点看一遍。如果你现在手头正好有几道题没搞懂别急着搜答案先自己动手把Demo写出来踩过坑之后面试官问什么都难不倒你。
返回列表