iOS开发基础80-iOS 10 / Xcode 8
iOS 10 / Xcode 8
Xcode 8 和 iOS 10 带来了不少变化,其中有些是临时升级坑(如 Xcode 插件屏蔽、日志混乱、Xib 格式兼容)
一、隐私权限设置(Info.plist)
iOS 10 起,访问隐私敏感数据(相机、相册、定位、麦克风等)必须在 Info.plist 中添加对应的 UsageDescription,否则 App 会直接崩溃。
常见权限 Key
| 功能 | Info.plist Key |
|---|---|
| 相机 | NSCameraUsageDescription |
| 相册 | NSPhotoLibraryUsageDescription |
| 麦克风 | NSMicrophoneUsageDescription |
| 定位(使用期间) | NSLocationWhenInUseUsageDescription |
| 定位(始终) | NSLocationAlwaysAndWhenInUseUsageDescription(iOS 11+)/ NSLocationAlwaysUsageDescription(iOS 10) |
| 通讯录 | NSContactsUsageDescription |
| 日历 | NSCalendarsUsageDescription |
| 提醒事项 | NSRemindersUsageDescription |
| 蓝牙 | NSBluetoothPeripheralUsageDescription |
| 运动与健身 | NSMotionUsageDescription |
| 语音识别 | NSSpeechRecognitionUsageDescription |
| 媒体资料库 | NSAppleMusicUsageDescription |
注意事项
- 描述字符串必须填写,且要说明为什么需要这个权限(如"需要访问相机以拍摄头像"),不能只写"需要相机权限"。
- 描述写得不好可能导致 App Store 审核被拒。
- 在 Xcode 中也可以通过
Privacy - XXX Usage Description的可视化方式添加。
二、系统版本判断
判断 iOS 系统版本有多种方式,各有优劣,面试常考。
方式一:isOperatingSystemAtLeastVersion:(推荐)
// iOS 8+ 推荐方式
if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){10, 0, 0}]) {
NSLog(@"iOS 10 及以上");
}
优点:精确到主版本、次版本、修订版本;不会有字符串比较的坑。
方式二:NSFoundationVersionNumber
if (NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_9_0) {
// iOS 9 及以上
}
缺点:不是每个 iOS 版本都有对应的常量(如 iOS 10 没有 NSFoundationVersionNumber_iOS_10_0),需要自己查数值,不推荐。
方式三:systemVersion 字符串比较(不推荐)
NSString *version = [UIDevice currentDevice].systemVersion;
if ([version compare:@"10.0" options:NSNumericSearch] != NSOrderedAscending) {
// iOS 10 及以上
}
缺点:字符串比较容易出错(如 "9.0" > "10.0" 的字典序问题),必须用 NSNumericSearch 选项。
Swift 方式:#available(最推荐)
if #available(iOS 10.0, *) {
// iOS 10 及以上
} else {
// 低版本
}
优点:编译器检查,自动处理版本判断,Xcode 会提示哪些 API 是哪个版本的,最安全。
三、ATS(App Transport Security)
ATS 是 iOS 9 引入的网络安全机制,默认禁止 HTTP 明文请求,强制使用 HTTPS。这是高频面试题。
默认行为
- iOS 9+ 默认只允许 HTTPS 请求。
- 直接发 HTTP 请求会报错:
App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure.
解决方案
方案一:全局允许 HTTP(不推荐,审核可能被拒)
在 Info.plist 中添加:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
iOS 10 后,苹果对全局开启 ATS 例外的应用审核更严格,需要说明理由,否则可能被拒。
方案二:针对特定域名允许 HTTP(推荐)
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>example.com</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
常用字段说明:
| 字段 | 说明 |
|---|---|
NSIncludesSubdomains |
是否包含子域名 |
NSExceptionAllowsInsecureHTTPLoads |
允许该域名的 HTTP 请求 |
NSExceptionMinimumTLSVersion |
最低 TLS 版本(如 TLS 1.0) |
NSExceptionRequiresForwardSecrecy |
是否要求前向保密 |
方案三:彻底改用 HTTPS(最推荐)
长期来看,所有接口都应该用 HTTPS,这是行业趋势,也是苹果的强制要求。
- ATS 是什么? iOS 9 引入的网络安全机制,默认禁止 HTTP。
- 怎么允许 HTTP? 全局
NSAllowsArbitraryLoads或针对域名NSExceptionDomains。 - 全局开启 ATS 例外有什么风险? App Store 审核可能被拒,需要说明理由。
- 推荐做法? 用 HTTPS,或针对特定域名开放例外。
四、iOS 10 通知大改(UserNotifications.framework)
iOS 10 对通知系统进行了彻底重构,统一了本地通知和远程通知,这是 iOS 版本特性面试的高频考点。
iOS 10 之前通知的问题
- 本地通知和远程通知 API 不统一,方法名相似容易混淆。
- 应用在前台时无法直接显示远程通知,需要手动处理。
- 已经发出的通知不能更新、不能撤销。
- 通知只能显示简单文本,不能包含图片、视频等富媒体。
- 权限申请方法分散。
iOS 10 通知的改进
- 统一框架:所有通知相关 API 统一到
UserNotifications.framework。 - 前台显示通知:应用在前台时也可以显示通知(通过
UNUserNotificationCenterDelegate)。 - 通知可更新/撤销:可以更新已发出的通知内容,也可以撤销未发出的通知。
- 富媒体通知:通知可以包含图片、视频、音频等附件。
- 自定义通知 UI:可以自定义通知的展开界面(Notification Content Extension)。
- 统一权限申请:本地和远程通知权限统一用一个方法申请。
基本使用流程
#import <UserNotifications/UserNotifications.h>
// 1. 注册通知权限
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | UNAuthorizationOptionBadge | UNAuthorizationOptionSound)
completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (granted) {
// 权限 granted
[center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
// 检查设置
}];
}
}];
// 2. 设置代理(处理前台通知等)
center.delegate = self;
// 3. 创建本地通知
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
content.title = @"通知标题";
content.body = @"通知内容";
content.sound = [UNNotificationSound defaultSound];
// 触发器(5 秒后)
UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:5 repeats:NO];
// 请求
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:@"myNotification"
content:content
trigger:trigger];
[center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
if (!error) {
NSLog(@"通知已添加");
}
}];
// 4. 前台收到通知时调用(需要实现 UNUserNotificationCenterDelegate)
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
willPresentNotification:(UNNotification *)notification
withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler {
// 前台也显示通知(横幅 + 声音)
completionHandler(UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionSound);
}
- iOS 10 通知有什么变化? 统一到 UserNotifications.framework、前台可显示、可更新/撤销、富媒体、自定义 UI。
- 本地通知和远程通知统一了吗? 统一了,都用
UNUserNotificationCenter管理。 - 前台怎么显示通知? 实现
UNUserNotificationCenterDelegate的willPresentNotification:方法,调用 completionHandler 传入显示选项。 - 怎么更新/撤销已发出的通知? 通过相同的 identifier 添加新请求来更新,用
removePendingNotificationRequestsWithIdentifiers:撤销。
五、UICollectionView 生命周期与预加载(Prefetching)
iOS 10 对 UICollectionView(以及 UITableView)的 Cell 生命周期和加载机制做了重要优化,这是性能优化相关的高频面试题。
iOS 10 之前的问题
- Cell 是一行一行加载的,当用户快速滑动时,整行 Cell 同时加载,容易造成掉帧卡顿。
- Cell 滑出屏幕后立即进入重用队列,滑回来时要重新走一遍完整生命周期。
- 没有预加载机制,数据加载和 UI 加载都在主线程,容易阻塞。
iOS 10 的改进
1. Cell 逐个加载(而非整行加载)
iOS 10 之前,CollectionView 是整行 Cell 一起准备和加载的;iOS 10 改成了一个一个 Cell 加载,分散了主线程压力,减少掉帧。
2. Cell 生命周期调整
| 方法 | iOS 10 之前 | iOS 10 及以后 |
|---|---|---|
prepareForReuse |
Cell 上边缘即将进入屏幕时调用 | Cell 还在屏幕外时就提前调用 |
cellForItemAtIndexPath: |
Cell 即将进入屏幕时调用 | 提前调用,在屏幕外就准备好数据 |
willDisplayCell: |
Cell 马上进入屏幕时调用 | Cell 真正需要显示时才调用 |
didEndDisplayingCell: |
Cell 完全离开屏幕时调用 | 不变 |
核心变化:数据准备(cellForItemAtIndexPath:)提前了,而最终显示(willDisplayCell:)推迟到真正需要显示时,做到"何时需要显示,何时才做最后的准备"。
3. Cell 缓存机制
iOS 10 之前,Cell 滑出屏幕后立即进入重用队列;iOS 10 中,系统会把滑出的 Cell 缓存一段时间,如果用户快速滑回来,不需要重新走完整生命周期,只需要调用 willDisplayCell: 就可以重新显示,提升了滑动流畅度。
4. Prefetching 预加载(最重要的新特性)
iOS 10 新增了 UICollectionViewDataSourcePrefetching 协议(UITableView 也有对应的 UITableViewDataSourcePrefetching),用于异步预加载数据。
// 遵守预加载协议
@interface ViewController () <UICollectionViewDataSourcePrefetching>
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 设置预加载代理
self.collectionView.prefetchingEnabled = YES; // 默认开启
self.collectionView.prefetchDataSource = self;
}
#pragma mark - UICollectionViewDataSourcePrefetching
// 预加载数据(异步调用,indexPaths 是有序的,按显示顺序排列)
- (void)collectionView:(UICollectionView *)collectionView prefetchItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths {
// 在这里异步预加载图片、数据等
for (NSIndexPath *indexPath in indexPaths) {
[self preloadImageAtIndexPath:indexPath];
}
}
// 取消预加载(可选,用户快速滑过不需要的数据时调用)
- (void)collectionView:(UICollectionView *)collectionView cancelPrefetchingForItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths {
// 取消不需要的预加载任务
}
@end
预加载的要点:
prefetchItemsAtIndexPaths:是异步调用的,不会阻塞主线程。indexPaths数组是有序的,按即将显示的顺序排列。- 预加载不能代替
cellForItemAtIndexPath:,只是辅助提前加载数据。 - 适合预加载的场景:图片下载、数据请求、复杂计算等耗时操作。
- UITableView 也有同样的预加载机制(
UITableViewDataSourcePrefetching)。
常见问题
- iOS 10 UICollectionView 有什么优化? Cell 逐个加载(非整行)、生命周期调整、Cell 缓存、Prefetching 预加载。
- Prefetching 是什么? iOS 10 新增的预加载协议,在 Cell 即将显示前异步预加载数据,提升滑动流畅度。
- prefetchItemsAtIndexPaths: 是在主线程吗? 不是,是异步调用的。
- 预加载能代替 cellForItemAtIndexPath: 吗? 不能,只是辅助提前加载数据。
- UITableView 有预加载吗? 有,iOS 10+ 同样支持
UITableViewDataSourcePrefetching。
六、其他 iOS 10 重要变化
1. UIRefreshControl 成为 UIScrollView 属性
iOS 10 之前,UIRefreshControl 只能在 UITableViewController 中使用;iOS 10 起,UIRefreshControl 变成了 UIScrollView 的属性,UICollectionView、UIScrollView 都可以直接用:
UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
[refreshControl addTarget:self action:@selector(loadData) forControlEvents:UIControlEventValueChanged];
self.collectionView.refreshControl = refreshControl; // iOS 10+
2. UITextContentType(自动填充)
iOS 10 新增 UITextContentType,给 UITextField 提示输入内容的语义类型,系统可以自动选择键盘类型、支持自动填充:
// 邮箱输入框,系统可以自动填充邮箱
self.emailField.textContentType = UITextContentTypeEmailAddress;
// 手机号
self.phoneField.textContentType = UITextContentTypeTelephoneNumber;
// 地址
self.addressField.textContentType = UITextContentTypeFullStreetAddress;
常见类型:UITextContentTypeUsername、UITextContentTypePassword、UITextContentTypeEmailAddress、UITextContentTypeTelephoneNumber、UITextContentTypeURL 等。
3. UIColor 支持 Display P3 色彩空间
iOS 10 新增了 Display P3 色彩空间相关的 API,支持更广的色域:
// Display P3 色彩空间创建颜色(iOS 10+)
UIColor *color = [UIColor colorWithDisplayP3Red:1.0 green:0.5 blue:0.2 alpha:1.0];
普通开发用 sRGB 即可,只有图片处理、设计类 App 才需要关注 Display P3。
总结
iOS 10 / Xcode 8 的高频面试知识点:
| 知识点 | 核心考点 |
|---|---|
| 隐私权限 | Info.plist 必须添加 UsageDescription,否则崩溃;常见权限 Key |
| 系统版本判断 | isOperatingSystemAtLeastVersion:、Swift #available、不推荐字符串比较 |
| ATS | 默认禁止 HTTP;全局 NSAllowsArbitraryLoads vs 域名 NSExceptionDomains;推荐 HTTPS |
| 通知大改 | 统一到 UserNotifications.framework;前台可显示;可更新/撤销;富媒体;基本使用流程 |
| UICollectionView 优化 | Cell 逐个加载;生命周期调整;Cell 缓存;Prefetching 预加载协议(异步、有序、辅助加载) |
| 其他变化 | UIRefreshControl 成为 UIScrollView 属性;UITextContentType 自动填充;Display P3 色彩 |
Xcode 8 当时的一些临时问题(插件屏蔽、日志混乱、Xib 格式兼容、Label 显示不全等)已经过时,不需要记忆。

浙公网安备 33010602011771号