恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
迅雷校招iOS笔试复盘:从OC底层到Runtime的备考指南
首页
资讯中心
/
迅雷校招iOS笔试复盘:从OC底层到Runtime的备考指南
迅雷校招iOS笔试复盘:从OC底层到Runtime的备考指南
发布时间:2026/8/31 20:14:31
2018年那个秋天迅雷的校招笔试邀请发到我邮箱时我正蹲在实验室里翻Objective-C的Property源码。当时iOS岗在线笔试基本还是OC的天下Swift 4刚推出不久卷子上那些读代码题清一色是block、runtime、内存管理这几个老面孔。迅雷校招iOS在线笔试B卷难度在当时的互联网校招里属于中等偏上题型和大厂没有本质区别客观选择题、手写编程题、再加一两道“设计一个组件”的开放题。今天不打算去复述某场考试的原题而是想把这类型笔试背后真正想考察的知识体系拆开结合我当年的备考经历给准备走iOS开发这条路的同学做一份全景复盘。无论你是正在投简历的应届生还是想补基础的转行党这篇文章都能帮你少走不少弯路。1. 一场校招笔试到底在考什么1.1 考卷结构从40分客观题到30分设计题校招笔试不是面面俱到地考你背了多少API而是用一份时间有限的卷子快速筛出两类人一类是基础扎实、能理解底层逻辑的另一类是平时只对着Interface Builder拖控件、面试前临时背两天题目的。2018年那会儿移动开发行业已经进入“会写界面不稀缺、能解决性能问题才值钱”的阶段所以卷面里iOS相关知识点考得非常密集尤其在Objective-C语言本身的底层机制上。从当年各个渠道汇总的笔试回忆来看典型B卷的比例大致是这样的客观选择题约40%OC语言、Runtime、内存管理、Foundation框架、多线程、网络。手写编程题约30%算法题或者简单iOS代码题有些要求在在线编辑器里补全代码。开放设计题约30%描述问题排查思路、设计图片缓存、分析TableView卡顿原因等。这种分布即使在今天依然有参考价值因为考点背后的判断逻辑没变基础扎不扎实、代码写不写得出来、遇到问题能不能系统性思考。1.2 答题节奏先写代码还是先做选择在线笔试系统一般会给60到90分钟。我的建议是拿到卷子先花5分钟把全部题目扫一遍标记每道题的大致耗时而不是直接埋头做第一道选择题。这样做的原因是手写代码题最容易出现“看懂了但写不完”的情况而开放题往往需要组织语言如果没有时间预算很容易草草两句话就提交了。我自己的时间分配方案是这样的前5分钟整体扫题给每道题标一个预计耗时。编程题优先写每道控制在15分钟以内先把核心思路和代码骨架写出来。客观题控制在25分钟以内遇到卡壳的先标记不恋战。最后20分钟留给开放题和检查开放题先写关键词再补充细节。不要在一道选择题上死磕太久。很多在线笔试系统交卷后不能回头修改所以做完一个题型就及时把答案填好避免最后慌乱中交白卷。踩过的坑都知道有些题目不是不会而是因为时间分配出问题反而把能拿的分丢掉了。2. 客观题里的高频考点OC语言与内存管理2.1 Objective-C的本质对象、isa指针、消息传递笔试选择题里特别喜欢问“Objective-C对象本质”这一类的题目。比如给你一段代码问NSObject实例底层到底长什么样答案的核心就落在isa指针上。每一个OC对象都可以看作一个结构体结构体第一个成员就是isa它指向这个对象的类对象类对象里存放着方法列表、属性列表、协议列表等信息。运行时通过isa才能确定一个对象能响应哪些方法。这里最关键的是要理解消息机制。在OC里调用方法并不是直接跳到函数地址而是给对象发送一条消息。比如[self doSomething]这行代码编译之后会转换成objc_msgSend(self, selector(doSomething))。运行时沿着isa找到类再在方法列表里查找对应的SEL找到就调用IMP找不到就进入消息转发流程。当年笔试很喜欢考这一类题某个对象调用了一个不存在的方法会发生什么答案不是简单一句“崩溃”而是要经过resolveInstanceMethod:、forwardingTargetForSelector:、methodSignatureForSelector:/forwardInvocation:这几个阶段。只有这些阶段都没有兜住最后才会触发doesNotRecognizeSelector:然后才crash。很多人做错就是只记住了“找不到方法就崩溃”丢掉了中间那一整段转发细节。2.2 属性修饰符copy、strong、weak、assign到底怎么选应届生做属性修饰符的题最容易折在copy和strong上。NSString、NSArray、NSDictionary这些有可变子类的类型属性通常建议用copy。原因在于copy可以把外部可能传进来的NSMutableString复制一份避免外部后续修改字符串内容时你的属性值被意外改动。你可以这样理解别人传给你一个可变对象你想保存的是当前内容的快照而不是一个随时可能在别处被改掉的引用。strong是持有并增加引用计数但持有的是同一个可变对象之后对象一变你的属性也跟着变很容易引入隐蔽的bug。block属性使用copy也是老生常谈。ARC下编译器会自动处理block的复制但很多老工程为了规范和兼容历史代码仍然会显式写上copy。weak和assign的区别也要说清楚assign一般用于基本数据类型weak用于对象且不持有对象被释放后weak会自动置为nil。如果assign修饰的是对象对象释放后指针不会置nil再访问就成了野指针这是崩溃的高发原因。atomic与nonatomic这里有个经典误区。很多人以为atomic就代表线程安全了实际上它只能保证getter/setter的原子性不保证复合操作的安全。你两个线程同时给一个NSMutableArray添加元素即使这个数组属性是atomic的也一样会因为线程竞争出问题。所以如果笔试里问“atomic能保证线程安全吗”应该把边界说清楚它保护的只是单次读取或单次写入不是整个业务逻辑。2.3 Block与循环引用一考一个准Block是当年笔试的常客因为它能同时考语言特性和内存管理。Block可以捕获外部变量本质上是一个带有捕获变量的函数对象。按存储位置可以分成三种全局Block、栈Block、堆Block。全局Block不捕获外部变量栈Block在栈上堆Block在堆上ARC下大部分Block会被自动copy到堆上。底层题目经常会问某段代码里block是什么类型这个判断能力要具备。循环引用的经典场景是self持有blockblock内部又引用了self。因为block捕获self时默认是强引用于是形成一个环谁都没法释放。解决办法是外面用__weak typeof(self) weakSelf self在block内部再用__strong typeof(weakSelf) strongSelf weakSelf保证block执行期间self不会提前释放。前者是防止循环引用后者是防止执行到一半时self已经被释放。这两个修饰符配合使用的写法笔试和面试官都特别喜欢追问值得认真理解。提示你可能会问block内部为什么还要先用weak再转strong直接用strong不是又循环引用了这里要区分清楚strongSelf是block执行期间的一个局部强引用block执行完就释放不存在持有周期问题它只保证当前这一次调用的安全并不会破坏用weak打破的循环引用结构。3. Runtime与底层机制真正拉开分差的地方3.1 ARC下的引用计数与弱引用实现ARC不是垃圾回收它仍然是引用计数只不过编译器在合适的时机帮你插入retain、release代码。你需要清楚对象什么时候被持有、什么时候被释放否则排查内存问题时依然会一头雾水。weak的实现机制是面试里最常追问的点系统维护一个全局的weak表以对象地址为keyvalue是对应所有弱引用指针地址的数组。对象dealloc时系统根据这个对象的地址找到数组把这些weak指针全部置为nil所以弱引用不会产生野指针。autoreleasepool也要有清晰认知。主线程的RunLoop每次迭代会自动创建和释放自动释放池但如果你在循环里创建了大量临时对象这些对象可能都会被加入同一个池子导致内存峰值变高。比较典型的场景是处理大量图片或字符串时可以在循环内部手动添加autoreleasepool块让临时对象尽早释放。这不仅是笔试考点也是实际优化中常见的处理方式。3.2 Runtime三件套关联对象、方法交换、动态方法Runtime是所有iOS底层题目的重头戏。关联对象属于“给一个已存在的类在不修改源码的情况下挂属性”的能力底层是通过AssociationsHashMap实现的。笔试里如果问关联对象是不是真正塞到类里变成成员变量了答案是否定的它只是在一个全局map中以对象指针为key存了值、策略和持有关系。因为关联对象不受正常dealloc流程管理释放时机和普通成员变量不同所以使用时要格外注意内存管理策略。方法交换也就是Method Swizzling考到的概率非常高。交换之后两个方法的IMP就互换了。常见的问题是交换方法应该在什么时候做答案是load方法里并且要用dispatch_once保护确保只执行一次。还要注意父类方法不能随便交换否则子类行为会变得不可预测也不要重复交换同一个方法否则可能出现递归调用死循环。这些细节在做动态埋点、AOP框架时特别重要。动态添加方法的题一般是问“如果一个对象收到未知方法会怎么样”。除了走消息转发流程你还可以在resolveInstanceMethod:里用class_addMethod动态添加实现。需要熟悉class_addMethod的参数含义cls、sel、imp、types。其中types是方法签名用编码字符串表示参数和返回类型这曾经在热修复方案里非常流行。3.3 KVO与KVC理解黑魔法的边界KVO是iOS面试里绕不开的内容常考的是底层原理。系统在第一次对某个对象的属性添加观察者时会动态创建一个子类名字通常带有NSKVONotifying_前缀然后把原对象的isa指针指向这个子类。子类内部重写setter在设置新值前后调用willChangeValueForKey:和didChangeValueForKey:发送通知。这就是为什么直接修改成员变量不触发KVO而通过setter修改才会触发。遇到“KVO没响应”的问题最先排查的就是有没有走setter方法。KVC的查找规则也要记清楚。调用setValue:forKey:时系统会先找setKey:方法找不到再看有没有_key或key的实例变量再不行就调用setValue:forUndefinedKey:如果这个方法也没重写就会崩。取值顺序类似先找key、isKey、_key等方法再看实例变量。这套规则在笔试题里经常以读代码的形式出现让你判断某行代码会不会crash没背熟很容易选错。4. 手写编程题与开放题的拿分思路4.1 手写单例别只写一个sharedInstance单例可能是手写题里出现频率最高的一道。标准答案是dispatch_once版本但只写一个sharedInstance只能拿基础分。面试官真正想看的是你会不会把“唯一实例”这件事做严谨比如在ARC下重写allocWithZone:、copyWithZone:、mutableCopyWithZone:防止别人通过alloc或copy再创建出第二个实例。虽然业务开发中不一定要求这么严格但笔试里写到这个层面至少说明你理解单例的本质。实现时要注意线程安全。dispatch_once能保证初始化阶段只执行一次而且性能最高。不要为了省事用synchronized来做初始化那会让所有访问单例的线程都去抢同一把锁效率很差。下面是当年比较标准的一份写法interface APIManager : NSObject (instancetype)sharedInstance; end implementation APIManager (instancetype)sharedInstance { static APIManager *instance nil; static dispatch_once_t onceToken; dispatch_once(onceToken, ^{ instance [[APIManager alloc] init]; }); return instance; } (instancetype)allocWithZone:(struct _NSZone *)zone { static dispatch_once_t onceToken; dispatch_once(onceToken, ^{ instance [super allocWithZone:zone]; }); return instance; } - (id)copyWithZone:(NSZone *)zone { return instance; } - (id)mutableCopyWithZone:(NSZone *)zone { return instance; } end这里需要说明的是实例变量需要声明成静态变量并配合allocWithZone:里的dispatch_once才能保证alloc路径也被拦截。4.2 算法题数据结构基本功得分不靠背题在线笔试的算法题通常不会特别偏但也不会简单地让你写个冒泡排序。比较常见的方向有字符串反转、链表反转、数组去重、二分查找、二叉树层次遍历、最大子序和等。以2018年前后的难度来看LeetCode的简单到中等题基本够用关键是能在在线编辑器里写出能跑、边界条件正确的代码而不是只会背题。拿反转链表举例核心思路是三个指针prev、curr、next遍历时不断改变next指向。很多人在纸上画得明白一写到代码就漏了边界比如链表为空、只有一个节点的情况。如果题目要求时间复杂度O(n)空间复杂度O(1)你得在代码或注释里体现出来。在线判题系统对代码正确性要求很高别以为只写思路不写代码也行有些卷子的编程题必须全部跑通才能拿分。还有一种策略是用运行时间和空间来换正确性。在线笔试环境里正确性永远比炫技重要。先写一个能过的朴素解法再考虑优化。不要一开始就挑战最优解结果边界条件没处理好最后连基础分都丢了。我当年见过不少同学栽在这个心态上。4.3 开放题设计图片缓存库怎么答才像老手开放题一般不会问“你背过哪个第三方库”而是问“你会怎么设计”。最经典的题目是设计一个图片缓存库。这个题目的最佳切入点其实是SDWebImage的架构但你不用提库名直接讲设计思路就行。回答时最好分层来讲。第一层是内存缓存用NSCache而不是NSMutableDictionary因为NSCache支持自动淘汰、线程安全在内存紧张时会自动释放对象这是很重要的优势。第二层是磁盘缓存负责持久化常见做法是把图片数据写到沙盒的Caches目录里文件名不建议直接用URL字符串因为URL里有斜杠和其他特殊字符最好用MD5或哈希值做文件名。第三层是网络下载用NSURLSession发请求下载前先查内存缓存和磁盘缓存命中就直接返回避免重复下载。还有一个加分的细节是并发控制。同一个URL出现在多个TableViewCell里如果每个cell都发起一次网络请求既浪费流量又卡界面。可以考虑用一个以URL为标识的下载管理容器相同URL的下载任务只发起一次所有等待的调用方通过回调或通知接收结果。另外图片解压和渲染也很关键大图在主线程解码会掉帧一般放到子线程处理。这个点在性能优化类开放题里非常加分。5. 高频易错细节与备考点滴5.1 一份容易丢分的细节速查表把平时收集的高频送命题整理成速查表方便临考前再看一眼考点常见错误正确理解weak与assign认为两个一样weak对象释放后自动置nil用于对象assign多用于基本类型copy语义以为copy一定产生新对象不可变对象调用copy是浅拷贝可变对象调用copy是深拷贝mutableCopy一定是深拷贝消息转发找不到方法立即崩溃先进入resolveInstanceMethod等转发流程全部未处理才崩溃block属性用assign修饰应该用copy或strong保证block从栈上搬到堆上NSInteger以为固定4字节64位下为8字节与int混用时要注意溢出问题深拷贝浅拷贝以为copyWithZone都是深拷贝要看具体类是否实现了NSCopying协议及协议行为5.2 笔试后的面试延伸准备笔试通过之后真正的挑战才刚开始。面试官通常会把笔试错题拿出来追问同时围绕简历上的项目深挖。比如简历里写过用了SDWebImage那就要能说清楚它的缓存策略和下载流程写过做过性能优化就得能说出Instrument里Leaks和Allocations怎么用遇到内存泄漏怎么定位。这个阶段不要疯狂背面试题多问自己“为什么这样写”和“如果不这样写会怎样”效果比什么资料都管用。我当时的做法是把笔试错题整理成一个文档每题后面补一段原理解释再用自己的话重新讲一遍。这个过程很痛苦但收获也最大。很多知识点当时以为自己懂了真正写下来才发现逻辑是断的。面试前两周把这些题反复过两遍回答问题时就顺多了。5.3 备考时间安排与资料参考如果目标是互联网大厂的iOS开发岗备考时间建议安排在两个月左右。第一个月把Objective-C语言基础和底层原理过一遍包括消息机制、Runtime、内存管理、多线程第二个月开始刷算法题、复盘笔试错题同时投简历。刷题频率建议每天两到三道LeetCode保持手感和对边界条件的敏感度。资料方面Objective-C方向看《Objective-C高级编程iOS与OS X多线程和内存管理》和《Effective Objective-C 2.0》就够了这两本覆盖面很稳。如果当时你已经开始写SwiftApple官方文档和WWDC Session也是值得长期跟的内容。我对“2018迅雷校园招聘iOS在线笔试B卷”这类回忆版题目总体上是有好感的它们像一面镜子照出的是某一个时间段里行业对iOS工程师的普遍期望。现在偶尔帮学弟学妹辅导时我常说不要迷信题库要把题目背后的原理拆开。这张卷子考的东西到现在依然有效因为苹果再怎么改API对象、引用计数、消息机制、Runtime这些底座不会变。笔试真正的意义不是让你把题目背下来而是逼你把这套底层逻辑彻底打通。我当年也是靠突击刷题换来面试机会但真正让我在后来写代码时不慌的反而是静下心把weak表和消息转发读明白的那几个下午。