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 之前通知的问题

  1. 本地通知和远程通知 API 不统一,方法名相似容易混淆。
  2. 应用在前台时无法直接显示远程通知,需要手动处理。
  3. 已经发出的通知不能更新、不能撤销。
  4. 通知只能显示简单文本,不能包含图片、视频等富媒体。
  5. 权限申请方法分散。

iOS 10 通知的改进

  1. 统一框架:所有通知相关 API 统一到 UserNotifications.framework。
  2. 前台显示通知:应用在前台时也可以显示通知(通过 UNUserNotificationCenterDelegate)。
  3. 通知可更新/撤销:可以更新已发出的通知内容,也可以撤销未发出的通知。
  4. 富媒体通知:通知可以包含图片、视频、音频等附件。
  5. 自定义通知 UI:可以自定义通知的展开界面(Notification Content Extension)。
  6. 统一权限申请:本地和远程通知权限统一用一个方法申请。

基本使用流程

#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 显示不全等)已经过时,不需要记忆。


posted @ 2016-11-15 10:49  Mr.陳  阅读(547)  评论(0)    收藏  举报