网易iOS校招笔试复盘:Objective-C、内存与系统设计核心考点全解析
2026/8/31 19:38:03 网站建设 项目流程

经常有准备校招的同学问我,网易iOS开发工程师(北京)的笔试卷,到底是在考什么?我前前后后帮团队筛过不少iOS校招笔试卷,“网易2018校园招聘iOS开发工程师(BJ)笔试卷”这类卷子,我现在还会拿来当复习蓝本。它不考偏门操作,而是把Objective-C语言基础、内存管理、多线程、网络、算法和系统设计这几块内容结结实实地串了一遍。这篇文章我会把这类笔试题的题型分布、每类题背后的考点,以及我当时准备校招的思路完整梳理一遍,不只给答案,更把“为什么这么答”讲清楚。

1. 笔试卷整体画像:一份校招笔试卷的考察逻辑

1.1 题型分布与分值节奏

网易2018校园招聘iOS开发工程师(BJ)笔试卷,采用的是线上限时答题模式,整场时间一般在90到120分钟之间。我根据历年校招iOS笔试的常规形态,把卷型结构做了一个还原,方便你建立整体认知:

题型题量考察能力建议分配时间
单选/多选题10~15题OC语言基础、内存管理、对象生命周期15分钟
简答/程序阅读题5~8题Runtime、Runloop、KVO、多线程原理20分钟
算法编程题2~3题数组、链表、字符串、二叉树40分钟
方案设计题1~2题图片缓存、IM消息、性能优化30分钟

这个分值分布是有内在逻辑的。选择题能快速筛出基础是否扎实,比如strongcopy的区别、atomic到底原子了什么、GCD死锁的经典场景,这些不需要长篇大论,会就是会,不会就是不会。程序阅读题看的是你对系统框架的理解深度,像“这段代码在子线程更新UI会出现什么结果”这种题,靠背面试题很难应付,必须真正理解Runloop和主线程的关系。最后两道设计题才是拉开差距的地方,大部分人的答案停在“用SDWebImage就行”,而高分答案会主动拆解内存设计、磁盘策略、缓存淘汰机制和弱网降级方案。

1.2 从岗位JD反推考点

你如果翻过网易当年的iOS开发工程师校招JD,会发现核心要求翻来覆去就几条:熟练掌握Objective-C,理解iOS应用生命周期和内存管理,有良好的数据结构和算法基础,有实际项目或实习经验。笔试题目其实就是在验证这几条。

所以2018年的笔试不会刻意追求新框架,更不会拿SwiftUI或最新的Swift并发特性来考你,因为校招生普遍缺少这部分实战积累。整份卷子真正想确认的,是你有没有把iOS最底层的那套机制吃透:ARC是怎么维护引用计数的、Runtime发消息走了哪条链路、Runloop在App启动和交互过程中扮演什么角色、KVO的底层实现是不是只靠addObserver就完事了。这些东西在简历上都能写“掌握”,但笔试一出手就知道是真掌握还是假掌握。

我见过不少准备充分的同学,一上来就猛刷Swift语法、SwiftUI组件,结果笔试选择题里考的都是Objective-C的__weakautoreleasepoolmethodSwizzle,当场傻眼。方向错了,努力越多越吃亏。

1.3 这份卷子对当下的参考价值

有人可能会问,2018年的卷子放到现在还有参考价值吗?我的判断是,价值反而更高了。iOS开发的底层考察逻辑,这几年变化并不大,日常开发用到的关键机制依然是ARC、Runloop、GCD、Block、KVC/KVO这一套。即使现在项目主流语言从OC切换到了Swift,底层依然绕不过objc_msgSend、引用计数和Runloop。把这份卷子的考点吃透,再去面对市面上大部分iOS岗位的笔面试,你都能做到心中有底。

2. 核心考点逐项拆解:OC、内存、Runtime与并发

2.1 内存管理:从引用计数到循环引用

内存管理是iOS笔试的绝对主力,几乎每份卷子都会考,而且一考就是连环题。

先说引用计数。Objective-C里每个对象都有一个retainCount,当引用计数降为0时,对象会执行dealloc并释放内存。ARC在编译期帮我们插入retain/release,省去了手动管理的心智负担。笔试常考的是ARC下几种修饰符的区别:

修饰符作用关键注意点
__strong默认,持有对象,引用计数+1赋值即持有
__weak弱引用,不增加引用计数对象释放后自动置nil
__unsafe_unretained类似weak,但对象释放后不置nil继续访问会野指针崩溃
copy拷贝一份新的不可变对象NSString、Block常用
assign简单赋值,常用于基本类型用于对象时极易悬空

