
这份二星级练习卷我用了整整三个周末才完整过了一遍期间踩了不少坑。它不是什么高深理论合集而是把日常开发里以为会、真写就卡壳的知识点集中到了一起。我见过不少工作两三年的iOS开发刷这套题成绩反而不如一个刚转正但平时爱折腾的新人——原因很简单这套卷子考的不是背题能力而是你有没有真正理解iOS系统在背后做了什么。下面我按模块拆一遍把每道题背后的考点和实际工程里的对应场景都讲清楚。1. 二星级不是简单级这套练习卷到底在考什么1.1 考点层级与能力模型从会用到说清原理先说结论二星级练习卷对应的是一个合格iOS开发者的下限但它考查的方式不是让你默写API而是要求你解释清楚为什么这么做。举个典型例子——题目里问UIStackView的distribution设为fillEqually时如果有两个label的文字长度不一样会发生什么。这种题在文档里能查到答案但如果你没在真机上看过效果回答时大概率会漏掉label的content hugging priority会参与约束计算这个关键点。整套卷子的考点大致分为五块语言基础Swift和OC各占一部分、UIKit与布局、网络层、并发与性能、系统能力集成。这五个方向和日常开发是完整对应的不是拍脑袋出的题。比如charles ios抓包这种题看起来是考工具使用实际上考的是HTTPS证书校验原理、ATS配置、代理机制三个点任何一个没吃透抓包这关就过不扎实。1.2 拿到卷子先别急着写时间分配与答题策略我的建议是先把整张卷子扫一遍按会做、有点懵、完全不会三类打标记。会做的直接写有点懵的留到第二轮完全不会的放在最后。二星级卷子有个特点难题往往藏在中后段前面几道语言基础题反而全是坑。我见过有人在前面的字符串题上纠结了20分钟导致后面的并发题没时间展开写。按我的经验时间分配应该是语言基础30%、UIKit与布局25%、网络层20%、并发与性能15%、系统能力10%。这个比例不是让大家做题时这么分配而是日常学习时应该投入的精力权重。网络层的题目虽然占比不高但一旦考到证书或抓包往往是大题分值高。所以复习时不能按题量来要按分值权重来。2. Swift与OC语言基础送分题里藏着三道必死题2.1 值类型与引用类型数组到底传的是值还是引用练习卷第一道大题就是Swift中Array是值类型为什么性能还这么好。这道题的标准答案是写时复制Copy-on-Write但只答到这步只能拿一半分。另一半在于写时复制依赖引用计数数组内部有一个指向堆内存的缓冲区只有在发生写入时才真正复制。所以你写let a [1,2,3]; var b a; b.append(4)第一步的b a只是复制了结构体本身和引用计数加一真正耗时的append才触发缓冲区复制。OC方向则完全相反考的是为什么NSMutableArray声明属性时要用copy还是strong。我见过太多人在这道题上栽跟头如果用一个NSMutableArray实例去赋值给一个声明为copy的NSArray属性得到的是一个不可变拷贝。但反过来如果你把属性声明为strong的NSMutableArray外部修改同一实例内部数据就悄悄变了。这道题的本质是考察你有没有被可变性坑过。2.2 循环引用的三类典型场景闭包、代理、Timer循环引用是二星级卷子的必考题而且通常会给你三段代码让你找问题。第一段是闭包class A { var closure: (() - Void)?; deinit { print(A deinit) } }然后在某个方法里写closure { self.doSomething() }。解法是在闭包捕获列表里[weak self]然后guard let self self else { return }。这个大家都知道但卷子考的是如果闭包里访问了self.view的某个属性或者self的lazy var为什么[weak self]不够还要注意什么——答案是lazy闭包如果持有self即使在[weak self]下也可能因为生命周期问题崩溃。第二种是代理protocol MyDelegate: class {}属性声明为weak var delegate。这道题大多数人能答对但进一步追问为什么代理用weak而delegate的对象必须遵守class协议就答不完整了。原因是规避循环引用而weak只适用于类对象结构体和枚举没有引用计数。第三种是Timer。二星级出现的是Swift 4.2之后的Timer新增的weak var方式也就是iOS 10引入的BlockInvocation方案。考点在于Timer.scheduledTimer默认会添加到RunLoop的common mode而RunLoop会持有TimerTimer的block又持有self形成一条无法主动打破的引用链。解决办法不一定是要在deinit里invalidate因为循环引用导致deinit根本不会执行。正确做法是[weak self]捕获并在viewWillDisappear或viewDidDisappear里invalidate。2.3 字符串与集合操作的陷阱Swift与OC的思维切换字符串这块是整张卷子最容易丢分的地方。Swift里String的下标访问要用String.Index直接写str[0]在早期版本编译不过这是过渡期大家的共同记忆。二星级卷子不考这种纯记忆题它考的是为什么Swift的String用Collection协议但不能用整数下标——因为Unicode的字符边界问题一个Character可能由多个Character组合而成比如emoji中的家庭组合符号。这也是为什么NSString和String之间转换时length和count可能不一样。OC方向则考NSString的copy与strong区别以及NSMutableString在作为NSDictionary的key时为什么不会被正确取出。后者对应的问题是不可变副本能保证key的hash值稳定但如果你不写copy用可变字符串作为key之后修改它hash就变了NSDictionary内部结构就乱了。这道题直接踩过的人不多但理解了之后你会养成属性一律用copy写NSString的习惯。3. UIKit与Auto Layout实战UIStackView到底解决了什么问题3.1 从frame到Auto Layout再到UIStackView的演进逻辑练习卷里有一道很有意思的题目对比frame、Auto Layout、UIStackView三种布局方式说明为什么StackView能减少大量约束代码。要答好这道题得回到布局的本质。用frame布局时你需要在viewDidLayoutSubviews或屏幕旋转回调里手动计算每个视图的坐标适配不同尺寸的屏幕就是一场噩梦。Auto Layout把问题变成了线性方程组求解视图的位置和大小由约束决定能自动适配但问题在于约束越多调试越痛苦更新约束时还要非常小心写错一条就会导致冲突或缺失。UIStackView在这个基础上更进一步它把沿着某个轴依次排列这个问题抽象成了系统内置的约束逻辑开发者不需要再为每个子视图写leading、trailing、top、bottom四件套。它内部会为每个arrangedSubview自动生成约束同时对外暴露axis、distribution、alignment、spacing几个简单属性。二星级卷子考这个本质是判断你是否清楚StackView不是万能的它适合等宽按钮组、标签流式排列这类规则场景遇到不规则重叠布局还是要拆开用约束。3.2 UIStackView的排列、分布与嵌套坑在哪我实际用下来的感受是UIStackView最坑的地方是distribution属性。很多人以为fillEqually就是让所有子视图等宽但如果你在一个水平StackView里放了一个很长的label、一个很短的label两边都设了content hugging priority那fillEqually并不等于每个都占1/n宽度——它优先保证内容不被压缩到低于固有宽度然后才做等宽如果某个视图的压缩阻力compression resistance更高它会把多出来的宽度吸走。另外StackView的spacing在iOS 11之后增加了customSpacing(after:)方法可以单独设置某两个相邻视图之间的间距。这个API很容易被忽略但它非常实用。比如一个标签后面跟一个输入框标签和输入框之间间距10输入框和下一个标签之间间距20以前要插两个空View现在一个API搞定。考试时会问你是否知道这个API答出来能说明你写过干净的布局代码。嵌套StackView是另一个考点。很多新人在一个StackView里塞各种复杂的组结果性能下降滚动时页面卡顿。原因在于StackView是通过Auto Layout计算子视图的尺寸和位置的嵌套过深会导致约束系统需要处理的变量暴涨。二星级考这个的常见问法是StackView嵌套性能比纯约束差多少没有绝对数值但你会听到一个经验法则不超过三层超过就看看是不是应该用UICollectionView或UITableView。3.3 动态高度与TableViewCell自适应布局的完整链路这套卷子的UI部分大概率会有一道TableViewCell自适应高度的综合题。它不像看起来那么简单因为要支持动态高度需要的不是设置UITableView的rowHeight而是estimatedRowHeight加tableView.rowHeight UITableView.automaticDimension然后保证cell内部contentView的约束从上到下是完整的。我在做这个时踩过一个大坑cell里的label设置了numberOfLines 0但忘记设置preferredMaxLayoutWidth导致在部分机型上文字换行宽度判断错误cell高度计算异常。虽然iOS 12之后这个问题改善了很多但在iOS 11上仍然存在。所以我在回答这类题时会把preferredMaxLayoutWidth作为必查项写在检查清单里。另外一个关于StackView和cell的组合你可以在cell里直接用一个垂直StackView里面放两行label然后让StackView的四边和contentView的约束对齐。这样你要做的就是设置StackView的spacing上下约束各设一个其它都交给系统。这是当前比较推荐的写法代码量小而且不容易漏约束。但千万别把StackView塞进UIScrollView里再放进cell这会导致约束系统不知道应该按哪个尺寸计算经常在真机上出现高度为0的问题。4. 网络层考察题从URLSession到抓包工具4.1 URLSession的缓存策略和会话复用你真的懂吗网络这一块二星级卷子不会直接问GET和POST的区别它会把题目包装成一个小场景页面加载时请求一个列表接口希望快速显示缓存同时后台刷新最新数据你怎么设计这就涉及URLSession的缓存策略和URLCache的使用。我把答案拆成两层。第一层是缓存策略URLRequest.CachePolicy.useProtocolCachePolicy会按服务器返回的Cache-Control和Expires决定是否使用缓存reloadIgnoringLocalCacheData则强制请求服务器。对于先展示缓存再刷新的场景通常用useProtocolCachePolicy配合服务器的ETag做条件请求。第二层是会话复用URLSession要设置configuration.requestCachePolicy同时还要设置URLCache的容量。很多人设了缓存策略但忘了容量限制导致磁盘缓存无限增长App被系统认定为占空间大头。4.2 ATS与HTTPS证书校验为什么上线前总要处理从iOS 9开始引入的App Transport Security是练习卷必考内容因为它直接对应一个很实际的体验你在开发阶段用http://192.168.1.10:8080调试本地服务结果iOS不让发请求报App Transport Security has blocked a cleartext HTTP错误。解决方法是先在Info.plist里配置NSAppTransportSecurity开发阶段用NSAllowsArbitraryLoads跳过ATS但上线前必须割掉或者配置NSExceptionDomains只放行测试域名。这道题常见追问是为什么ATS不允许HTTP明文传输这个对应的是抓包工具能解密HTTPS的本质。我在做题时把这个串联起来理解ATS是客户端的安全策略证书校验则是确认服务器的身份。Charles等抓包工具能解密HTTPS原理是安装了Charles的根证书让设备信任它这样Charles就能作为中间人篡改传输。如果不做SSL Pinning抓包工具能看到的就不只是明文了连加密的HTTPS内容也能截获。4.3 证书校验证与Charles抓包的实操链路这里值得展开因为很多人会在这个过程里卡住。用Charles抓包iOS设备完整流程是电脑上打开Charles设置代理端口8888iPhone上连同一个Wi-Fi在Wi-Fi设置里配置手动代理代理地址填电脑的IP端口8888然后iPhone上首次访问HTTPS时会提示证书不可信需要在Safari里下载Charles的证书charles-proxy-ssl-proxying-certificate.pem。之后的坑在于iOS 10.3之后证书需要手动信任——去设置-通用-关于本机-证书信任设置里打开开关不然依然抓不到HTTPS。我见过不止一个同事在这一步卡了半小时以为代理设置错了其实就是漏了证书信任开关。这套练习卷如果考到抓包必考这个细节。另一个细节是证书安装后的实际作用范围只对当前系统信任的域名生效如果遇到证书校验做到SSL Pinning的AppCharles就无法解密这时需要的是越狱环境下的SSL Kill Switch但这类内容已经超出正常开发调试范畴不建议去碰。5. 并发与性能优化电池优化题答得好直接加分5.1 GCD与OperationQueue怎么答才显得有工程经验并发题是练习卷的区分度所在。同样是问多线程有哪几种方式初级回答是GCD、OperationQueue、Thread但这个背书式答案拿不到高分。更好的回答顺序是先说明主线程是UI更新的唯一线程任何耗时操作都不能阻塞主线程然后根据场景选工具——同步等待回调的可以用DispatchSemaphore有依赖关系的用OperationQueue需要取消任务的用BlockOperation等等。举一个卷面场景图片列表下拉加载更多同时要支持暂停和继续。这道题用GCD就不太好做因为GCD不提供取消机制。用OperationQueue设置maxConcurrentOperationCount 4把每个下载操作包装成BlockOperation维护一个字典保存线程安全的operation对象取消时调用cancel()。这个回答既讲了原理又给了落地方案很容易给阅卷人留下好印象。DispatchSemaphore是另一个高频答案。它考得不深但要能说清楚信号量和死锁的关系。比如在主线程里semaphore.wait()会怎么样答案是直接导致主线程阻塞如果这个等待的事件恰好在主线程队列里才会触发就形成死锁。这个场景其实就是网络请求回主线程的典型坑主线程等待子线程回调子线程的任务又被派到主队列互相等。5.2 电池优化定位、后台任务与推送的取舍iOS开发里电池优化是一类容易被轻视的题但二星级卷子明确把它列为综合应用题。原因很简单一个App如果耗电严重用户会直接卸载体验比功能缺失更致命。我在做这道题时把考核点拆成三块定位服务、后台任务、推送唤醒。定位是耗电大户。CLLocationManager的精度设置直接决定耗电量kCLLocationAccuracyBest用了GPSkCLLocationAccuracyHundredMeters可能只需基站和Wi-Fi定位。真正体现工程判断的是什么时候用高精度。我的做法是地图导航页面启动时用高精度切到后台或用户停住不动时降级到kCLLocationAccuracyKilometer同时利用startMonitoringSignificantLocationChanges做显著位置变化监听而不是一直startUpdatingLocation。后台任务的常见误区是立刻想到beginBackgroundTask。但beginBackgroundTask只是延长了App被杀的时间不是无限后台运行的权限系统给的最长时间通常是30秒到10分钟不等。二星级卷子会问如何才能在后台正确地完成一个上传任务答案不是beginBackgroundTask而是用URLSession的background会话配置让系统在后台调度网络请求同时设置urlSession(_:task:didCompleteWithError:)来提示用户上传完成。推送的静默推送也类似content-available: 1的远程推送能让App在后台被短暂唤醒执行代码但系统会综合用户使用频率、电量来限制唤醒次数频繁使用会被系统判定为恶意唤醒直接降级不再推送。5.3 卡顿排查用Instruments而不是拍脑袋和性能相关的另一道题是如果主线程卡顿你如何定位。这道题有两个层面的答案。第一个层面是工具菜单Xcode自带的Instruments里Time Profiler可以直接看到主线程各方法的耗时分布Dispatch Queue模板可以看到任务派发的情况。第二个层面是代码层面的自检检查是否在viewDidLoad里做了大文件同步读取scrollViewDidScroll里是否每次都重新创建了视图图片解码是否放在了主线程。我实际排查过一个案例列表滚动时偶尔卡一帧用Time Profiler抓出来是UIImage的init(named:)导致的因为named方法有缓存但首次创建时会从磁盘读出数据并在主线程做解码。解决方案是改成子线程解码或者用imageWithContentsOfFile:加ImageIO在子线程创建缩略图。这个经验直接搬到练习卷上就是加分点因为它说明你不是只在理论上懂主线程别做耗时操作而是踩过真实case。6. 系统能力集成题蓝牙、唤起App与跨端文件共享6.1 CoreBluetooth状态机系统级蓝牙权限和App级权限的差别练习卷有一道高频题CBCentralManager的state应该监听哪些值系统蓝牙关着和App没权限有什么区别。很多刚入行的开发者在这道题上一脸茫然因为他们分不清系统蓝牙开没开和App能不能用蓝牙是两回事。CBCentralManager有个属性是state它返回的是CBManagerState枚举包括unknown刚开始初始化、resetting系统蓝牙服务重置、unsupported设备不支持蓝牙4.0、unauthorizedApp未被授权使用蓝牙、poweredOff系统蓝牙被关闭、poweredOn正常。面试时如果把这些问题混在一起只会答蓝牙没开就会被扣分。另外iOS 13之后蓝牙权限从原来的NSBluetoothPeripheralUsageDescription改为NSBluetoothAlwaysUsageDescription这个变更也是考试常考细节。我用CBCentralManager.init(delegate:queue:)初始化时习惯在centralManagerDidUpdateState:回调里先判断state如果poweredOff就弹出系统设置引导如果unauthorized则提示用户在设置里打开权限。这套状态机授权引导的组合比在真机上一上来就扫描要稳得多。6.2 浏览器唤起安装AppUniversal Link背后的一套事ios浏览器唤起安装App这个热搜词背后是Universal Link和URL Scheme两代方案的比较。早期方案是URL Scheme比如myapp://open?pageid1问题在于如果用户没装App系统会弹窗提示无法打开而且URL Scheme存在被其他App抢注的可能性。Apple推出Universal Link的目的就是用HTTPS链接唤醒App你在浏览器里打开https://qq.com/xxx如果手机装了QQ系统直接用QQ打开如果没有装则打开Safari进入官网再从官网下载。考试考察Universal Link时会问你AASA文件是什么。AASA全称是apple-app-site-association一个放在你的域名根目录的JSON文件里面声明了哪些路径允许唤起App。装完这个文件后开发者在Xcode的Signing Capabilities里添加Associated Domains域名格式是applinks:example.com。这套配置看似简单但实际容易踩的坑是AASA文件必须HTTPS访问、不能有重定向、MIME类型不能是application/json的问题经常在真机上导致没有效果。另外用户在Safari里如果点击了右上角的地址栏图标系统会记住此网站不想打开App要过很久才恢复这也是开发和测试时困扰大家的点。6.3 iOS与Windows共享文件夹从局域网文件协议说起windows和ios共享文件夹这个场景现在已经很常见它考验的是对本地网络和SMB协议的理解。iOS上的文件App提供了一个连接服务器功能支持SMB协议连接Windows共享文件夹。步骤是两个设备连同一局域网Windows上开启文件共享然后在iOS文件App里点右上角三个点选择连接服务器输入smb://设备IP/共享文件夹名回车后输入用户名密码即可。这道题如果出现在练习卷里大概率会问为什么iOS不能直接访问Windows共享文件夹需要答到SMB协议和iOS对SMB的兼容性。iOS 13之后文件App原生支持SMB本质上就是打开了一个远程文件浏览会话。实际使用中容易遇到两个问题一个是在Wi-Fi不稳定的场景下大文件传输会中断另一个是Windows共享文件夹对大小写敏感的问题如果文件夹内有中文名但编码不一致iOS上可能看到的是乱码。这个偏向日常应用层面的知识点一般会作为附加题出现。7. 工程化与上架全链路证书、打包、测试与提审7.1 开发者证书与描述文件一年一更新的背后逻辑工程化模块里ios开发者app证书更新几乎是必考。要理解整个证书体系关键不是背流程而是理解为什么Apple要设计这么一套繁琐的东西。每个开发者账号可以生成一个开发和发布证书证书本质是一对公私钥里的公钥文件用开发者账号私钥签名。描述文件则绑定“证书App ID设备UDID”三样只有描述文件里注册的设备才能安装对应环境的App。我在实际更新证书时最常踩的坑是本地原来生成证书的私钥丢失导致在开发者后台无法重新生成描述文件因为需要匹配原证书。解决办法是找回原始.p12文件或直接在后台撤销证书、重新生成一套再在Xcode里重新选择。iOS开发者后台的证书、标识符和配置文件页面层级很容易走晕我的习惯是先点证书生成证书并下载到钥匙串再创建描述文件顺序反了会提示找不到可用证书。和证书紧密相关的一个热门话题是ios微信双开签名失败。微信双开依赖企业证书签名一旦证书被系统吊销签名就会失败原来安装的应用也不能正常打开。这类操作本质上是在利用签名漏洞或企业分发通道存在严重安全风险我强烈不建议触碰。练习卷如果考到签名题重点方向是正常开发、测试、上架的各个环节如何处理签名。7.2 uniapp打包iOS测试包与纯原生打包的差异这套卷子在工程化部分还会有一道跨端题用uniapp打iOS测试包和纯原生相比要注意什么。这道题对做纯原生的同学有难度但它是近年来面试官非常爱问的陷阱题。uniapp打包iOS有两种方式一种是通过HBuilderX云打包直接在云端生成ipa安装包不需要本地配置Xcode另一种是离线打包下载官方离线SDK用Xcode打开工程后自己配置证书和签名。前者适合快速跑通验证后者适合对包体大小和原生插件有定制要求的场景。云打包时需要先在manifest.json里配置App图标、启动图、权限声明然后选择iOS打包证书.p12和描述文件。这里有个显而易见的坑很多人用开发证书打包导致安装到非注册设备上报无法安装因为开发描述文件绑定了设备UDID。另一个值得一提的点是ios开发者版本和开发者模式。在iOS 16之前每天安装到真机的App数量有限制且必须通过Xcode或描述文件签名iOS 16之后新增了开发者模式要在设置-隐私与安全性里手动启用否则Xcode运行真机调试时会报无法启动开发者模式。这个细节在练习卷如果考到会让不少从老版本过渡过来的人措手不及。如果用uniapp做了原生插件开发还要注意ios混合开发方案的选型是走WKWKWebView的JSBridge方案还是用uni-app的uts插件直接调用原生API。这两种方案各有适用场景考试问到这个背后的本质是考察你对H5和原生之间的通信机制是否清楚。7.3 上架审核避坑加急审核与常见被拒原因上架题考的是经验。App Store Connect后台的操作不算难难的是知道别人在什么情况下被拒。我在练习卷里专门给自己整理了拒审常见原因Guideline 2.1App完成度不够比如填了测试账号但实际打不开、Guideline 3.2.2业务模式不符合要求比如虚拟支付没有走IAP、Guideline 4.2App最小功能集要求、Guideline 5.1.1数据收集需要用户同意。这些在过去的印象里只存在于审核文档中但踩过一次就知道每个条款背后都有真实案例。加急审核是另一个偏门考点。Apple官方提供了一个加急审核申请网页填写App id、原因、联系方式。合理的加急理由包括线上版本存在严重崩溃、紧急安全漏洞、需要配合特定时间点的活动等。但不要滥用一般申请理由不够充分就会被拒绝而且不会告诉你具体原因。上架前最好先在TestFlight里用真机过一遍主流程我吃过一次亏某次提交后审核员进入App发现登录按钮在iPad上被键盘遮挡直接以2.1大礼包退回。这类问题在模拟器上很难发现因为iPad模拟器的虚拟键盘行为和真机不一致。8. 把练习卷变成自己的知识地图错题整理与进阶路线8.1 错题整理以练习卷为骨架建立个人知识库做完这套二星级练习卷真正的价值不在分数而在于你把错题整理成什么样子。我的习惯是每个错题都记录三个维度错误答案、正确答案、背后原理。比如为什么代理要用weak如果只记防止循环引用就太浅了我会把weak的语义、引用计数的变化、以及如果不是类协议会怎样这三个点都写进去这样下次遇到页面A持有页面BB的代理指向A的场景能直接调用知识库里的方案。整理时我倾向用Notion或Obsidian这类双链笔记工具。每个知识点单独建页题目本身关联到知识点页知识点页再关联到代码示例和真实项目经验。这样练习卷就从一个静态文档变成了活的工程手册。二星级卷子覆盖的范围足以撑起一个初学者的知识图谱后续每遇到一个真实问题就在对应节点上补充案例。坚持半年这个库的价值会远超单一的刷题数量。8.2 从二星到三星进阶方向与自我检验方法练习卷做到第二遍如果正确率能稳定在80%以上就可以开始往三星级方向走。三星级的差异点主要在三块更底层的系统知识比如RunLoop源码级别、消息转发、KVO底层实现更复杂的架构设计比如MVVM的绑定方案、Coordinator模式、模块化架构以及更新的框架适配比如Swift Concurrencyasync/await、SwiftUI生命周期、Live Activity等。我个人建议的自我检验方法不是继续刷题而是把每个知识点用费曼学习法讲给别人听。找一个朋友哪怕不是iOS开发也行给他讲清楚为什么UIStackView能减少约束数量。如果他能听懂说明你是真的理解了。如果讲得磕磕绊绊那就回去重新看资料。这个方法比埋头刷题效率高很多因为你会发现讲课的过程会逼你把模糊的细节想清楚比如StackView的alignment和distribution到底是怎么配合的。最后一个小技巧练习卷做完后把它放到GitHub上做一份自己的iOS知识自检清单然后把每次真实项目里踩的坑都往对应章节里补。半年后回头看你会比当初刷题时的自己强一大截。