ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

B站2020校招iOS笔试题解析:内存管理与多线程核心考点

B站2020校招iOS笔试题解析:内存管理与多线程核心考点 1. 试卷整体风格与考察逻辑拆解先聊点题外话。B站这套2020校招iOS笔试卷一在当年流传度相当高很多准备大厂iOS岗位的同学都拿它当模拟题刷。我后来也拿这份卷子给组里实习生做过摸底整体感受是它没有刻意追求偏题怪题而是把iOS日常开发中最常碰到的知识点、最容易踩的坑以及基本功里最能体现水平的内容揉进了同一张卷子里。这套试卷适合谁看我的判断是三类人第一类是正在准备秋招或春招的应届生需要用它来检验自己的知识体系是否完整第二类是工作一两年但基础不牢的初中级开发可以通过题目反查自己平时写代码时有没有真正理解底层原理第三类是负责校招出题或面试的工程师可以从题目设置中借鉴如何区分“背题型候选人”和“理解型候选人”。从出题风格看这份卷子有几个明显特征值得注意。首先是覆盖面广但重心集中UI布局、内存管理、多线程、网络、数据结构都有涉及但重心明显落在内存管理和多线程这两个iOS面试高频考点上这也是实际开发中最容易出问题的两个区域。其次是不少题目看起来考的是结论实际上考的是推导过程比如问到某个修饰符或某个API的行为时如果只背过结论而没理解底层机制很容易在变体问法中翻车。第三是算法题难度适中但非常看基本功不考冷门数据结构但很考验代码的严谨性和边界处理能力。1.1 出题重心一份卷子背后的技术栈分布我大致把这份卷子考察的内容分成了四块每一块对应到实际开发中的能力要求也不同。第一块是语言与内存管理基础包括 Objective-C 的内存管理机制、属性修饰符的语义差异、weak 与 strong 的本质区别、block 的底层结构和循环引用问题。这块内容占比最大也是区分度最高的部分。原因很简单iOS 开发中几乎所有的疑难 crash 和内存泄漏都源自对这块理解不透彻面试官通过几个小问题就能快速判断候选人是不是“只会调用 API 的搬运工”。第二块是 Runtime 与消息机制涉及 objc_msgSend 的查找流程、Method Swizzling 的原理与风险、KVO 的底层实现方式、isa 指针与类对象的关系等。这块考察的是对 Objective-C 动态特性的理解深度。说实话日常开发中很少有人会手写 Runtime 代码但理解这套机制对排查 bug、阅读第三方库源码、理解系统框架行为都有巨大帮助。B站考题里只要出现 Runtime 相关题目往往就能筛掉一批只停留在语法层面的候选人。第三块是多线程与并发重点在 GCD 的队列与任务组合、线程安全与数据竞争、死锁场景分析、信号量、栅栏函数等。这块贴近实战因为线上 App 的卡顿、crash、数据错乱很大一部分都跟多线程使用不当有关。考察多线程题目时面试官真正想知道的往往不是你是否背过 API而是遇到“多个线程同时写一个可变数组”这种真实问题时你会怎么处理。第四块是 UI 与架构设计包括 UIView 与 CALayer 的关系、事件响应链、Auto Layout 的性能问题、MVC/MVVM 的取舍等。这类题目看似简单但开放性很强能考察候选人是否具备全局思维而不仅仅是会写几个页面。1.2 应试视角这套题到底在筛选什么人站在应试者的角度我更建议大家把重心放在“题目背后的能力模型”上。B站作为内容型平台对 iOS 开发者的要求不只是能写完需求还要能处理复杂交互、优化视频播放体验、保证页面流畅度。因此试卷中出现的 UI 和性能相关题目本质上是在看你对用户体验有没有敏感度。另外这份卷子的选择题里有一些“略带陷阱”的选项比如把相近的修饰符或相近的 API 放在一起混淆。这类题的目的不是为难你而是考察你是否真正理解每个选项之间的差异。我见过不少候选人原理能讲得头头是道但一做选择题就容易在细节上丢分原因就是平时写代码太依赖编译器提示和自动补全缺少对关键字语义的精确记忆。还有一点对准备笔试很重要B站的技术栈虽然以 Objective-C 为主但 Swift 的内容也偶有涉及。建议备考时把 Swift 的基本语法和 Optional 机制过一遍不必太深但至少不能完全陌生。毕竟 2020 年前后正是混合开发的高峰期很多团队的老项目都是 Objective-C 与 Swift 混编状态。2. 高频基础知识题精讲内存管理与属性修饰符我一直觉得iOS 笔试里最不该丢分的就是内存管理相关的题目因为这是每个 iOS 开发者天天都在打交道的东西。但现实情况是这块恰恰是丢分重灾区。很多候选人知道“weak 不增加引用计数、strong 增加引用计数”但一到具体场景就懵了尤其是涉及 block、delegate、NSTimer 这些容易产生循环引用的对象时判断经常出错。2.1 weak、strong、copy、assign 的实际语义差异先来看一个非常经典的选择题变体声明一个 NSString 属性时用 copy 还是 strong这个问题的标准答案是 copy因为 NSString 可能被传入一个 NSMutableString 实例如果不做拷贝后续对可变字符串的修改会直接影响到属性值造成数据意外变化。但更深一层的问题是copy 对可变和不可变字符串的底层行为相同吗这里涉及到 copy 与 mutableCopy 的本质区别。对不可变对象执行 copy 默认是浅拷贝相当于 retain 一次因为对象本身不可变没必要新建实例对可变对象执行 copy 则一定会深拷贝。所以如果你声明的是 copy 类型的 NSString 属性从外部传入 NSMutableString 时系统自动执行深拷贝此时属性持有的是一个不可变副本后续无论外部如何修改原对象属性值都不受影响。这个机制我建议每个候选人都能完整讲出来而不是只回答“NSString 要用 copy”。再看 assign 和 weak 的区别这也是高频陷阱。assign 修饰的属性在对象被释放后指针不会自动置空继续访问就是野指针会造成不可预期的 crash。weak 修饰的属性在对象释放后由 runtime 自动将指针置为 nil访问时安全返回 nil。很多人以为“assign 用于基本数据类型、weak 用于对象类型”就是全部但真正的考点是为什么 assign 不能用于对象因为 assign 不参与引用计数管理也不能注册到 runtime 的 weak 表中对象释放后指针悬空这跟 weak 的核心差异在于“是否自动置 nil”。2.2 循环引用场景的典型表现与解题思路循环引用在笔试里几乎必考通常以“以下哪种情况会造成循环引用”或“如何解决循环引用”的形式出现。我总结了三类最典型的场景基本可以覆盖 90% 的考点。第一类是 block 循环引用。block 内部捕获 self而 self 又持有 block形成 self - block - self 的闭环。解决方案是使用 weakSelf即__weak typeof(self) weakSelf self在 block 内部使用 weakSelf。但如果 block 内部需要执行耗时操作操作完成后 weakSelf 可能已经被释放此时需要考虑在 block 开头__strong typeof(weakSelf) strongSelf weakSelf保住对象生命周期。这里有个容易被忽视的细节如果在 block 内直接使用 self即便 self 是通过 weakSelf 间接引用编译器也可能因为宏展开问题误报或不报建议在面试中讲清楚 weak-strong dance 的完整写法。第二类是 delegate 循环引用。很多初学者把 delegate 声明为 strong导致 vc - view - delegate(vc) 的闭环。标准做法是 delegate 使用 weak 修饰但要注意 delegate 是结构体类型时比如 iOS 17 的 Swift 版本weak 语义会略有不同笔试中一般不会深入到这里但知道更好。第三类是 NSTimer 循环引用。NSTimer 的 target 被 runloop 强持有而 timer 又被 self 持有形成 self - timer - runloop - self 的闭环。解决思路有三种在合适时机主动 invalidate 并置 nil使用 iOS 10 的 block 版本 timer 配合 weakSelf或者使用代理类中转。很多候选人只答出“要 invalidate”但没有解释清楚 invalidate 为什么能打破循环以及如果在 dealloc 里才 invalidate 实际上永远等不到 dealloc 调用这个死局这是面试官最喜欢追问的点。2.3 属性修饰符选择的三条实操建议除了答对题目我还想分享几条实际写代码时关于属性修饰符的经验这些在笔试中如果作为加分项说出来效果会很不错。建议一不可变集合NSArray、NSDictionary、NSSet属性统一用 copy。理由跟 NSString 一样防止外部传入可变集合后内容被篡改。而且 copy 对不可变集合本身没有额外开销只做指针拷贝安全性提升是免费的。建议二对外暴露的模型对象属性如果希望外部不能修改内部状态readonly 配合在 .m 文件中重新声明为 readwrite 是常见模式。笔试中如果要求设计一个模型类记得体现这个细节。建议三block 属性一律用 copy。虽然 ARC 下 strong 和 copy 对 block 来说效果几乎一样都是拷贝到堆上但语义上 copy 更准确也兼容 MRC 时代的写法。别在这种细节上丢印象分。3. Runtime 与消息机制从原理到应用场景Runtime 是 Objective-C 的灵魂也是大厂笔试卷里“拉开差距”的必考板块。B站这套题里如果出现 Runtime 相关题目通常不会直接问“什么是 Runtime”而是通过具体现象来考察候选人对动态机制的理解。比如给你一段 Method Swizzling 代码问你有没有问题或者问你 KVO 的实现原理。3.1 objc_msgSend 的查找流程与缓存机制完整讲一次 objc_msgSend 的执行流程是 iOS 面试的高频题。大致可以拆成四步。第一步检查 receiver 是否为 nil。Objective-C 允许向 nil 发消息返回 nil 或 0不会 crash。这是 Objective-C 比较人性化的设计也是很多诡异 bug 的来源——你向 nil 发消息没报错但数据也没了。第二步从 receiver 的 isa 指针找到类对象在类对象的 cache 中查找方法实现。cache 是哈希表结构如果命中直接调用。这里 macOS 和 iOS 上有 arm64 的优化路径但笔试阶段讲清楚“先查缓存、再查方法列表”就足够了。第三步如果 cache 未命中查找当前类的方法列表如果找不到就沿着 superclass 链逐级往上找直到 NSObject。如果最终没找到进入第四步。第四步触发消息转发流程依次调用 forwardingTargetForSelector 和 methodSignatureForSelector / forwardInvocation。如果转发也没处理最终调用 doesNotRecognizeSelector 抛出 unrecognized selector 异常。面试官常会追问什么是 isa 指针类对象的 isa 指向什么元类是什么这里我建议大家画一张图来理解instance 的 isa 指向 classclass 的 isa 指向 meta classmeta class 的 isa 指向根元类根元类的 isa 指向自己形成一个闭环。类对象的 superclass 指向父类元类的 superclass 也有一条独立的继承链。理解这张图才能在遇到“objc_getClass 和 object_getClass 的区别”这类题目时准确作答。3.2 Method Swizzling 的常见坑与正确写法Method Swizzling 几乎在每套 iOS 面试题中都会出现B站这套卷子也不例外。核心原理是通过 Runtime 的class_addMethod和method_exchangeImplementations把两个方法的实现指针交换从而在调用原方法时触发新方法。但考察的重点往往是“怎么用才是对的”。我见过不少候选人一上来就写method_exchangeImplementations交换两个方法完全忽略类簇、继承、线程安全等问题。正确的 Swizzling 姿势应该是这样先定义一个分类方法在load方法中执行交换load是类加载时调用的方法能保证交换只执行一次。使用dispatch_once确保线程安全。交换前先调用class_addMethod判断当前类是否已经有原方法的实现如果子类没有实现而是继承自父类直接交换会导致父类实现被修改引发全局影响。正确做法是先给当前类添加原方法的 IMP指向原实现再交换新方法。这里有个真实踩坑案例可以分享一下。我早年做无痕埋点方案时通过 Swizzle 了 UIViewController 的 viewWillAppear 来统一统计页面曝光当时没做 class_addMethod 判断结果发现所有继承自 UITableViewController 的子类页面都出现了重复曝光——因为 UITableViewController 没有单独实现 viewWillAppear交换的是父类 UIViewController 的实现导致每个子类都走了一遍被替换的逻辑。后来补上 class_addMethod 判断才解决。Method Swizzling 在笔试里如果作为简答题出现答案结构可以参考原理IMP 交换- 使用场景埋点、AOP、无痕统计- 注意事项load dispatch_once class_addMethod 判断 不影响父类。把这个结构背熟基本能拿全分。3.3 KVO 底层实现与手动触发KVO 的底层原理也是笔试常客。简单来说当一个对象注册了 KVO 监听后Runtime 会动态创建一个该类的子类命名为 NSKVONotifying_ 前缀拼接原类名然后将对象的 isa 指针指向这个子类。子类重写了被观察属性的 setter 方法在 setter 内部先调用willChangeValueForKey再调用父类的 setter最后调用didChangeValueForKey从而触发观察者的回调。这套机制衍生出三个很容易考的考点。考点一KVO 触发回调的时机。很多人以为只要属性值发生变化就会回调但实际上是只有在调用 KVO 兼容的 setter 方法时才会触发。直接修改成员变量_name xxx不会触发回调。使用setValue:forKey:会触发因为 KVC 内部调用了 setter。考点二手动触发 KVO。调用setValue:forKey:时系统内部的automaticallyNotifiesObserversForKey:如果返回 NO就不会自动发送通知这时需要开发者手动调用willChangeValueForKey和didChangeValueForKey。这个知识点在面试中经常出现我建议大家能写出手动触发的完整代码。考点三KVO 与 KVC 的关系。KVC 触发 KVO 的途径、KVC 查找 setter 的顺序先找setKey:再找_setKey最后找成员变量这些细节尽量都过一遍因为它们是理解整个动态机制的拼图。4. 多线程与并发经典场景与实战写法多线程题在笔试卷里属于“送分题与送命题并存”的类型。送分题是 GCD 基本 API 的使用送命题是死锁分析和资源竞争问题。我发现很多候选人对类似“为什么在主线程同步派发到主队列会死锁”这种问题原理讲不清楚只能说“会卡死”但追问底层机制就卡壳。4.1 GCD 队列与任务的组合关系全解析先梳理一个基础框架GCD 里有两种队列串行队列、并发队列和两种任务派发方式同步派发、异步派发组合起来有四种经典情况但实际考察的变体更多。第一组串行队列 同步派发在当前线程执行不会开启新线程按顺序执行。注意这里“当前线程”是调用者的线程如果调用者是主线程就在主线程执行。第二组串行队列 异步派发会开启新线程不一定是新线程系统线程池复用任务按顺序执行。这是 iOS 开发中常用的“异步串行”模式比如处理数据库读写时为了保证写顺序可以用串行队列 异步派发。第三组并发队列 同步派发在当前线程执行不开新线程但任务之间仍然是按顺序执行。这里有个容易混淆的点并发队列只是表示“如果异步派发可以并行”但同步派发依然会在当前线程逐个执行。第四组并发队列 异步派发开启多个线程任务并发执行。这是最常用的模式但带来的线程安全问题也最多。死锁的典型场景就是主队列 同步派发。主队列是串行队列任务必须按 FIFO 顺序执行主线程当前正在执行一个任务这个任务内部又同步地往主队列派发新任务新任务要等当前任务结束后才能开始但当前任务又在等新任务执行完成后才返回于是互相等待形成死锁。这个解释比单纯背结论要有说服力得多。4.2 线程安全与多线程写同一个可变对象笔试中有一类高频场景题多个线程同时往一个 NSMutableArray 里 addObject会发生什么这个问题的标准答案是会产生内存问题或崩溃因为 NSMutableArray 不是线程安全的。但只回答到这里其实不够面试官更想听到的是如何解决。解决办法从低级到高级有多种层次。最粗糙的解法是加锁使用 NSLock 或 synchronized保证同一时间只有一个线程操作数组。更精细的解法是使用串行队列做写入保护读操作可以并发写操作串行执行比如用 dispatch_barrier_async 实现多读单写。更现代的解法是使用系统提供的线程安全容器iOS 10 支持 NSMutableArray 的线程安全子类如用 atomic 属性包装或者直接把数据设计成不可变类型每次修改都生成新对象。从笔试答题的角度我建议大家按“问题 - 原因 - 方案 - 方案对比”的顺序组织答案。先说结论多线程同时写可变数组不安全可能 crash原因是数组内部的存储结构在扩容或调整时被多个线程同时修改造成数据竞争。然后给方案加锁、串行队列、栅栏函数、最终考虑用值类型代替可变引用类型。最后对比一下各种方案的优缺点锁的开销较小但容易死锁串行队列安全性高但会降低并发度barrier 是只读并发、写入独占的折中方案适合读多写少的场景。4.3 常考并发编程题目的一份参考实现很多校招笔试卷里喜欢出一道“手写多线程相关代码”的题考察候选人能不能写出线程安全、功能完整的代码。我印象比较深的是类似“实现一个线程安全的单例”或者“用 GCD 实现多线程同步”。线程安全单例的核心代码我给大家梳理一遍看清楚几个关键点。使用 dispatch_once 是推荐写法因为 GCD 保证 block 只会执行一次且线程安全。早期很多老代码会使用 synchronized 加锁判断但因为锁的开销和重复初始化问题已经不建议新代码使用。Swift 中可以使用 static let 或者 struct 的静态属性来保证线程安全利用语言层面的 let 不可变特性。另一道常考题是“多个并发任务完成后回到主线程执行一个汇总任务”。这可以直接用 dispatch_group。先创建 group然后多个任务异步进入 group等所有任务完成后调用dispatch_group_notify在指定队列执行汇总逻辑。如果想耐心等待完成再执行可以调用dispatch_group_wait但注意它会阻塞当前线程如果是在主线程调用就造成了同步等待页面会卡住所以一般推荐 notify。5. 算法与代码题思路简洁应试与边界处理B站这套笔试卷的编程题难度适中不会让你现场写红黑树但要求代码简洁、逻辑严谨。这里提醒一个高手习惯笔试时先确认输入范围、边界条件再动笔写代码。很多人一上来就写循环结果边界判断漏了代码逻辑对但分数不高因为用例过不了。5.1 链表类题目的核心套路链表题是校招笔试最常见的算法题没有之一。B站这套卷子如果涉及算法题链表反转、链表是否有环、合并两个有序链表、找中间节点都是高频考点。这类题目的核心套路是对指针和边界条件的掌控。以“反转链表”为例三种解法中迭代法最推荐笔试时使用因为空间复杂度 O(1) 且不容易出错。核心逻辑是维护 prev、curr、next 三个指针循环中先把 next 保存下来再把 curr.next 指向 prev随后三个指针整体后移。很多候选人写的时候容易忘记保存 next导致链表断开后就找不回后面的节点了。如果笔试环境允许建议先画一个小链表在草稿上推演指针变化再落到代码上。处理链表题还有一个我自己很受用的小技巧让一个 dummy 节点指向头节点这样在链表头部插入或删除时不需要单独处理头节点为空的特殊逻辑代码会简洁很多。比如“删除链表中所有值等于 val 的节点”这道题用 dummy 节点配合一个 prev 指针遍历逻辑非常清晰。5.2 LRU 缓存的设计与实现LRU 缓存是笔试编程题中区分度较高的一道题。设计一个 LRU Cache要求 get 和 put 都是 O(1) 时间复杂度。这个题的核心是哈希表 双向链表哈希表提供 O(1) 的查找双向链表保证 O(1) 的节点删除和移动。如果只让说数据结构而不让写代码也要能解释清楚“为什么用双向链表而不是单链表”——因为删除一个节点时单链表需要知道前置节点如果只给当前节点无法在 O(1) 内完成删除而双向链表可以直接通过 prev 指针找到前置节点。写代码时最容易出错的地方就是链表节点的链接关系维护尤其是 moveToHead 和 removeNode 这两个操作很多人写着写着指针就乱了。建议先把 removeNode 独立封装好再用它组合出 get 和 put 的逻辑这样代码结构清晰、不容易写错。在 put 时还要注意先判断 key 是否存在如果存在则更新值并移动到头部如果不存在则判断容量是否满满了先删除尾部节点再插入新节点。这道题值得在笔试前自己亲手写三遍以上因为它是经典中的经典写熟后很多类似的缓存、淘汰策略题目都能举一反三。5.3 递归与回溯题的取分要点递归与回溯也是笔试高频题型比如全排列、子集、组合总和等。这类题的普遍难点不在思路而在代码的整洁度和终止条件的正确性。我给一个通用的应试模板写出终止条件再写循环循环中先做选择再递归最后撤销选择。举个例子如果是全排列问题需要一个 visited 数组标记哪些元素已经使用过每次循环选择一个未使用的元素加入路径然后递归递归返回后撤销选择。这里最容易漏掉的是“撤销选择”这一步很多人忘记把 visited 恢复原状导致结果错误。如果时间紧张我建议大家优先掌握三个模板题全排列体现回溯框架、子集体现组合与状态压缩、组合总和体现剪枝优化。吃透这三个大部分回溯题都能套用。6. 方案设计与开放题如何组织回答结构B站这类大厂的笔试卷除了客观题和编程题最后往往会有一两道开放性的设计题。例如“如果让你设计一个短视频信息流页面你会怎么做”或者“如何优化一个卡顿的列表页面”。这类题没有唯一答案考察的是候选人的思路是否清晰、是否具备全局意识。很多候选人栽在这种题上不是因为不懂技术而是因为没有结构地乱答一通。6.1 信息流页面的整体设计思路先说一下回答这类问题的加分结构应该是需求分析 - 架构分层 - 数据流设计 - 关键细节 - 优化点。按这个顺序讲面试官能快速抓住你的思路而不是听完一头雾水。以“短视频信息流页面”为例需求分析要明确需要支持滑动浏览视频列表、播放与暂停、点赞评论、加载更多、视频缓存等核心功能。架构分层可以考虑自下而上网络层负责接口请求与缓存策略、存储层负责本地缓存视频元数据和播放记录、数据管理层负责对数据的增删改查通知 UI 刷新、UI 展示层负责列表的复用与播放器的渲染。数据流设计上可以采用响应式编程思想数据变化通过 block、代理或 KVO 通知 UI 更新我个人的习惯是数据层完全独立于 UI 层即使上层 UI 换掉数据层也能复用。关键细节部分可以提到使用 UICollectionView 实现瀑布流布局以适配不同视频比例cell 的重用需要处理好播放器的绑定关系避免播放器在滑动时被误释放列表采用分页加载但要注意避免分页参数错乱导致数据重复或漏加载。优化点可以从预加载、缓存、模糊图过渡、后台线程处理 JSON 解析等角度展开。6.2 性能优化类题目的通用分析链路性能优化类开放题我建议所有候选人都背熟一个分析链路先定位问题再分析瓶颈最后给出解决方案并按优先级排序。定位问题的方式包括用 Instruments 的 Time Profiler 检查 CPU 耗时用 Core Animation 工具检查图层渲染用内存工具检查泄漏和内存占用峰值。分析瓶颈时可以从 CPU、GPU、内存、网络四个维度分别排查。CPU 高往往是因为主线程做了太多计算、复杂的布局计算或频繁的 JSON 解析GPU 高则可能是图层混合过度、离屏渲染、图片过大等内存高可能是缓存没有清理或图片未压缩网络慢则可能是请求未走缓存、接口数据量过大。给出解决方案时要注意分优先级回答。第一优先级是明显缺陷的修复比如去掉主线程上的耗时操作第二优先级是架构层面的优化比如引入图片缓存机制、数据预加载第三优先级才是细节调优比如优化图层混合、减少 off-screen rendering。这样组织答案面试官会认为你不仅有技术还有项目管理意识。6.3 笔试卷中的简答题答题策略B站这套笔试卷除选择题外还会有一两道简答题经常是让你解释某个机制或某个现象。简答题的高分关键有两点第一是结构清晰第二是适当展开。结构清晰指的是用“是什么 - 为什么 - 怎么做”的框架回答不要只写结论。比如问你“什么是 Method Swizzling”不要只写“交换两个方法实现”而要包含实现原理、使用场景、注意事项三个层次。适当展开指的是在篇幅和深度上要给面试官留下“这个人是真的懂”的印象但不是字数越多越好而是要在正确的地方展开。比如提到 weak 时可以顺便讲一下 weak 表的结构提到 block 时讲一下捕获变量的三种类型这些看似细节的点恰恰能体现候选人的积累。7. 这套试卷带给我的一些心得与建议说实话B站这套2020校招iOS笔试卷的出题质量在当年算是比较高的它没有堆砌过于冷门的知识点也没有故意出有争议的偏题而是把 iOS 开发中最核心、最常用的技术和最容易踩的坑浓缩成了一套可以快速筛人的题目。我身边不少朋友当年都刷过这套题后来有人进了B站有人去了其他大厂大家一致的评价是做这套卷子的意义不在于押中了多少题而在于通过它发现自己在知识体系上的盲区。7.1 复盘比刷题更重要我给备考同学的第一条建议是不要只追求刷题量一定要做复盘。每做完一套题把错题整理到一个文档里按照“题目 - 正确解法 - 我的错误答案 - 错误原因 - 对应知识点”的结构记录。这个整理过程本身就是一次深度学习比你盲目地再刷十套题都有用。复盘时要重点关注“为什么会错”这个层面。是因为没记住结论还是因为原理没理解如果是前者直接背如果是后者一定要花时间把底层机制搞清楚。我举个例子如果你在“assign 和 weak 的区别”这道题上错了不要只记住“assign 不会自动置 nil”最好把 weak 表的实现源码翻出来读一遍理解了 runtime 是怎么样维护 weak 变量的之后遇到任何变体题目你都不会再错。7.2 基础知识的深度决定上限从这套试卷的考点分布来看iOS 开发的基础知识深度决定了一个候选人的上限。Swift 语法可以速成UIKit 可以边做边学但如果对内存管理、Runtime、多线程这些底层机制理解不透写的代码就只能在“能跑”的层面很难达到“稳定、高效、易维护”的层面。我在带新人的时候有一个很直观的感受基础扎实的同学遇到线上 crash能快速根据调用栈推断出问题的大致范围和可能原因基础不牢的同学同样一个 crash只能反复尝试“加几个判断看能不能规避”排查效率天差地别。所以准备笔试的过程本质上也是为后续实际工作打基础的过程不要带着应试思维去死记硬背要用“以后排障用得上的心态”去理解原理。7.3 最后再分享一个小技巧准备一份自己的技术复盘文档最后分享一个我个人非常受益的习惯不要把备战资料分散在各处而是集中到一个文档里按主题归类持续更新。我会准备一个“iOS 知识体系自查表”里面涵盖了从内存管理到 UI 渲染、从网络层到数据持久化、从架构设计到性能优化的二十多个主题每个主题下面记录我自己的理解、面试中碰过的问题、以及在实操中踩过的坑。每做完一份笔试题或参加完一次面试我都会回来更新这份文档。这样积累三个月后你会有非常完整且带着个人体感的知识体系不再是零零散散的知识点。笔试也好、面试也好都不再是临阵磨枪的背诵而是胸有成竹的输出。这套B站2020校招题表面上是检验知识面实际上也是检验你平时有没有系统地积累和归纳。如果你现在就开始这么做等到真正面试时你的状态会完全不同。
返回列表