__weak底层依赖Runtime维护的弱引用表(SideTable里的weak表)。当一个对象被释放时,系统会遍历所有指向它的weak指针,把它们统一置为nil,这就是我们常说的“安全”所在。很多同学只知道weak指向的对象释放后会变成nil,却答不出底层表结构,这就会在简答题中丢分。

另一类高频考点是循环引用。最常见的是Block循环引用:对象持有Block,Block内部又强引用了self,形成self -> block -> self的引用环,导致双方都无法释放。解法是使用__weak typeof(self) weakSelf = self,在Block内部再通过strongSelf转一下,避免执行期间对象被提前释放。此外还有NSTimer循环引用、delegate循环引用、CADisplayLink循环引用,这些场景都要能举例说明。

我建议复习时不要只看概念,要做一道经典题:写出下列代码的打印顺序,并解释为什么。代码一般长这样:

- (void)viewDidLoad { [super viewDidLoad]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"1"); }); [self performSelector:@selector(printSomething) withObject:nil afterDelay:0]; NSLog(@"3"); }

想拿分,光知道performSelector:afterDelay:依赖Runloop还不够,你还要解释主线程Runloop在点击事件和定时器之间的工作顺序。这类题就是典型的“基础题但陷阱深”。

2.2 Runtime:消息传递、动态解析与方法交换

Runtime是iOS开发中“高级感”最强的部分,也是网易这类大厂笔试的常客。它考察的不是你会不会用某个API,而是你能不能说清消息发送的完整路径。

在Objective-C中,方法调用[obj doSomething]编译后其实会变成objc_msgSend(obj, @selector(doSomething))objc_msgSend会沿着对象的isa指针找到类对象,再在方法列表里查找doSomething对应的IMP。找到就叫函数,找不到就进入消息转发流程。

消息转发流程需要按顺序记忆:

  1. +resolveInstanceMethod::动态给类添加方法的机会。
  2. -forwardingTargetForSelector::把消息转发给另一个对象处理。
  3. -methodSignatureForSelector::返回方法签名,配合-forwardInvocation:做完整转发。

笔试如果出简答题,让你设计一个“防崩溃组件”,本质上就是在考你能否利用这三级转发,把未实现的方法拦截下来,避免抛出unrecognized selector异常。

方法交换(Method Swizzling)也是高频题。它利用Runtime在运行时替换方法的IMP,经典用途是给系统方法打点、HOOK某个方法。你要能说清为什么交换后要在自定义方法里调用原来的IMP、为什么不要在+load里做复杂逻辑、以及为什么在Swift中扩展系统类方法时使用Swizzling要格外小心。只背结论不追原理,很容易在追问环节翻车。

2.3 多线程与锁:从GCD到线程安全

App要处理异步任务,GCD是绕不开的工具。笔试对多线程的考察集中在三块:队列类型、死锁场景、线程安全。

GCD有两种队列:串行队列和并发队列。serial队里任务按顺序执行,concurrent队里任务可以同时执行。同步执行dispatch_sync和异步执行dispatch_async,组合起来又会产生不同效果。最经典的死锁题长这样:

dispatch_queue_t queue = dispatch_get_main_queue(); dispatch_sync(queue, ^{ NSLog(@"不会执行到这里"); });

在主线程同步向主队列提交一个Block,Block在等待主线程执行,主线程在等待Block完成,两个互相等待,直接死锁。类似的还有在串行队列内部再同步向这个队列提交任务,同样死锁。这类题几乎每年都考,理解了队列和线程的关系就能稳拿分。

线程安全方面,atomic是一个高频陷阱。很多同学以为atomic就是线程安全,实际上它只保证了属性的getter/setter原子性,并不保证整个对象或业务逻辑的线程安全。举个简单例子,两个线程同时读取atomic数组的count,再根据下标取元素,中间如果另一个线程修改了数组,依然会越界崩溃。真正要保证业务安全,还得靠加锁。

锁的性能可以从快到慢排序:os_unfair_lockNSLock@synchronized。笔试中一般只会问区别,不会让你背性能表,但能说出“@synchronized本质上是给对象加了一次递归锁,性能成本偏高,适合低频保护”就是加分项。

2.4 Runloop与事件循环

