公司动态

2015搜狗iOS笔试真题解析:从内存管理到Block的核心考点

📅 2026/8/29 5:07:04
2015搜狗iOS笔试真题解析:从内存管理到Block的核心考点
说实话我到现在都记得2015年那年秋天刷搜狗iOS工程师笔试题的场景。那会儿iOS开发正处在一个“ARC已经普及但面试必考MRC原理”的拧巴阶段题目的风格也特别能反映那个年代既有对Objective-C语言细节的极致抠挖又有对内存管理、运行时机制的多角度轰炸最后还要手写算法题。今天把这份经典笔试的考点拆一遍结合我后来面试别人和被别人面试的经验给准备iOS面试的朋友一份能直接用的参考。这份内容适合谁看如果你是正在准备大厂iOS岗位面试的开发者或者带团队做技术招聘又或者单纯想看看2015年移动端工程师的门槛到底有多高都可以往下读。我按当年的题型分布和考察逻辑把每个考点背后“公司到底想考什么”这件事说透。1. 2015年搜狗iOS笔试这道题为什么值得重新翻出来讲1.1 当年的技术语境ARC、iOS 9与刚满一岁的Swift2015年是iOS开发史上的一个关键节点。iOS 9在当年9月正式推送iPhone 6s带着3D Touch登场Swift 1.2到2.0的过渡让不少人开始把目光投向这门新语言但绝大多数商业项目的主力依然是Objective-C。ARC虽然从iOS 5就开始推广到2015年已经成为新建项目的默认选择但很多老代码、第三方库还是MRC时代遗留的写法所以内存管理的底层原理依然是笔试必考区。搜狗这个时间点招iOS工程师背景是手机输入法和搜索业务在移动端打得正凶。输入法这种产品对性能敏感度极高键盘弹出要跟手联想词计算不能在主线程卡顿内存占用要克制。所以这家公司的笔试题特别看重三件事语言底层功底、内存与多线程的实战能力、基础算法的扎实程度。这其实也解释了为什么题目里大量出现引用计数、Block、GCD这类细节题——因为这些直接关系到输入法这种高频交互App的上线质量。1.2 互联网公司笔试题的通用套路基础原理决定上限2015年那批互联网公司的笔试风格高度相似搜狗并不是个例。题型通常是“单选多选简答一道编程题”前边的选择题拼的是知识面广度和精确度简答题看你有没有真正理解机制而不是背概念最后一道编程题则直接暴露工程能力。有意思的是很多人刷题时只关注“这道题答案是什么”但实际上面试官批卷时看的是另一种东西面对不熟悉的知识点时你是靠记忆硬背还是能推演出来。例如内存管理里weak和assign的区别如果你只是记住“weak用于对象assign用于基本类型”那题目换个问法就懵了如果你理解weak在运行时维护了一个SideTable里的弱引用表对象销毁时会自动置nil那不管怎么考都能接住。这也是我在下面几节里反复强调原理的原因。2. 高频选择题深度拆解内存管理与Objective-C核心机制2.1 MRC遗留题引用计数和autorelease的陷阱2015年的笔试题里MRC手动引用计数几乎是必考区。搜狗的题目不会直接问你“什么是引用计数”而是给一段代码让你判断某个对象在哪个时点被释放。典型考法在MRC环境下执行以下代码后obj对象的引用计数是多少这种题的坑点在于autorelease。很多人以为autorelease就是“自动释放”实际它的机制是延迟释放对象被注册到当前的autorelease pool里等到pool被销毁时才统一发送release消息。所以如果你在方法内部创建对象并调用autorelease再返回给外部调用方如果把返回值retain住对象就活下来了如果没有等pool drain的时候对象就没了。这里给一个实操判断口诀alloc/new/copy/mutableCopy产生的对象引用计数为1需要自己release其他方式如类工厂方法产生的对象通常已经autorelease过你不该再release它若想持有要么retain要么用strong属性。这套逻辑现在ARC下已经不用手写但理解它仍然重要因为很多Block、NSTimer、CF桥接导致的内存泄漏问题本质上还是看引用计数谁加谁减。2.2 copy与strong、weak与assign一个都不能错搜狗选择题里特别喜欢出修饰符对比题表面考语法实际考你懂不懂属性和对象所有权。最经典的对比是copy和strong。先说结论NSString、NSArray、NSDictionary这几种有可变子类的容器类型属性一般声明为copy而不是strong。原因很反直觉你用strong把一个NSMutableString赋给一个NSString属性这个属性的类型虽然是NSString但实际指向的对象还是可变类型。如果赋值之后原始可变字符串被修改了这个“不可变”属性的值也跟着变了这是典型的隐式Bug。代码示意如下property (nonatomic, copy) NSString *name; property (nonatomic, strong) NSString *nameStrong; NSMutableString *mStr [NSMutableString stringWithString:sogou]; self.name mStr; self.nameStrong mStr; [mStr appendString:-input]; // self.name还是sogouself.nameStrong变成了sogou-input这个例子我在面试中用过无数次能一次讲清楚的人基本对属性修饰符是理解了而不是背了。再来看weak和assign。两者都不会增加引用计数区别在于assign用于基本数据类型对象释放后指针依然存在会成为野指针weak用于对象类型对象释放后运行时会把指针自动置nil不会出现野指针。2015年的题目还会追问一个点weak的实现这就引出SideTable和NSObject的dealloc流程后面第三节讲KVO时再展开。2.3 Block的坑变量捕获和多选题失分重灾区Block在2015年的笔试里基本是“失分重灾区”因为选择题只要涉及Block经常是多个正确选项漏选多选都不得分。搜狗这类公司喜欢在Block上出题是因为它有语言特性和内存管理两个维度的考点。先看变量捕获Block能捕获外部变量但默认捕获的是const值拷贝所以在Block内部无法直接修改局部变量。如果要修改需要给变量加__block修饰编译器会把这个变量包装成一个结构体Block捕获的是结构体的指针这样才能修改原变量。这个机制还有一层应用就是循环引用。当对象持有BlockBlock又捕获self时会形成环。解决方案是使用__weak typeof(self) weakSelf self;。2015年的题目到这一步为止但今天面试还会追问为什么用了weakSelf有时还要在Block内部再strongSelf因为异步Block执行过程中self可能被提前释放用strongSelf可以保证Block执行期间self活着执行完自动释放。3. 简答题与代码输出题KVO、Runloop与GCD死锁3.1 KVO的实现原理从isa-swizzling说起KVOKey-Value Observing是搜狗笔试简答题的常客而且问法很直接简述iOS中KVO的实现原理。标准答题框架是这样的当你对一个对象注册KVO监听时系统会在运行时动态创建一个该对象所属类的子类比如NSObject的私有子类NSKVONotifying_XXX然后把对象的isa指针指向这个子类。这个子类重写了被观察属性的setter方法在setter里先调用willChangeValueForKey:再调用父类的setter最后调用didChangeValueForKey:从而触发监听回调。这个机制里最容易被追问的点是KVO触发的必要条件。很多人以为只要改属性的值就会触发实际只有通过setter方法修改属性才能触发——直接修改成员变量_ivar xxx不会。另一个坑是把属性赋值成同一个值哪怕对象内容没变化setter还是会走KVO依然会触发因为setter不管新值是否等于旧值都会照常发通知。这两点在用KVO做UI联动时经常踩雷。3.2 Runloop在iOS里的实际作用别只会背概念搜狗2015年的简答题里有道Runloop相关的题说明Runloop在iOS开发中的主要应用场景。考这个原因很简单输入法、搜索框这类高频交互场景都依赖对事件循环的理解。Runloop本质是一个事件循环让线程在没有事件时休眠、有事件时唤醒处理。iOS开发中最常打交道的场景有这几个第一NSTimer靠Runloop才能触发而且Timer在滑动列表时会被Runloop的Tracking模式阻塞导致定时器不按预期触发。解决方案是把Timer加入NSRunLoopCommonModes这样滑动时也能正常回调。第二PerformSelector相关方法依赖Runloop的当前模式。第三AutoreleasePool的释放节点在Runloop每次循环结束时这也是为什么主线程中大量临时对象不会导致内存暴涨的原因。第四常驻线程的实现就是为了不销毁线程、让Runloop跑起来接收事件。笔试答题时我建议不要只列概念要结合具体场景描述比如“滑动TableView时如果Timer时间不准确你怎么排查”把深入原理的思考过程写进去比堆名词得分高得多。3.3 GCD主线程死锁题一眼看穿的关键思路GCD在2015年笔试题里最常见的坑是主线程同步死锁。代码大概是这样的dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(hello sogou); });这道题考的就是在主线程执行dispatch_sync到主线程队列时会发生什么。答案是死锁。因为dispatch_sync会阻塞当前线程等待Block执行完毕而Block又排在主线程队列里等待执行而主线程现在正被阻塞着没法去执行队列里的Block形成互相等待。理解的关键在于“同步提交”的本质是“当前线程等待”而“主线程队列”的本质是“所有任务都在主线程串行执行”。当等待者和被等待者都在同一个线程时必然死锁。当年的简答题还喜欢结合场景App启动时你在主线程调用了一个dispatch_sync到主队列的代码会卡多久答案不是“卡一会”而是直接死锁崩溃。今天再考多线程往往换成异步并发、线程安全、数据竞争这些更复杂的问题但核心判断思路没变先看有没有“等待”再看等待的目标是否也需要当前线程去执行。4. 编程题实战从算法到代码实现的完整思路4.1 一道高频手写题单链表反转的三种写法搜狗2015年的编程题我见过好几个版本的回忆最稳的一道是“反转单链表”。这题在业界出现频率极高因为它能同时考出链表基本功、指针操作和理解能力。先给最常写的迭代方法typedef struct ListNode { int val; struct ListNode *next; } ListNode; ListNode *reverseList(ListNode *head) { ListNode *prev NULL; ListNode *cur head; while (cur ! NULL) { ListNode *next cur-next; cur-next prev; prev cur; cur next; } return prev; }关键点在于保存next指针。很多人第一次写会漏掉“先存当前节点的下一个节点”这一步导致链表在后移时断掉。还有一种递归写法更简洁但需要想清楚递归的终止条件是head为空或者head-next为空返回值是反转后的新头在递归返回的过程中把当前节点的下一个节点的next指向当前节点当前节点的next置空。笔试时如果时间充足建议写完代码再手动跑一遍。比如原链表1→2→3迭代过程prev、cur、next是怎么变化的跑一遍能发现指针操作是否有误。我在批改笔试题时看到有人用栈先把所有节点压栈再弹出重建链表也能实现但一般会被追问“额外空间复杂度是多少”——如果答O(n)那这道题就扣分了因为标准答案要求O(1)额外空间。4.2 字符串处理的边界问题手写代码最容易翻车的点笔试题里字符串处理也常出现比如“实现一个函数把字符串中的空格替换成%20”。这题在C语言和Objective-C里解法不同但考察点是同一个你是不是只想到了从头到尾遍历忽略了移动字符的高成本以及边界条件处理。最优解法是先遍历一遍字符串统计空格数量计算替换后字符串的总长度然后从后往前遍历替换。这样每个字符只需移动一次时间复杂度O(n)空间复杂度O(1)在字符数组原地操作的前提下。手写时最常犯的三个错一是没有给字符数组预留足够空间替换后字符串变长导致越界二是从前往后遍历时替换后的新内容覆盖了还没处理的原内容三是忘了结尾的\0字符串结束符。这三个坑我在面试现场见过无数次能避开的候选人基本说明有工程经验。4.3 写在代码旁边的注释和思考过程2015年搜狗的笔试编程题官方不一定要求写注释但我在看了大量卷子后有个体会代码旁边画点示意图或写几行思路对评分非常有利。因为批卷人需要的不是你写出标准答案而是判断你的思考方式。我自己的做法是先写解题思路一两句话再写时间复杂度分析最后写代码。比如链表反转我会先写“用三个指针prev、cur、next先存后指最后返回prev”。这个习惯在工作后的Code Review里也非常受用因为它逼着自己在动手前先想清楚边界条件和终止条件。5. 从2015到今天的面试变迁哪些变了哪些一直没变5.1 题型演进Swift、SwiftUI、跨平台能力的加入今天的iOS面试和2015年相比变化非常明显。Swift已经是主流面试题里大量出现可选型、泛型、Protocol Oriented的内容UI部分越来越多地问SwiftUI的声明式布局和数据流而不是纯Storyboard和frame布局跨平台框架React Native、Flutter、鸿蒙也成了高频话题尤其现在很多公司移动端岗位要求“iOS为主具备跨端经验更佳”。但是——下面这点很关键——基础原理的考察权重并没有下降。ARC虽然不用手写了但被问“autorelease在ARC下还存在吗”依然能考倒一片人答案是存在只是编译器帮你插入了合适的release和autorelease调用Runloop依然会在性能优化题里出现Block、KVO、GCD也依然是面试题库的常青树。可以说2015年那份笔试题剔除掉具体语言语法后70%的知识点在今天依然有效。5.2 现在的面试官更看重什么工程思维和排查经验对比来看2015年的笔试题以“知识验证型”为主今天则明显偏向“问题解决型”。现在的面试官更愿意问“线上出现卡顿你会怎么排查”“App崩溃率突然升高你怎么定位”而不是“GCD的作用是什么”。这种变化反映了行业对工程师要求的提升不再满足于会用而要能定位问题、设计解决方案、评估方案代价。所以如果你现在要准备面试我的建议很直接把2015年这类的经典笔试题当基础练习但不要止步于此。做完一道内存管理题想一想它在实际项目里会表现为什么Bug做完一道GCD死锁题想一想如果线上遇到主线程卡死你手里的工具Instruments的Time Profiler、线程堆栈该怎么用做完一道链表题想一想它和App开发里的数据流动有什么关系。把“刷题”升级成“理解”才是应对面试变化最稳的路。6. 备考搜狗这类经典笔试题的几个实用建议过了这么多年如果让我给准备笔试的朋友提炼几个可落地的建议我会说下面几条。第一条是把往年真题当“考点地图”而不是“背诵材料”。搜狗2015年这套题最值钱的地方在于它清晰地划出了iOS客户端开发的知识边界——语言特性、内存管理、多线程、运行时机制、基础算法。你按这个地图去系统过一遍远比零散刷几百道面试题高效。我当年就是照着这个框架把《Objective-C高级编程》里关于Block和GCD的部分啃了两遍后来笔试遇到相关题基本很稳。第二条是练手写代码时一定要控制在白纸或纯文本编辑器里完成。2015年笔试还是纸质试卷现在多数是线上IDE或共享文档但手写和IDE自动补全带来的体验完全不同。我建议每周至少在纯文本环境里完整写三道链表、数组、字符串相关的题练到肌肉记忆的程度。面试考场上那种紧张状态下能流畅写出来的代码才是你真实水平的反映。第三条是重视“说出你的思考过程”。笔试改卷和面试口头考察最大的区别是你写下答案的过程就是暴露思维过程的过程。如果一道题你不会尽量写上你的切入点或者部分正确的思路不要空着。我在批改时见过很多写法很乱但思路正确的卷子反而比写了标准答案却没有任何解释的卷子更让我愿意给高分。第四条是关于错题整理。把每个错题背后的知识点归类定期回看。iOS知识点是网状的而不是线性的——KVO和内存管理有关Block和循环引用有关Runloop和Timer、AutoreleasePool都有关。把错题按“相关知识点”连接起来才能把这些网状结构内化成你自己的知识体系。说到底搜狗2015年这份iOS工程师笔试题之所以过了这么多年还有人翻出来讨论是因为它精准地踩中了客户端工程师最核心的能力区。技术栈可以过时语言可以更替但“理解原理、重视边界、尊重工程实践”这几件事在任何时代都是区分优秀开发者和平庸开发者的分水岭。我自己在带团队时也依然会用类似的知识点去考察候选人——只是换了更新的外衣而已。