news 2026/9/1 9:47:20

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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 指向 class,class 的 isa 指向 meta class,meta class 的 isa 指向根元类,根元类的 isa 指向自己,形成一个闭环。类对象的 superclass 指向父类,元类的 superclass 也有一条独立的继承链。理解这张图,才能在遇到“objc_getClass 和 object_getClass 的区别”这类题目时准确作答。

3.2 Method Swizzling 的常见坑与正确写法

Method Swizzling 几乎在每套 iOS 面试题中都会出现,B站这套卷子也不例外。核心原理是通过 Runtime 的class_addMethodmethod_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,就不会自动发送通知,这时需要开发者手动调用willChangeValueForKeydidChangeValueForKey。这个知识点在面试中经常出现,我建议大家能写出手动触发的完整代码。

考点三:KVO 与 KVC 的关系。KVC 触发 KVO 的途径、KVC 查找 setter 的顺序(先找set<Key>:,再找_set<Key>,最后找成员变量),这些细节尽量都过一遍,因为它们是理解整个动态机制的拼图。

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校招题,表面上是检验知识面,实际上也是检验你平时有没有系统地积累和归纳。如果你现在就开始这么做,等到真正面试时,你的状态会完全不同。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/1 9:43:40

Anthropic API连接失败与网关路由报错排查指南

在日常开发里&#xff0c;只要接触过 Claude 或 Anthropic 系列 API&#xff0c;基本都遇到过两类让人头疼的情况&#xff1a;一类是网络请求层面的 unable to connect to anthropic services 、 failed to connect to api.anthropic.com &#xff0c;另一类则是模型路由层…

作者头像 李华
网站建设 2026/9/1 9:42:48

Agent Skills实战:从Claude Code到Codex的可复用技能体系

这次我们来看一个直接把两个词串起来的方向&#xff1a;Claude Code 和 Agent Skills。最近几乎所有讨论 AI 编程、AI 自动化、Agent 开发的地方&#xff0c;都会反复出现这两个概念。网上相关的教程很多&#xff0c;但大多要么只讲“怎么用 Claude 聊天”&#xff0c;要么只讲…

作者头像 李华
网站建设 2026/9/1 9:40:27

yuzu 模拟器:在电脑上运行 Switch 游戏的上手指南

yuzu 模拟器&#xff1a;在电脑上运行 Switch 游戏的上手指南 【免费下载链接】yuzu 任天堂 Switch 模拟器 项目地址: https://gitcode.com/GitHub_Trending/yu/yuzu yuzu 是一个开源的任天堂 Switch 模拟器&#xff0c;用 C 编写&#xff0c;官方维护 Windows、Linux 和…

作者头像 李华
网站建设 2026/9/1 9:36:11

LibTV指南:AI漫剧制作工作流与角色一致性实战

如果你最近刷 B 站&#xff0c;大概率会刷到一些制作精良的 AI 漫剧&#xff1a;运镜流畅、角色表情统一、配音自然&#xff0c;弹幕里不少人问“这是怎么做的”。更多的人尝试用 AI 工具复刻&#xff0c;结果卡在同一个地方——角色上一秒还是这张脸&#xff0c;下一秒就换了个…

作者头像 李华