Runloop是iOS事件处理的引擎。一个App启动后,主线程的Runloop会不断跑圈,处理触摸事件、定时器、网络回调、界面刷新。笔试常考Runloop的几种Mode、Source/Timer/Observer三类事件源,以及autoreleasepool在什么时候释放。

一个很经典的题目是:为什么NSTimer在滑动ScrollView时会暂停?答案是在滑动过程中,Runloop切到了UITrackingRunLoopMode,默认只在kCFRunLoopDefaultMode下工作的定时器就被暂停了。解决方法是把Timer加到NSRunLoopCommonModes下,或者改用CADisplayLink。这类题考的不是API,而是你对事件驱动模型的理解。

Runloop和autoreleasepool的关系也值得写进答案:App启动后主线程会注册两个Observer,一个监听kCFRunLoopEntry,进入Runloop时创建自动释放池;另一个监听kCFRunLoopBeforeWaitingkCFRunLoopExit,在Runloop即将休眠和退出时释放自动释放池。所以autorelease对象并不是立刻释放的,而是要等当前Runloop周期走到特定时机。

3. 架构与系统设计题:怎样回答更占优势

3.1 三类高频设计题的递进解法

方案设计题是网易笔试中拉分最明显的部分。校招生容易答成“知识点堆砌”,但高分答案要有明确的模块拆分和取舍逻辑。

第一类是图片缓存设计。推荐答案是分三层表述:内存缓存、磁盘缓存、网络加载。内存缓存使用NSCache,因为它在系统内存紧张时会自动回收;磁盘缓存按路径或数据库存储;网络层负责下载和缓存写入。再往后可以补充缓存淘汰策略LRU、缓存键生成规则、图片解码是否放在子线程、避免主线程卡顿,以及弱网下的占位图和重试机制。这样答,面试官能看出你确实考虑过真实业务。

第二类是IM消息架构。一个即时通讯模块至少要拆成连接层、协议层、业务层和存储层。连接层负责长连接建立、心跳维持、断线重连;协议层处理消息编解码;业务层管理会话、未读数、消息发送状态;存储层解决离线消息和历史记录。追问时大概率会问你“消息发出去,对方没收到怎么办”,你要能说出ack确认、超时重传、消息幂等性这些点。

第三类是启动速度优化。这题在2018年校招中就已经出现,现在更是高频。回答可以从pre-main阶段和非pre-main阶段分开讲。pre-main阶段可以压缩动态库数量、减少类分类数量、检查+load方法;App启动阶段可以统计耗时方法、把非UI操作延后到viewDidAppear、使用启动清单优化顺序。这个答案没有固定标准,核心是让面试官看到你有“先量化再优化”的工程思维。

3.2 万能的分析框架:需求、约束、模块、容错

设计题如果没思路,我建议用一套通用框架:先说需求,再说约束,再拆模块,最后谈容错。

举个例子,题目是“设计一个头像上传功能”。很多人上来就写“用AFNetworking上传图片到七牛”,这显然太浅。按框架来走:需求是用户修改头像,上传后全端同步;约束包括图片大小限制、上传速度、弱网、流量消耗、失败重试;模块拆成图片选择与压缩、上传任务管理、进度展示、失败队列;容错部分要处理弱网导致的中断、服务器返回失败、CDN同步延时。

把这套逻辑讲完,整个答案就有了骨架。面试官追问任何一环,你都有内容可聊。校招考察的不是结果,而是你有没有意识去考虑这些边界问题。

3.3 架构选型:MVC、MVVM与组件化

“iOS架构”这个词在面试中很容易被问,但笔试题通常不会让你写一个大架构,更多是让你评价某种架构的优缺点,或者在阅读代码时指出职责混乱的问题。

MVC是iOS默认架构,问题在于Controller太重,容易膨胀成“上帝类”。MVVM引入ViewModel层,把视图逻辑和业务逻辑解耦,但校招生在讲述时容易把MVVM和“把代码拆开写”划等号。高分回答会点出MVVM的核心是数据绑定,在iOS里要么通过KVO,要么通过ReactiveCocoa或Swift Combine,并说明双向绑定的代价是调试成本变高。

组件化是另一个常被问的概念,但笔试一般不展开,最多在简答题里让你“谈一谈模块化设计和CocoaPods私有库的关系”。你能说出来:组件化解决的是团队协作、编译速度、代码隔离问题,CocoaPods私有库只是载体,真正困难的是组件间通信方案,就已经很有深度了。

4. 编程题准备:刷题范围、手写代码与常见误区

4.1 刷题优先级的规划

