What's New in Xcode?

2016 - 02 - 02

Posted by chengway

Getting started

本章 demo 叫做 Local Weather,是一个基于你的地理位置显示天气的 APP。你可以在真机上运行一下,下面让我们来探索 Xcode 的新特性。

想要看到 energy gauge 只能在真机上运行

Free provisioning

Xcode 7 最大的变化是允许你免费真机调试了,也就是说不用每年交 99 美金也可以在你的手机上运行你写的程序了,但如果提交上架还是需要加入开发者计划。

使用免费的 Provisioning 在真机上运行看这里

本章天气的 API 来自于 openweathermap.org

Energy impact gauge

我们来研究一下 Energy Impact

Energy Impact 显示了你 App 的耗电情况

Utilization 部分显示了当前时刻的 energy impact,右边的 average 是平均值

在底部,有四行分别代表 CPU、网络、位置和后台。每个小方格代表 1 秒的状态,如果这 1 秒钟状态是活跃的,则小方格会被灰色填满。

注意,CPU 和 位置状态始终是活跃状态,而网络活跃一段时间会停 4 ~ 5 秒钟,后台状态则是完全不活跃状态。

按下 Home 键将 App 退到后台,此时将会中止网络和位置状态,而后台状态变成活跃,CPU 始终是活跃状态。

观察一下 console,log 表明所有的后台程序将在 10.79 秒后结束,但是后台 energy graph 不会中止

Code browsing features

新版 Xcode 允许我们接手一个新项目时,能够快速熟悉整个 App 结构

Interface of Swift classes

Swift 和 Objective-C 的一个显著区别就是没有头文件了,但头文件让我们可以快速熟悉一个类的用途,真的是很方便。Xcode 7 终于让 Swift 也能显示头文件了

随便打开一个 swift 文件 WeatherViewController.swift ,打开 assistant editor 点击 assistant editor 菜单,选择 Counterparts (1) ▶ WeatherViewController.swift (Interface)

你也可以选择 Generated Interface

私有变量和方法并不会显示出来,还注意到头文件中的注释了吗?只有在源文件中使用 ////** 才会在 Interface 中显示注释

你也可以为 Objective-C 的头文件生成 Swift 接口。我们找一对用 Objective-C 写的类文件:RWHTTPManager.hRWHTTPManager.mRWHTTPManager 类用 OC 语法封装了 NSURLSession

现在用同样的方法转成 Swift 接口:

是不是很奇幻,用 Swift 生成的新接口貌似更加通俗易懂

这里有两个小 issue,可能是 OC 转 Swift 时编译器还不那么完美:

1.注意转换后 baseURL 作为属性是 optional,而作为参数是 non-optional 的。但是 baseURL 显示是必须的。我们在 OC 文件中修正一下,用 _Nonnull 替换 _Nullable

@property (nonatomic, strong) NSURL * _Nonnull baseURL;  

保存,再生成一次 Swift 接口,现在 baseURL 变成 non-optional 的了

var baseURL: NSURL  

2.另外一个 issue 是在 fetchJSONAtPath 方法中注意参数 relativePath,在 Swift 接口中也变成可选的了,但是通过文档我们知道这个参数不能为 nil,同样的方法,用 _Nonnull 替换 _Nullable

New documentation features

现在 Xcode 7 中的注释支持 markdown 语法了,NSHipster 有篇文章很棒,看这里

Find call hierarchy

现在查找指定方法在哪里被调用也很方便,选中方法右键或 Control 单击,弹出菜单选择 Find Call Hierarchy 即可

Decreasing energy impact

回到我们的 App 上来,现在有点费电,下面来修复一下。在开始前,先回顾下整个 App 的工作流程:

  1. 请求用户当前位置
  2. 一旦获取到用户位置,使用 Http 请求抓取当地天气
  3. 收到天气数据后,更新 UI 并且定时 15 秒后再次发送更新请求
  4. countdownLabel 显示倒计时,距下次发送请求还有多少时间。计时间隔为 0.1 秒

我们可以搜索 // Step,查看详细的步骤:

回想一下之前在 energy impact 中网络的活跃状态,活跃 10 ~ 11 秒后跟随 3 ~ 5 秒的非活跃状态,这是因为只要发出网络请求,他会持续 10 秒钟,即使已经完成了请求

这会导致每次网络请求都会很耗电,你可能认为 15 秒只发一次请求,只占 7%,对资源消耗并不严重,但这只是理想状态,真实的情况是每 15 秒发一次请求,网络请求会持续 11 秒,时间占用达到了 73%

这是我们第一个要修复的问题

Reducing network energy impact

天气虽然变化很快,但 15 秒更新一次的频率显然有点太快了。我们让他再每次启动时更新一次就够了,因为这也符合用户习惯,打开看一眼就关掉了。

注释掉 Step 6 每隔 15 秒更新的代码

// Step 6: Set a timer to fetch the weather again in 15 seconds  
// networkFetchTimer = NSTimer  
//  .scheduledTimerWithTimeInterval(15, ...  

运行,Energy Impact 结果好了很多

Reducing CPU energy impact

