
这份“360公司-2019校招笔试-ios开发工程师客观题合集”我在准备秋招时翻来覆去看了好几遍。它不是那种随随便便拼凑的题库而是能看出大厂客户端笔试到底在考察什么能力的参考样本。对于正在准备iOS开发校招的同学来说这份合集的核心价值不在于“背答案”而在于帮你建立一套完整的iOS知识体系同时摸清考官出题时的侧重点。本文我会结合这份合集的典型考察方向把“常考什么、为什么考、怎么准备”一次讲透顺带附上一些我自己实际做题、复盘时总结的避坑经验。1. 先从这份合集看大厂笔试的出题逻辑1.1 客观题在笔试环节扮演什么角色很多同学把校招笔试简单理解为“刷掉不懂基础的人”这个理解太表面了。360这类公司在校招笔试阶段设置客观题核心目的其实是“在最短时间内完成候选人大范围的知识点筛查”。一套试卷几十道选择题覆盖语言特性、内存管理、并发、网络、UI渲染等多个维度面试官拿到成绩单后能快速判断出你的能力长板在哪、短板在哪后续面试时的追问方向也就有了依据。尤其对iOS开发工程师这个岗位来说客观题占比往往不低。原因是iOS日常工作中涉及大量“知道就是知道不知道就是不知道”的知识点比如runtime的消息转发流程、ARC下对象的释放时机、runloop的mode切换这些细节靠临时推理很难“蒙对”必须真的构建过知识体系。客观题恰恰是衡量这种积累的高效方式。这份合集里的题目分布也印证了这一点Objective-C语言基础、内存管理、runtime机制、多线程、UIKit生命周期是绝对的高频区网络和系统框架次之。数据结构与算法在客观题里也有出现但占比明显低于纯iOS技术栈说明客户端校招更看重候选人对iOS平台特性的掌握深度。1.2 从题目分布反推知识权重我按照这份合集里的考察方向做了个大致的分类统计虽然不同年份、不同批次的试卷会有浮动但整体趋势很有参考价值语言和编译特性约占25%内存管理与runtime合计约占30%多线程与runloop约占20%UIKit与界面相关约占15%剩下的是网络、存储、系统框架以及少量计算机基础。这个分布透露出的信号很明确笔试真正在意的不是你会不会用某个API而是“你能不能理解API背后的原理”。举个例子你可以在简历上写“熟练使用AFNetworking”但如果问到你“为什么AFNetworking在iOS 13上需要手动设置completionQueue”这类问题时背API的人会卡壳理解网络线程模型的人却能顺手答出来。所以备考的时候每遇到一个知识点至少要往深处多问自己两到三个“为什么”。2. 客观题高频考点全拆解从语言特性到系统框架2.1 Objective-C语言特性与编译原理Objective-C的底层机制是笔试第一大热门。经常出现的问法包括分类Category能否添加实例变量、load和initialize的执行时机区别、dynamic和synthesize的作用差异、weak属性在ARC下的实现方式。先说分类能不能添加实例变量这是最经典的一个坑。语法上分类允许用property声明属性但不会自动生成带下划线的成员变量和setter/getter方法因为分类的结构体里并没有ivar列表的扩展入口。如果你真需要在分类里存储关联对象标准解法是使用objc_setAssociatedObject和objc_getAssociatedObject很多同学知道这个答案但不理解为什么考场上换个说法就可能做错。load和initialize的区别也是历年高频。load是在类被加载到runtime时调用不管有没有使用到这个类都会触发且父类先于子类、分类后于主类initialize则是懒加载首次向类发送消息时才执行。这个知识点看起来简单但笔试喜欢考“多个分类同时实现了initialize会怎样”——答案是只会执行一个后编译的那个生效而load却是所有分类的实现都会被调用。这类细节就是典型的“知道原理就能做对背结论就容易翻车”的题目。还有编译特性的考察比如你写了一段字符串拼接代码编译器可能会把它转换成stringWithFormat或者进行常量折叠。这类题目不会直接问“编译器做了什么优化”而是变换成“以下哪种写法更高效”来考察。备考时可以去把一份Objective-C代码用clang -rewrite-objc转成C实现看看对理解编译过程非常有帮助。2.2 内存管理引用计数、ARC与循环引用内存管理是iOS客观题绝对的重头戏。核心考点集中在引用计数原理、ARC下的修饰符语义、autoreleasepool的释放时机、以及循环引用的典型场景。考察引用计数时常见问法是给出一段代码让你判断某个对象在某个时刻的retainCount。这类题现在出得少了因为ARC下retainCount本身没有太大意义但不代表引用计数思想不重要。改头换面后考点变成了“持有关系”谁强谁弱、谁创建谁释放、block捕获了谁。循环引用是几乎年年出现的考点。常见场景包括block内部使用了self、NSTimer的target强引用了self、delegate用strong修饰、父子视图互相持有。这些场景我后面会专门用题目演示这里先强调一个容易被忽略的地方——NSTimer套weakSelf仍然可能出问题。因为timer被加到runloop后会被runloop持有而target对self是弱引用看似没问题但如果timer一直不失效self被迫一直存活的同时timer和runloop也构成了长期持有关系这在业务上可能引发内存占用、方法重复调用等奇怪bug。所以正确姿势通常是在合适时机主动invalidate而不是单纯依赖weakSelf。autoreleasepool的释放时机也常被拿来考尤其是“在for循环里创建大量临时对象要不要手动包一层autoreleasepool”。答案是要因为在MRC时代和ARC下自动释放池的排空节点是固定的比如runloop一次循环结束如果一次性创建大量临时对象且不手动干预峰值内存会明显抬高。理解了这一点你就能解释为什么很多图片加载、JSON解析的底层实现里都嵌入了autoreleasepool。2.3 Runtime、KVO与消息转发机制Runtime机制是iOS笔试里最能拉开区分度的一块。考得深的有三部分消息发送、消息转发、方法交换Method Swizzling。选择题里常见的是给你一段代码问调用的实际结果是哪个方法。先捋一下消息发送流程objc_msgSend根据对象的isa指针去类对象的方法列表里查找找不到就去父类继续找直到NSObject还没有就进入动态方法解析resolveInstanceMethod。如果这一步没有动态添加方法就会走快速转发流程forwardingTargetForSelector还处理不了就进入完整转发流程methodSignatureForSelector和forwardInvocation。考试里最常见的变体是只实现了其中一两步问你能走到哪一步、会不会崩溃。KVO的实现原理也是笔试常客。简单说系统会在第一次对某个对象添加观察时动态生成一个中间类比如NSKVONotifying_XXX把对象的isa指针指向这个子类并重写被观察属性的setter方法在setter里同时触发willChangeValueForKey和didChangeValueForKey。理解了这个原理很多题目都能迎刃而解比如“KVO观察的是属性还是成员变量”——答案是被观察对象的setter方法直接修改成员变量不触发通知。还有“对同一个对象多次添加和移除观察者会不会崩溃”——iOS 11之前重复移除确实可能崩之后系统做了兼容处理但编辑器和很多第三方库仍会警告这道题就在考你对系统版本行为的了解。方法交换也是常见题型尤其是“交换了viewDidLoad实现后原方法还会执行吗”这类问题。答案取决于你交换时是把原实现保存下来再调用还是直接替换成另一个方法的实现。这个知识点本身不复杂但笔试喜欢和“load方法里做交换的时机”结合着出。2.4 RunLoop、多线程与线程安全RunLoop和线程是我备考时花费精力最多的模块因为概念相互交织容易学成一团浆糊。RunLoop的核心考点包括为什么主线程默认有RunLoop而子线程没有、RunLoop的Mode切换机制、source/timer/observer三类事件源谁先谁后。笔试里常考“NSTimer在滚动scrollView后为什么会停”。原因就是RunLoop在事件处理时会切换到UITrackingRunLoopMode而NSTimer默认注册在kCFRunLoopDefaultMode即NSDefaultRunLoopMode下被TrackingMode挡住了。解决办法是把它添加到NSRunLoopCommonModes这个模式可以看作一组mode的集合能够同时覆盖Default和Tracking。多线程部分GCD是绝对主角。需要弄清楚的概念有同步/异步、串行/并发队列、死锁的成因、信号量、栅栏函数、group通知。死锁是必考项经常给一段在主队列调用sync的代码问会不会卡死。答案是不会报错但主线程被自己阻塞直接造成UI不响应。实际项目中这个bug并不少见很多人以为在主队列用async就万事大吉其实异步也可能因为顺序依赖而拖慢界面。线程安全方面iOS笔试喜欢考察atomic到底保证了什么。atomic只能保证属性的读写操作是原子的不能保证对象内多个属性的组合操作安全更不能替代业务层面的加锁。常见的额外考察点是atomic的底层是spinlock还是mutex这里提醒一下iOS 10之后系统已经用os_unfair_lock替代了有优先级反转风险的spinlock如果是比较新的题目需要注意这个细节。2.5 UIKit与界面渲染UI相关的客观题一般不会太难但覆盖面广。高频点是UIView和CALayer的关系、生命周期方法调用顺序、约束冲突的排查思路、离屏渲染的触发条件。UIView和CALayer的关系几乎是必考。UIView负责事件响应和内容布局CALayer负责实际内容绘制和动画渲染。为什么系统要把它们拆成两层直接原因是要支持跨平台的Core Animation而更深层的原因是UIView的交互能力很重如果所有绘制工作都压在UIView上性能会非常难优化。所以当题目问“UIView是不是CALayer的delegate”时你要知道答案不是简单的“是”或“否”因为UIView确实作为CALayer的delegate存在但它并不实现CALayerDelegate的所有方法是否显示绘制完全由系统内部决定。视图控制器的生命周期顺序也是经典送分题loadView、viewDidLoad、viewWillAppear、viewWillLayoutSubviews、viewDidLayoutSubviews、viewDidAppear。但笔试喜欢在边界条件上做文章比如“在viewDidAppear里修改约束会有什么后果”——因为约束更新可能触发新的布局传递如果在一个不合适的时间点修改会造成不必要的重复布局极端情况下甚至在iOS 13之后的版本出现随机崩溃原因是系统对生命周期安全的校验变得更严格了。离屏渲染是我认为笔试最容易失分的地方。常见触发条件有圆角clipsToBounds组合、shadowPath未设置、mask、栅格化shouldRasterize、组透明度。理解离屏渲染的关键是知道GPU需要在一个单独的内存缓冲区中完成绘制再合成到当前帧缓冲区这个切换是需要代价的。所以题目问“为什么设置了圆角和shadow后滑动掉帧”答案基本绕不开离屏渲染的开销。反过来如果题目问“如何避免离屏渲染”正确的方向是用贝塞尔曲线裁剪、预先绘制圆角图片、设置shadowPath等方式替代系统默认的绘制路径。2.6 网络、持久化与系统框架网络这块笔试考察的核心是HTTP协议基础和iOS网络层的封装关系。经常考GET和POST的实质区别不是参数位置不同而是语义和请求体的设计差异、HTTP状态码含义、DNS解析过程、HTTPS的握手流程。其中“TCP三次握手和TLS握手是一起发生的吗”这种跨层问题比较有区分度。持久化方面候选人里知道NSUserDefaults、文件归档、SQLite、Core Data、FMDB的人很多但考得细的是“它们分别适合什么场景”。NSUserDefaults适合轻量配置不适合存大数据归档适合单个对象SQLite是关系型数据存储量大且需要查询时用Core Data虽然本质也可以是SQLite但主打的是对象图管理学习成本高。笔试不是让你写具体SQL而是判断一段业务描述应该用哪种存储方案。系统框架方面偶尔会考到64位环境下NSInteger和int的区别、keychain的存储位置、剪贴板UIPasteboard的读写限制等。这类题目相对零散备考时花少量时间过一遍Apple官方文档的Framework目录就够用不必深挖。真正值得多花时间的是安全领域比如App Transport Security在iOS 9之后默认阻止HTTP明文请求、代码签名和描述文件的作用、Keychain在删掉App后为什么数据还在这些概念性选择题出现频率不低。3. 典型题目实战解析从读题到拿分3.1 经典循环引用题题目类似这样在ViewController中定义一个blcok属性内部使用了self以下哪种写法能避免循环引用A. 使用__weak typeof(self) weakSelf selfblock内使用weakSelfB. 直接在block内使用selfC. 使用__unsafe_unretained修饰selfD. block执行完成后手动把self置nil答案通常是A。但别急着选完走人考官可能再挖一个变种如果block被保存到类的某个属性上且block内部又调用了weakSelf的某个方法调用过程中weakSelf正好被释放了会出现什么情况答案是weakSelf会变成nil向nil发送消息是安全的不会崩溃但业务逻辑会中断。所以现在很多团队更推荐在block开头将weakSelf重新赋给strongSelf保证方法执行期间self不会被提前释放这也是面试官追问时的加分点。我当年做这种题时踩过一个坑看到一个选项带__weak就觉得安全没注意block是串行执行还是并发执行也没注意是否在子线程持有self。实际上如果block内部启动了一个异步任务然后又在这个异步任务里访问了self那么外部weakSelf持有住block、block持有住异步任务异步任务又持有self这条链依然可能形成强引用环。要在选择题里稳稳拿分必须把“谁持有谁”的链条一步步画清楚。3.2 Block变量捕获题题目常给一段代码int a 10; __block int b 10; void (^block)(void) ^{ a 20; b 20; }; block(); NSLog(a%d, b%d, a, b);问你输出结果是什么。如果不加__blocka在block内部的修改不会影响外部变量因为普通局部变量是值捕获加了__block后b会被包装成一个结构体block持有的是它的指针所以内部修改能反映到外部。答案是a10b20。这种题看似简单但延伸出来的考点是对象类型的捕获。如果把a换成一个NSMutableArray在block内部调用addObject不修改a本身那外部的array内容会变因为捕获的是指针你修改的是指针指向的对象内容但如果直接对a重新赋值一个新的array那外部a不会变。这个区别很多人上考场才想明白。我建议备考时用一个playground把这两种情况分别跑一遍印象会非常深刻。3.3 题目三KVO触发时机与手动触发题目一般这样描述对一个属性做了KVO监听然后在代码里给该属性赋值但没有调用willChangeValueForKey和didChangeValueForKey请问观察者会收到回调吗正常情况下如果该属性是用property声明并且没有修改setter实现系统会自动调用willChangeValueForKey和didChangeValueForKey所以观察者能收到。但如果属性是直接对成员变量赋值比如_age 10就不会触发KVO。还有一种经典陷阱手动调用didChangeValueForKey但没调willChangeValueForKey这种情况系统虽然能通知观察者但观察者拿到的oldValue可能是异常值因为没有触发标记。延伸考点是KVO的依赖键。比如UI界面显示的是“学生完整信息”而观察的是学生的name和age系统可以通过keyPathsForValuesAffectingValueForKey来建立依赖。笔试可能会问“为什么只改变age属性时依赖于完整信息的观察者也被通知了”这时如果你不清楚KVO的keyPath依赖机制就很容易懵。建议把依赖键的手动实现方式记清楚因为它是区分“会用KVO”和“懂KVO”的典型标记。3.4 多线程死锁题死锁题最典型的描述是在主线程的viewDidLoad里执行dispatch_sync(dispatch_get_main_queue(), ^{ ... })结果是答案死锁主线程卡死。因为在主队列中调用sync任务意味着当前线程需要等待block执行完再继续但这个block又被安排在主队列中主队列的任务必须等viewDidLoad执行完才能轮到于是形成了互相等待。变种一是改成在子线程调用dispatch_sync(dispatch_get_main_queue())结果不会死锁因为主队列并没有被占用主线程可以正常执行完这个block再返回。变种二是嵌套serialQueue比如自定义了一个串行队列然后在里面再同步提交一个任务到同一个队列同样会死锁。理解的关键就一句话同步提交到“当前正在执行任务的串行队列”会发生死锁提交到并发队列或当前未占用的队列则不一定。我备考时刷到过一道题问“dispatch_barrier_sync和dispatch_barrier_async的区别”很多人只记得一个阻塞一个不阻塞但忘了barrier在并发队列里才是语义正确的如果放到全局并发队列实际上是按普通block处理的不保证栅栏效果。这类题如果能答出“barrier必须配合自建并发队列使用”就能显著加分。4. 一套可落地的备考路线与实操安排4.1 知识体系打底先建框架再补细节很多同学备考iOS笔试时最大的问题是“知识点看完了但不知道哪里还没学透”。我的建议是先把知识框架画出来再往每个分支填细节。框架可以按照“语言层、运行时层、系统框架层、应用层”四个维度组织。语言层包括Objective-C基础语法、Block、KVC/KVO、分类与扩展、属性修饰符。运行时层包括消息机制、方法交换、isa指针、类结构、关联对象。系统框架层包括内存管理、RunLoop、多线程、网络、持久化、Core Animation。应用层包括生命周期管理、页面路由、性能优化、事件处理链。框架建好之后每学一个模块尝试用一段代码来验证。比如学RunLoop时写一个小demo创建子线程、往子线程RunLoop里添加timer观察它是否一直运行。这种“写出来”的验证方式比单纯对着文档记结论有效得多。4.2 刷题方法与时间规划刷题不是越多越好而是要控制错题复盘的频率。我备考期间的做法是每两天刷一套模拟题第一天定时做第二天只复盘错题和蒙对的题把每道错题背后对应的知识点写进错题本并标注关联的另外两个可能考点。这样持续三周知识盲区会被逐渐清干净。时间规划上建议前两周主攻语言、内存、runtime、多线程这四个核心模块因为它们难度高、占比大第三周补UI、网络、存储等相对零散的知识点最后一周回归真题做整套模拟卷同时开始复盘之前记录的易混清单。要注意的是iOS开发是一个实践性很强的领域纯理论复习容易忘最好是每天留出一小时写代码实验尤其是RunLoop、多线程这种一定要实跑验证。4.3 模拟笔试环境与复盘要点模拟笔试的核心是“时间压力下的决策能力”。选择题不需要每一步都写出答案但你需要训练自己的分题节奏简单题控制在40秒内中等题控制在90秒内超难题不要死磕先标记后跳过。一套试卷做完后按照“答案正确率”和“知识点覆盖率”两个维度复盘。复盘时特别要关注那些你虽然选对但拿不准的题。拿不准意味着知识体系有缺口只是运气好蒙对了。我会把这些题单独标记为“伪正确”并在错题本里记录下来。笔试不是高考没有老师帮你批改所以主动分析自己“为什么犹豫”非常关键。犹豫的根源通常是两个知识点搞混了比如同步和异步、强引用和弱引用、串行和并发这时候用表格把概念对列出来比反复刷题更有效。5. 笔试避坑经验与易混淆概念速查5.1 客观题里的“文字陷阱”类型校招客观题为了确保区分度会在选项措辞上埋一些坑。最常见的是绝对化表述比如“一定不会崩溃”“任何情况下都不会”“总是先执行”。只要出现绝对化限定词大概率是错误选项因为iOS开发里几乎找不到一条“永远成立”的规则。第二种陷阱是张冠李戴把A概念的特点挪到B概念上。比如问“load方法在什么时候调用”错误选项会写成“在第一次使用类时调用”这就是把initialize的特点混进来了。应对方法是做题时不仅要知道正确选项为什么对还要能解释错误选项为什么错如果解释不了说明你只是记住了结论。第三种陷阱是“过时答案”。iOS系统迭代很快有些旧结论已经不再正确。比如iOS 13之后UISceneDelegate引入AppDelegate的职责范围就变了iOS 15之后系统对UINavigationBar的默认样式也做了调整。如果试卷年份比较新要注意答案是否与当前系统行为一致。备考时看官方文档的“Deprecated”列表能帮你避掉不少这类坑。5.2 高频易混淆点对照表我把客观题里最常见的混淆概念整理成了一张速查表备考最后几天反复过一遍比零散刷题收获更大。概念对核心区别典型考点assign / weakassign不处理对象释放后悬垂问题weak置nil修饰delegate用weak还是assignstrong / copycopy对不可变对象可能只是retain可变对象会深拷贝block属性通常用copy修饰的原因dispatch_sync / asyncsync阻塞当前线程等待async不阻塞主队列调用sync为何死锁串行队列 / 并发队列任务是否同时执行与同步/异步是两码事barrier只能用于自建并发队列load / initializeload在类加载时触发initialize在首次使用前触发多个分类实现时选哪一个frame / boundsframe相对父视图bounds相对自身坐标系bounds改变后子视图位置变化隐式动画 / 显式动画Core Animation默认0.25秒动画显式可自定义为什么改frame有动画效果离屏渲染 / CPU绘制离屏渲染仍在GPU只是换缓冲区圆角阴影为何掉帧这张表看起来简单但每一条背后都能展开出三道以上的选择题。建议不要只是背表而是对着每一行“为什么这样区分”自己复述一遍能讲顺畅才算真正掌握。5.3 答题节奏与状态管理笔试考验的不只是知识储备还有在有限时间内稳定输出的能力。iOS岗位的客观题一般题量在30到50道之间时间60到90分钟平均下来每道题只有一两分钟。我的实际体会是前10道题往往会因为状态没调整好而浪费较多时间所以进入正式答题前可以先把试卷整体扫一遍标记出自己比较有把握的模块和明显偏难的题目先从熟悉的板块开始做。遇到犹豫不决的题目不要立刻硬啃先按第一感觉选一个答案并标记“待复查”等整卷做完再回头。回头复查时重点看两类题目一类是涉及概念对区分的比如以上讲的weak/assign、sync/async另一类是考察代码执行结果的因为这类题只要把代码执行逻辑一步步推下去通常能得出确定答案。最后的检查阶段不要轻易修改答案除非你发现自己确实记错了某个关键知识点否则第一感觉的正确率往往更高。另外笔试前一天的休息很重要。iOS知识点密集且琐碎熬夜刷题带来的疲劳感会直接影响第二天的读题效率。我备考的时候踩过这个坑前半夜刷得太high第二天做逻辑题明显反应变慢后来再准备其他笔试时就调整为“睡前只过一遍错题本不做新题”整体状态稳定了很多。6. 写在后面的一点个人体会这份合集我从头到尾复盘过至少三遍每遍都有新的收获。第一遍是单纯刷题对答案第二遍开始琢磨每道题背后的知识点网络第三遍则是把错题和犹豫题拿出来讲给自己听。到后来我发现客观题确实只是敲门砖但它能逼着你把iOS的知识体系从“会用”过渡到“理解”。真正吃透这些基础概念之后后面二面、三面的技术深挖也会顺畅不少。如果你正在准备类似的iOS校招笔试希望这份拆解能帮你少走一些弯路。