算法题不是iOS专场,但网易笔试一定会安排。编程题不会出特别偏的题,范围集中在数组、链表、字符串、二叉树、栈和队列。如果你想在短期内复习完,我建议按优先级来:

  • 第一优先级:链表反转、合并两个有序链表、寻找链表中间节点、环形链表判断。
  • 第二优先级:二叉树前中后序遍历、层序遍历、二叉树最大深度、最近公共祖先。
  • 第三优先级:字符串翻转、括号匹配、最长无重复子串。
  • 第四优先级:排序算法手写和复杂度分析,尤其是快速排序和归并排序。
  • 第五优先级:LRU、手写线程安全的单例、生产者消费者模型。

这个优先级不是随便拍的,它们直接对应iOS日常开发里“列表数据、缓存管理、任务调度”这些真实场景。大厂喜欢考这些题,是因为它们能反映一个人的基本功。

4.2 一道高频手写题:LRU缓存

LRU(最近最少使用)缓存是图片缓存框架的核心,也是笔试和面试里反复出现的“老朋友”。我在这里给一个基于哈希表+双向链表的Swift实现,笔试时用OC或Swift写核心逻辑都可以,关键是让评委看到你的数据结构设计能力。

final class LRUCache { private final class Node { var key: Int var value: Int var prev: Node? var next: Node? init(_ key: Int, _ value: Int) { self.key = key self.value = value } } private let capacity: Int private var cache: [Int: Node] = [:] private let head = Node(0, 0) private let tail = Node(0, 0) init(_ capacity: Int) { self.capacity = capacity head.next = tail tail.prev = head } func get(_ key: Int) -> Int { guard let node = cache[key] else { return -1 } moveToHead(node) return node.value } func put(_ key: Int, _ value: Int) { if let node = cache[key] { node.value = value moveToHead(node) return } let node = Node(key, value) cache[key] = node addToHead(node) if cache.count > capacity { let removed = tail.prev! removeNode(removed) cache.removeValue(forKey: removed.key) } } private func addToHead(_ node: Node) { node.prev = head node.next = head.next head.next?.prev = node head.next = node } private func removeNode(_ node: Node) { node.prev?.next = node.next node.next?.prev = node.prev } private func moveToHead(_ node: Node) { removeNode(node) addToHead(node) } }

这段代码有几个注意点:哈希表保证O(1)查找,双向链表保证O(1)删除和插入;每次get或put都会把节点移到头部,尾部自然就是最久未使用的节点,删除时直接移除尾部。背代码没有意义,你要能说出“为什么用哈希表”“为什么需要双向链表而不是单向链表”。

4.3 编程题的高分习惯

编程题想拿满分,除了答案正确,还要注意两点:边界条件和代码规范。

边界条件包括空数组、只有一个节点、链表头尾操作、整数溢出。很多同学写代码时不会主动检查这些,笔试系统一跑边界用例就崩。我的建议是落笔前先花30秒列出边界条件,写到方法入口处统一的guard或if判断。

代码规范包括命名清晰、不要写过长函数、注释只加在关键位置、复杂度要用一行字写清楚。不要小看这个习惯,我在批卷时经常看到同一个算法,一个同学只写了核心循环,另一个同学把时间复杂度、空间复杂度、边界用例都注释清楚,后者即使代码有小瑕疵,分数也会更高。原因很简单,笔试筛选的是未来要一起协作的人,代码可读性本身就是工程能力的一部分。

5. 从笔试卷延伸到工程化基本功:iOS开发者的日常积累

5.1 证书、签名、真机调试与模拟器

很多校招生对笔试试卷里出现“证书”“签名”这类题目不理解,觉得这是上架阶段才用得到的东西。实际上面试官问这些,是想确认你是否有过完整的真机调试和打包经验。

iOS开发者证书的核心逻辑是“Apple信任的链条”:开发者通过Apple Developer账号生成Certificate,再配合Provisioning Profile,把设备和App绑定起来,才允许在真机上安装运行。平时开发时我一直开着Xcode的Automatically manage signing,它会自动帮你管理证书和描述文件。遇到“ios开发者app证书更新”的问题,大多发生在证书过期或设备UUID变更时,去Apple Developer后台重新下载描述文件即可。

模拟器和真机的区别也要能讲明白:模拟器模拟的是x86/arm64架构,很多系统能力都可以跑,但CPU架构、传感器、推送行为并不完全一致。所以“ios设备模拟”适合快速调试UI和逻辑,但涉及到相机、定位、后台任务和性能优化,必须用真机验证。“ios模拟器”也是自动化测试的重要载体,后面会提到。