每隔 0.1 秒更新一次 view 的倒计时动画是消耗 CPU 的罪魁祸首,让我们停掉他

// Step 7: Update the countdown label every 0.1 seconds using a timer  
// countdownUpdateTimer = NSTimer.scheduledTimerWithTimeInterval(0.1 ...  

同时也不需要 countdownLabelStackView 显示了

// countdownLabelStackView.hidden = false  

现在又好了很多,不是吗?

Reducing location energy impact

我们获取到用户地理位置就不该再继续请求了,iOS 9 提供了新的请求方法 requestLocation() 他成功请求一次用户位置就会停止继续请求

// Step 2: Request the location  
locationManager.requestLocation()  

Reducing background energy impact

进入后台,我们通过控制台发现 11 秒后,后台任务已经完成了,但后台线程依然在消耗电池资源。这是因为调用 beginBackgroundTaskWithExpirationHandler(_:) 执行后台任务完成后,我们并没有调用 endBackgroundTask(_:) 告知系统你的任务已经结束了。

performBackgroundWork() 方法末尾手动补上就行了

print("Background work completed in: (formattedElapsedTime) " +  
  "sec")  
UIApplication.sharedApplication().endBackgroundTask(  
  backgroundTaskIdentifier)  

至此,你已经修复了所有的耗电问题

Core Location instrument

如果你想深挖一下用户位置是如何获取的,iOS 9 的新 Core Location instrument 很可能适合你。运行 App,去 energy gauge 界面,点击 Location 按钮

Instruments 将会打开运行 Core Location instrument

注意第二行 “CLLocationManager changed accuracy to kCLLocationAccuracyBest”,获取到这种精度的位置信息花费了 892.03 ms,天气 App 显然不需要这么细的精度,改成 kCLLocationAccuracyKilometer 就足够了

locationManager.desiredAccuracy = kCLLocationAccuracyKilometer  

这次精度调低,获取地理位置的时间也加快了上百倍

Playground improvements

天气 App 优化到此结束,我们下面来玩一下 Playground,Xcode 7 还是增加了很多新特性

Rich playground authoring

现在 playground 支持 Markdown 语法了,你可以用 Markdown 语法写完,然后开启 Render Documentation 渲染

开启渲染

单行注释用 ////**,多行用 /*: 和 */,更多 Markup 格式看这里

Playground pages

现在你可以在 playground 里添加多个页面了,回到 project navigator,点击左下角加号按钮,选择 New Page

添加完毕,将第一个命名为 Home

选中 Page Two,会发现 Previous 和 Next 两个链接(需要关闭 Render Documentation 才能看见)

//: [Previous](@previous)  

import Foundation  

var str = "Hello, playground"  

//: [Next](@next)  

@next 和 @previous 代表特殊符号,用来链接前后的页面,如果要链接到指定页面,需要提供 page name,如果名字中间有空格用 %20 代替

可以在 Page Two 页面的任意位置添加下面的链接(跳转到 Home)

//: [Jump to Home](Home)  

在 Home 页面添加跳转 Page Two 的链接

//: [Jump to Page Two](Page%20Two)  

Inline results

Inline results 允许你在 playground 里直接看运行结果,比如我们写个 view,在右边的 sidebar 里点击 Show Result,可以直接查看渲染好的 view

在 view 变量下面直接可以看到渲染结果

改变 view layer 的 borderWidth 到 40,渲染结果也跟着更新

Sources and resources

现在你可以在 playground 里添加辅助的源文件和资源文件了(分别放在 Sources 文件夹和 Resources 文件夹)将这些支持文件放到相关文件夹里,可以使结构更清晰

每一个 playground page 都有自己的 sources 和 resources 文件夹,且优先于使用自己的支援文件夹里的内容

Manually run playgrounds

不想每次在 playground 上做点修改都自动编译运行,机器还卡成狗,现在你可以手动控制允许了

Other improvements

StoryboardsInterface Builder 还有几个改进:

  • Control-dragging 从 View 到另一个 View 加约束的时候,按住 Option 键,将会看到可添加的 constants
  • 现在可以设置一个 view 的 layout margins 和为约束(constraint)设置一个标识(identifier),这样出现在 document outline 中的 constraint 有了更好的可读性。

还有很多新特性需要你自己去挖掘

Address sanitizer

Xcode 推出了一个新的工具,会帮助捕获在使用 Objective-C 或 C 时,可能出现的内存损坏错误

你可以去 Product Scheme Edit Scheme 中开启:Enable Address Sanitizer

开启后,Xcode 将添加额外的 instrumentation 来构建你的应用程序,以便于更好地找出内存错误

Right-to-left support

iOS 9 的一个重大更新是开始支持从右到左的语言,如果你使用了 Auto Layout,那么几乎不需要你做什么工作就能正常在这种语言环境下使用

Xcode 提供了一个不需要更改语言环境就能测试 view 在从右到左环境下的特性
,编辑你的 scheme,在 Options 界面下,展开 Application Language,这里有个 Right to Left Pseudolanguage 选项,勾上就能开心地测试了~

iOS 9 by Tutorials 全书到此结束,终于写完了,呼呼~


-EOF-

Table of Contents