5.2 网络调试:Charles抓包和数据流分析

笔试时网络相关题目一般只考HTTP层和Socket层,但真正做完一个项目后你会发现,不会抓包会非常痛苦。我日常排查网络问题最常用的工具是Charles,它本质上是一个本地代理工具,职责是转发和展示请求/响应数据。

用Charles做iOS抓包,基础步骤是:电脑上启动Charles,开启SSL Proxying;手机跟电脑连同一局域网,把手机WiFi代理指向电脑IP和端口8888;然后在手机浏览器访问chls.pro/ssl下载安装Charles的根证书;最后在Charles里配置Enable SSL Proxying,填入需要解密的域名。这套流程做完,你在App里的HTTPS请求就能在Charles里看到明文了。

需要提醒一句:抓包工具只能用于调试自己参与的App或测试环境,不要用于抓取他人App的隐私数据,这是职业底线。在笔面试中,主动说出“我只在自己开发的App和测试环境中使用”反而会加分。

5.3 自动化测试:从手动点点点到自动化回归

很多同学对“ios自动化”的理解停留在用脚本点击屏幕。实际上iOS自动化包含三个层次:单元测试、UI测试和持续集成。单元测试用XCTest针对函数和类做隔离验证;UI测试通过XCUITest模拟用户点击、滑动、输入;持续集成则把这些测试接到构建流程里,每次提交代码自动跑一遍。

理解自动化对笔试有直接帮助,因为在回答“如何保证代码质量”这类开放题时,能说出“通过单元测试覆盖核心工具类,通过UI测试做关键路径回归,再通过CI在每次merge前触发全量测试”这种答案,明显比干巴巴回答“会写测试”更有说服力。

6. 高频考点自查与复习建议

6.1 能不能30秒答出来:自查清单

我在陪同学做模拟面试时经常用一套“30秒问答”清单,这些问题如果不能在30秒内给出完整回答,说明基础还需要再补:

问题回答要点容易丢分的地方
Category和Extension区别Category在运行时增加方法,不能直接添加实例变量;Extension是编译期,可以添加属性混淆运行时和编译期
NSString为什么用copy防止可变字符串被外部修改只答“安全”不解释
KVO底层原理isa-swizzling,动态生成子类,重写setter不关注willChange/didChange
load和initialize区别load是类加载时,initialize是首次使用时说不清调用顺序
hitTest事件传递从父视图向子视图递归调用pointInside说反查找顺序
Block变量捕获局变量用const拷贝,__block用指针传递说不清静态变量捕获方式

这些问题看起来简单,但每一条都可以往深挖。比如KVO,不仅要答出“利用Runtime动态生成子类”,最好还能说出观察者释放时要调用removeObserver,否则会崩溃。这类细节在笔试多选题里经常设陷阱。

6.2 从笔试到面试的衔接点

笔试只是第一关,面试官大概率会拿着你的笔试答案继续追问。比如你选择题选了atomic,他会问“atomic到底能保证什么”;你设计题里写了LRU,他会问“为什么NSCache不直接暴露LRU控制接口”;你提到SDWebImage做了磁盘缓存,他会问“磁盘缓存的失效策略怎么设计”。

所以我建议复习时不要背题,要把每一个答案都当成“展开话题的起点”。每个考点至少准备30秒的观点陈述,外加一个业务场景。比如你回答“Block循环引用”时,主动举一个NSTimer持有self的例子,面试官大概率会认可你的工程经验。

6.3 最后两周怎么学

如果距离笔试只剩两周,我的复习节奏是:第一周回归基础,用《Objective-C高级编程》这本书把内存管理、Block、GCD三章精读一遍,顺手把书里的代码示例在Xcode里跑一遍;第二周进入刷题和模拟模式,上午刷LeetCode热题中的easy和mid,下午做一份完整的模拟卷,晚上复盘错题。

模拟卷不要只做一遍,做完后一定要把每道题的知识点拆开,写清楚“这道题在考什么”“为什么我会错”“如果换成另一种问法我能不能识别”。这个方法听起来朴素,但坚持两周效果非常明显。我记得当初自己准备笔试时,把一份模拟卷翻来覆去做了四五遍,真正到考场上时,很多答案几乎已经长在肌肉记忆里了,这份手感比临时抱佛脚背一百道题都管用。

希望这篇复盘能帮你在面对网易或同类公司的iOS校招笔试时,少走一些我走过的弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询