<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
        <title>wil.døg</title>
        <description>wil.døg - wildog</description>
        <link>http://wil.dog</link>
        <link>http://wil.dog</link>
        <lastBuildDate>2019-04-12T03:29:51+00:00</lastBuildDate>
        <pubDate>2019-04-12T03:29:51+00:00</pubDate>
        <ttl>1800</ttl>


        <item>
                <title>一个导出豆瓣数据的线上服务</title>
                <description>&lt;p&gt;豆瓣的搜索功能实在是太弱了，想在曾经标记过的一大堆东西里找到一个条目只能靠一页一页的翻，想统计个数据更是基本不可能的事，搜索了下网上唯一一个可以导出豆瓣数据的脚本已经是 2014 年的了，早就不能用了，顺便发现有这个需求的人挺多的，不管是数据统计还是备份用途，于是我写了一个可以导出豆瓣数据的线上服务，戳链接即可：&lt;a href=&quot;http://wil.dog/douban&quot;&gt;http://wil.dog/douban&lt;/a&gt;，可以导出如下图格式的 Excel 文件，然后按惯例记录一下开发途中遇到的问题。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;//wil.dog/static/images/exported.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;爬爬爬爬数据&quot;&gt;爬爬爬…爬数据&lt;/h2&gt;

&lt;p&gt;爬数据的过程因为比较简单只有两层：从用户条目列表爬到所有条目的 URL，然后再从这些 URL 爬具体的条目信息，我就懒得用爬虫框架了，一边爬一边用 bs4 抓需要的信息，利用 Python 标准库带的同步队列类 Queue 实现了一个多线程的管线化工作流程。顺便吐槽下豆瓣的前端写得真的很乱，看着页面结构很相似的电影、音乐、图书页面抓信息的时候都得使用不同的方法。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;403 Forbidden, 403 Forbidden, 403 Forbidden…&lt;/strong&gt;
&lt;br /&gt;这才是问题的重点，在本机上爬数据不伪装请求的情况下，过一会就被豆瓣 ban 掉了，收到的回应只有 403，于是开始研究怎么伪装请求。一开始想到的是 User-Agent 和 Referer 的问题，然而加上后并没有什么卵用，然后是自然就是 Cookie 的问题了。先把浏览器访问豆瓣得到的 Cookie 带到请求里，然后正常了运行了…两分钟后，开始收到 302 回应重定向到豆瓣的机器人验证页面，要求输入验证码，然而我不想做处理验证码这么麻烦的事情，当然也更不想做用代理这种麻烦的事情，于是开始着手研究 Cookie，然而并没有发现什么规律…可能就是真的没有什么规律！Cookie 里的 bid 字段看上去是唯一管用的东西，然后就试着随机生成了一大堆 bid，每个请求再从这一堆 bid 里随机选择一个带进 Cookie 里，最后数据总算是可以正常爬而且不被 ban 了。&lt;/p&gt;

&lt;h2 id=&quot;完全不够优雅的架构&quot;&gt;完全不够优雅的架构&lt;/h2&gt;

&lt;p&gt;服务器端程序是用 Flask 写的，用 uWSGI + Nginx 部署在阿里云上，然而我并没有一个通过备案的域名所以不能解析到阿里云的 80 端口上，所以只能另辟蹊径…最后的决定是前端以纯静态页面的形式托管在 Github Page 上，用 JSONP 协议和阿里云上的服务器端程序进行跨域通信。&lt;/p&gt;

&lt;p&gt;服务器端的接口很简单只有三个，&lt;code class=&quot;highlighter-rouge&quot;&gt;/addTask&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;/getState&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;/getFile&lt;/code&gt;, 分别用来请求添加新的导出任务、请求某个用户的任务状态和请求导出的 Excel 文件，前两个接口都以 JSONP 的格式返回状态内容、状态类型和可选的导出文件的 URL，前端页面在提交 &lt;code class=&quot;highlighter-rouge&quot;&gt;/addTask&lt;/code&gt; 请求后会以 ajax 的方式轮训 &lt;code class=&quot;highlighter-rouge&quot;&gt;/getState&lt;/code&gt; 获取并显示最新的任务状态直到任务成功或失败。&lt;/p&gt;

&lt;p&gt;考虑到效率和避免滥用问题，导出的文件会在服务器暂存 24 小时，24 小时内请求添加同样的导出任务都会直接返回暂存的文件，与此同时我把最大并发任务数限制到了 6 个，超过之后请求添加新的导出任务会提示排队，测试了一下 6 个并发任务基本上就是我这个 1G RAM 单核 CPU 阿里云的极限了…&lt;/p&gt;

&lt;p&gt;前端页面和服务器端源码都放 Github 上了，和部署时用的程序有少量区别，由兴趣的可以给个 star：&lt;a href=&quot;https://github.com/Wildog/douban-exporter&quot;&gt;https://github.com/Wildog/douban-exporter&lt;/a&gt;&lt;/p&gt;

</description>
                <link>http://wil.dog/2016/10/26/the-make-of-douban-exporter/</link>
                <guid>http://wil.dog/2016/10/26/the-make-of-douban-exporter</guid>
                <pubDate>2016-10-26T00:00:00+00:00</pubDate>
        </item>

        <item>
                <title>iOS 10 Extensions 开发实战</title>
                <description>&lt;p&gt;首先声明这并不是一篇实战教程，只是记录下开发中遇到的零零碎碎的问题和自己的解决办法。&lt;/p&gt;

&lt;p&gt;iOS 10 最不能让我接受的改版就是 Health.app，应用层级变多，现在要点好几下才能看到自己每周的步数和运动距离统计。与此同时 iOS 10 的 Widgets 面板给了 Widget 更多的空间，想着能有一款让人一目了然地查看一周运动步数的 Widget 就好了，然而找了下 App Store 上目前并没有很好的成品，于是自己动手开写，途中顺便也尝试了下 iMessage Extension。&lt;/p&gt;

&lt;p&gt;先扔 Repo 地址和效果图：&lt;a href=&quot;https://github.com/Wildog/iOS-10-Steps-Widget&quot;&gt;https://github.com/Wildog/iOS-10-Steps-Widget&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;//wil.dog/static/images/steps-widget.gif&quot; alt=&quot;Steps Widget&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;ios-10-widget-新特性折叠展开&quot;&gt;iOS 10 Widget 新特性：折叠/展开&lt;/h2&gt;

&lt;p&gt;如上面的效果图所示，iOS 10 通过展开的方式给了 Widget 更多空间，展开状态&lt;code class=&quot;highlighter-rouge&quot;&gt;NCWidgetDisplayModeExpanded&lt;/code&gt;下的最大高度是系统决定的，折叠状态 &lt;code class=&quot;highlighter-rouge&quot;&gt;NCWidgetDisplayModeCompact&lt;/code&gt;下的高度则是固定的（110 左右），在 &lt;code class=&quot;highlighter-rouge&quot;&gt;TodayViewController&lt;/code&gt; 中可以通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;NCWidgetProviding&lt;/code&gt; 协议中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;widgetActiveDisplayModeDidChange:&lt;/code&gt; 获取状态变化并设置需要的高度：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-objc&quot; data-lang=&quot;objc&quot;&gt;&lt;span class=&quot;k&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;widgetActiveDisplayModeDidChange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NCWidgetDisplayMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;activeDisplayMode&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;withMaximumSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CGSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;maxSize&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;activeDisplayMode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NCWidgetDisplayModeExpanded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;preferredContentSize&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CGSizeMake&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;280&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;activeDisplayMode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NCWidgetDisplayModeCompact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;preferredContentSize&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maxSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;获取-healthkit-信息&quot;&gt;获取 HealthKit 信息&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;h3 id=&quot;新的访问控制&quot;&gt;新的访问控制&lt;/h3&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;iOS 10 中增加了新的隐私访问控制，需要在 info.plist 中设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;NSHealthShareUsageDescription&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;NSHealthUpdateUsageDescription&lt;/code&gt; 的值作为数据使用的描述，两个值都必须设置，否则会收到类似下面的通知：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSHealthShareUsageDescription key with a string value explaining to the user how the app uses this data.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如下图设置好这两个值过后就能正常地请求应用授权了：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;//wil.dog/static/images/healthkit-usage-description.png&quot; alt=&quot;Steps Widget&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;h3 id=&quot;异步请求带来的问题&quot;&gt;异步请求带来的问题&lt;/h3&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;获取 HealthKit 数据的方式是向 &lt;code class=&quot;highlighter-rouge&quot;&gt;HKHealthStore&lt;/code&gt; 的实例发送 &lt;code class=&quot;highlighter-rouge&quot;&gt;executeQuery:&lt;/code&gt; 信息，而这个方法是异步调用的。我一开始的做法是在 &lt;code class=&quot;highlighter-rouge&quot;&gt;viewDidLoad&lt;/code&gt; 中直接调用一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;queryHealthData&lt;/code&gt; 方法，在这个方法里面执行一系列（一周的数据，按天请求）的 &lt;code class=&quot;highlighter-rouge&quot;&gt;executeQuery: &lt;/code&gt;，返回后再交给图表绘制。然而数据交给图表时几乎不可能是完整的，因为 &lt;code class=&quot;highlighter-rouge&quot;&gt;executeQuery: &lt;/code&gt; 的异步请求此时并没有执行完，最终导致应用崩溃。所以需要一个办法在所有的异步请求全部处理完之后再进行其它处理，&lt;code class=&quot;highlighter-rouge&quot;&gt;dispatch_group&lt;/code&gt; 可以很好的解决，同时 &lt;code class=&quot;highlighter-rouge&quot;&gt;dispatch_group&lt;/code&gt; 内部的任务也是并发进行的：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-objc&quot; data-lang=&quot;objc&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// 创建 dispatch_group
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dispatch_group_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hkGroup&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dispatch_group_create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 依次执行请求
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(......)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 创建 query
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;HKStatisticsQuery&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HKStatisticsQuery&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
                        &lt;span class=&quot;nf&quot;&gt;initWithQuantityType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stepType&lt;/span&gt;
                        &lt;span class=&quot;nf&quot;&gt;quantitySamplePredicate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;predicate&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HKStatisticsOptionCumulativeSum&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;completionHandler&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:^&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HKStatisticsQuery&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HKStatistics&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NSError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sumQuantity&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;doubleValueForUnit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HKUnit&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;countUnit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]];&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arrayForData&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;addObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NSNumber&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;numberWithDouble&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]];&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 数据存储完后离开 dispatch_group，可以理解为信号量 +1
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;dispatch_group_leave&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hkGroup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}];&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 执行异步请求前进入 dispatch_group，可以理解为信号量 -1
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;dispatch_group_enter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hkGroup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;healthStore&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;executeQuery&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 最后等待所有异步请求完成
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dispatch_group_notify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hkGroup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dispatch_get_main_queue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 通知主线程绘制图表
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;h3 id=&quot;锁屏状态下无法访问-healthkit-数据&quot;&gt;锁屏状态下无法访问 HealthKit 数据&lt;/h3&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果你尝试在锁屏状态下通过 Widget 访问 HealthKit 数据，你会在 Console 中收到类似下面的信息：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Widget[3459:674785] Error Domain=com.apple.healthkit Code=6 &quot;Protected health data is inaccessible&quot; UserInfo={NSLocalizedDescription=Protected health data is inaccessible}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;很遗憾在锁屏状态下由于隐私保护是没法访问 HealthKit 信息的，所以我们需要对此类错误进行处理并缓存之前的数据用于锁屏状态下显示。由于数据很简单而且数量不多，数据的缓存用 &lt;code class=&quot;highlighter-rouge&quot;&gt;NSUserDefaults&lt;/code&gt; 实现相当简单：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-objc&quot; data-lang=&quot;objc&quot;&gt;&lt;span class=&quot;n&quot;&gt;NSUserDefaults&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;userDefaults&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NSUserDefaults&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 保存数据
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;userDefaults&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;setObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arrayForData&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;forKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;snapshot&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 恢复数据
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NSArray&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arrayForData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;userDefaults&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;arrayForKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;snapshot&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;然后我们在之前 query 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;completionHandler&lt;/code&gt; 块中加入对错误的检测并在图表上显示对应提示，并设置一个全局的 flag 用于检测到错误时显示之前缓存的数据。&lt;/p&gt;

&lt;h2 id=&quot;绘制线形图&quot;&gt;绘制线形图&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;//wil.dog/static/images/chart-view.png&quot; alt=&quot;Steps Widget&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;h3 id=&quot;绘制渐变&quot;&gt;绘制渐变&lt;/h3&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;通过得到的数据绘制好一条 &lt;code class=&quot;highlighter-rouge&quot;&gt;BezierPath&lt;/code&gt; 路径后，再利用这个路径创建一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;CAShapeLayer&lt;/code&gt; 形状层，设置好属性和动画后，再创建一个和这个 View 一样大的 &lt;code class=&quot;highlighter-rouge&quot;&gt;CAGradientLayer&lt;/code&gt; 渐变层，最后将渐变层作为 sublayer 添加到 &lt;code class=&quot;highlighter-rouge&quot;&gt;self.layer&lt;/code&gt; 上，并把渐变层的 &lt;code class=&quot;highlighter-rouge&quot;&gt;mask&lt;/code&gt; 属性设置为之前创建的 &lt;code class=&quot;highlighter-rouge&quot;&gt;CAShapeLayer&lt;/code&gt; 形状层就实现了：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-objc&quot; data-lang=&quot;objc&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// 创建形状层
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CAShapeLayer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chartLineShape&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CAShapeLayer&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;layer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;chartLineShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chartLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CGPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;chartLineShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineWidth&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chartLineWidth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;chartLineShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strokeColor&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UIColor&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;colorWithHue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;52&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;saturation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;brightness&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;83&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alpha&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CGColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;chartLineShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fillColor&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UIColor&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;clearColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CGColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;chartLineShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineCap&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kCALineCapRound&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;chartLineShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineJoin&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kCALineJoinRound&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 创建动画
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CABasicAnimation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;drawAnimation&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CABasicAnimation&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;animationWithKeyPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;strokeEnd&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;drawAnimation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;duration&lt;/span&gt;            &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;animationDuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;drawAnimation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;repeatCount&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;drawAnimation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;removedOnCompletion&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;YES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;drawAnimation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fromValue&lt;/span&gt;           &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NSNumber&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;numberWithFloat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;drawAnimation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;toValue&lt;/span&gt;             &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NSNumber&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;numberWithFloat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;drawAnimation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timingFunction&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CAMediaTimingFunction&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;functionWithControlPoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;348&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;000&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;285&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;743&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chartLineShape&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;addAnimation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;drawAnimation&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;forKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;drawChartLineAnimation&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 创建渐变层
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CAGradientLayer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gradientLayer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CAGradientLayer&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;layer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gradientLayer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;            &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CGRectMake&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gradientLayer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;colors&lt;/span&gt;           &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;@[(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__bridge&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UIColor&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;colorWithHue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;57&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;saturation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;74&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;brightness&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;86&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alpha&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CGColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__bridge&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UIColor&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;colorWithHue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;52&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;saturation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;brightness&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;76&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alpha&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CGColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__bridge&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UIColor&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;colorWithHue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;52&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;saturation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;brightness&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;83&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alpha&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CGColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gradientLayer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;startPoint&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CGPointMake&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gradientLayer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endPoint&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CGPointMake&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 设置遮罩
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layer&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;addSublayer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gradientLayer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gradientLayer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mask&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chartLineShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;h3 id=&quot;检测触摸点击&quot;&gt;检测触摸点击&lt;/h3&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我单独写了一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;ChartNodeView&lt;/code&gt; 来表示和绘制节点，并在 &lt;code class=&quot;highlighter-rouge&quot;&gt;ChartView&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;drawRect:&lt;/code&gt; 中创建节点并将它们作为 subview 添加进来，触摸点击节点会触发动画效果和显示节点的相关信息，所以需要检测触摸事件并通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;hitTest:&lt;/code&gt; 识别触摸对象来判断节点序号：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-objc&quot; data-lang=&quot;objc&quot;&gt;&lt;span class=&quot;k&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;touchesBegan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NSSet&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;touches&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;withEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UIEvent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;touchPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;touches&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;withEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;super&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;touchesBegan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;touches&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;withEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;touchPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NSSet&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;touches&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;withEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UIEvent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;UITouch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;touch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;touches&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;anyObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;CGPoint&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;touchPoint&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;touch&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;locationInView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;UIView&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;touchView&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;hitTest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;touchPoint&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;withEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;// 判断触摸对象是否为节点
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;touchView&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;isKindOfClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ChartNodeView&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]])&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 重新高亮节点
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UIView&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subview&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subviews&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subview&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;isKindOfClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ChartNodeView&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]])&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;ChartNodeView&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nodeView&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ChartNodeView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subview&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nodeView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isActive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nodeView&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;toggleState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ChartNodeView&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;touchNode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ChartNodeView&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;touchView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;touchNode&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;toggleState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 显示节点信息的逻辑交由 delegate 处理
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;_lastSelected&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;touchNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;delegate&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;respondsToSelector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;@selector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;clickedNodeAtIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:)])&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;delegate&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;clickedNodeAtIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_lastSelected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;这个线型图的实现很简单，写的时候也注意了一定的可复用性，如果有需要的话可以直接拿走用。&lt;/p&gt;

&lt;h2 id=&quot;ios-10-imessage-extension&quot;&gt;iOS 10 iMessage Extension&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;//wil.dog/static/images/steps-imsg-ext.png&quot; alt=&quot;Steps Widget&quot; /&gt;&lt;/p&gt;

&lt;p&gt;iOS 10 提供的 iMessage 扩展可以生成漂亮的 rich message。把之前 Widget 里写好的 ViewController 拿过来改改就可以直接用，这里记录下信息的生成，使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;MSMessageTemplateLayout&lt;/code&gt; 可以创建带&lt;a href=&quot;https://developer.apple.com/reference/messages/msmessagetemplatelayout&quot;&gt;媒体文件、标题和说明&lt;/a&gt;的布局，创建 &lt;code class=&quot;highlighter-rouge&quot;&gt;NSMessage&lt;/code&gt; 后，设置其布局属性，然后通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;MSMessagesAppViewController&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;activeConversation&lt;/code&gt; 属性获取当前对话并执行 &lt;code class=&quot;highlighter-rouge&quot;&gt;insertMessage:&lt;/code&gt; 来插入信息，整个过程完毕后交给用户添加评论或发送：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-objc&quot; data-lang=&quot;objc&quot;&gt;&lt;span class=&quot;n&quot;&gt;MSMessageTemplateLayout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MSMessageTemplateLayout&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;layout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;image&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;layout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;caption&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;@&quot;This is a caption&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;MSMessage&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MSMessage&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;layout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;URL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NSURL&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;URLWithString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&quot;emptyURL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;activeConversation&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;insertMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;completionHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NSError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// error handling
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}];&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;截取图表为图片时使用的方法是 &lt;code class=&quot;highlighter-rouge&quot;&gt;drawViewHierarchyInRect:afterScreenUpdates&lt;/code&gt;，创建 image context 时注意使用的方法是 &lt;code class=&quot;highlighter-rouge&quot;&gt;UIGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGFloat scale)&lt;/code&gt; 且 &lt;code class=&quot;highlighter-rouge&quot;&gt;scale&lt;/code&gt; 的值需要设为 0，表示 scale factor 由设备决定，如果使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;UIGraphicsBeginImageContext(CGSize size)&lt;/code&gt; 的话默认的 scale 值为 1，在 2x, 3x 设备上会显示模糊的图像：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-objc&quot; data-lang=&quot;objc&quot;&gt;&lt;span class=&quot;n&quot;&gt;UIGraphicsBeginImageContextWithOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineChartView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bounds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineChartView&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;drawViewHierarchyInRect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lineChartView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bounds&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;afterScreenUpdates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;YES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;UIImage&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;image&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UIGraphicsGetImageFromCurrentImageContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;UIGraphicsEndImageContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;顺便吐槽-xcode-8&quot;&gt;顺便吐槽 Xcode 8&lt;/h2&gt;

&lt;p&gt;升级之后整个人都懵逼了，所有的插件都没了，包括没法离开的 XVim，看了下 &lt;a href=&quot;https://github.com/alcatraz/Alcatraz/issues/475&quot;&gt;Alcatraz Issues&lt;/a&gt; 里面的讨论，官方采用了 runtime library validation 对 Xcode 插件进行验证，并提供了新的 Xcode 插件协议 Xcode Source Editor Extensions（然而目前只能提供基本的替换功能），主要是为了防止 XcodeGhost 这样的恶意插件。废话说得再多，我依然不能接受。翻遍 Github 找到了个很好用的工具 &lt;a href=&quot;https://github.com/inket/update_xcode_plugins&quot;&gt;inket/update_xcode_plugins&lt;/a&gt;，不光可以 unsign Xcode 使其重新支持插件，还可以给插件自动添加 UUID，经过十分钟的拷贝和 unsign，Xcode 8 总算是能正常使用了。&lt;/p&gt;

</description>
                <link>http://wil.dog/2016/09/17/ios-10-widget-development/</link>
                <guid>http://wil.dog/2016/09/17/ios-10-widget-development</guid>
                <pubDate>2016-09-17T00:00:00+00:00</pubDate>
        </item>

        <item>
                <title>记一则流媒体传输上的坑</title>
                <description>&lt;p&gt;最近用 Flask 写了个文件系统服务器，想在前端 streaming 服务器上的媒体文件，先是直接写了个生成器方法，传递给 stream_with_context 后生成 Response 返回，在本地测试时发现了两个怪异现象。&lt;/p&gt;

&lt;p&gt;一个是部分 mp4 文件能正常播放而其他 mp4 文件在播放时显示出错，另一个是 mp3 文件在不同浏览器下播放行为不统一，在 Chrome 下能正常显示总时长，而在 Safari 下不显示总时长而只显示 Live Broadcasting。&lt;/p&gt;

&lt;h2 id=&quot;分析&quot;&gt;分析&lt;/h2&gt;

&lt;p&gt;针对第一个问题，分析文件后发现能正常播放的 mp4 文件都是我之前通过 HandBrake 转码过后的 web optimized mp4 文件，如下图所见，此类文件的 metadata(moov) 位于媒体数据(mdat)之前，浏览器请求视频文件后从文件开头开始接收，如果 metadata 在文件开头，浏览器就能正常读取信息并完整地播放视频。而大多数视频的 metadata 都在媒体数据之后，导致浏览器不能正常 streaming。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;//wil.dog/static/images/moov.jpg&quot; alt=&quot;moov before mdat&quot; /&gt;&lt;/p&gt;

&lt;p&gt;第二个问题通过对两个浏览器的请求进行抓包比较得到问题所在：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;//wil.dog/static/images/chrome-header.png&quot; alt=&quot;chrome&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;//wil.dog/static/images/safari-header.png&quot; alt=&quot;safari&quot; /&gt;&lt;/p&gt;

&lt;p&gt;从图上可以看出 Safari 在请求 mp3 文件之前先发送了一个头部带有 &lt;code class=&quot;highlighter-rouge&quot;&gt;Range: bytes=0-1&lt;/code&gt; 的请求给服务器，如果服务器的响应头部没有对应的 Content-Range 或者响应码不是 206 Partial Content，而是直接回应整个文件，Safari 就会认为这是一个 Live Broadcasting 流，并把 audio.duration 这个 tag 的值设为无限大。&lt;/p&gt;

&lt;h2 id=&quot;总结&quot;&gt;总结&lt;/h2&gt;

&lt;p&gt;总结一下这两个问题的解决办法可以得出流媒体传输的必要条件：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;视频文件需要是 fast-start 或者 web optimized 的，可以通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;ffmpeg -i input.mp4 -movflags faststart -acodec copy -vcodec copy output.mp4&lt;/code&gt; 处理得到&lt;/li&gt;
  &lt;li&gt;服务器端需要正确地响应带有 Range 头部的请求，以及返回正确的 Content-Type 头部&lt;/li&gt;
  &lt;li&gt;服务器端不压缩地直接返回媒体文件，Content-Encoding 头部的值为 identity&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;最终代码&quot;&gt;最终代码&lt;/h2&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;partial_response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;file_size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getsize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file_size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file_size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'rb'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;seek&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;bytes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;mi&quot;&gt;206&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;                                    &lt;span class=&quot;c&quot;&gt;# Partial Content&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;mimetype&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mimetypes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;guess_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# Content-Type must be correct&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;direct_passthrough&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;                &lt;span class=&quot;c&quot;&gt;# Identity encoding&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;'Content-Range'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'bytes {0}-{1}/{2}'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;'Accept-Ranges'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'bytes'&lt;/span&gt;                &lt;span class=&quot;c&quot;&gt;# Accept request with Range header&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'Range'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;re&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'bytes=(?P&amp;lt;start&amp;gt;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;d+)-(?P&amp;lt;end&amp;gt;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;d+)?'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'start'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'end'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;
        
&lt;span class=&quot;nd&quot;&gt;@app.route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'/&amp;lt;path:p&amp;gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;v_get_video_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;''&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'Range'&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;partial_response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;send_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'Content-Disposition'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'attachment'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;make_response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'Not found'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;404&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;最后的 &lt;code class=&quot;highlighter-rouge&quot;&gt;res.headers.add('Content-Disposition', 'attachment')&lt;/code&gt; 是为了让 Safari 以下载而不是新窗口播放的方式来打开媒体文件的超链接。&lt;/p&gt;

&lt;p&gt;如此一来就能正常的传输流媒体了：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;//wil.dog/static/images/streaming.png&quot; alt=&quot;streaming&quot; /&gt;&lt;/p&gt;

&lt;p&gt;完整项目见此：&lt;a href=&quot;https://github.com/Wildog/flask-file-server&quot;&gt;https://github.com/Wildog/flask-file-server&lt;/a&gt;&lt;/p&gt;

</description>
                <link>http://wil.dog/2016/09/07/streaming-with-flask/</link>
                <guid>http://wil.dog/2016/09/07/streaming-with-flask</guid>
                <pubDate>2016-09-07T00:00:00+00:00</pubDate>
        </item>

        <item>
                <title>用 Pythonista 写 UI</title>
                <description>&lt;p&gt;&lt;a href=&quot;http://omz-software.com/pythonista/&quot;&gt;Pythonista&lt;/a&gt; 从 1.5 版本起到现在的 3.0 版本， 陆续增加了 UI 编辑器 、 Share Extension 、断点调试等功能，原本就是 iOS 上日常脚本神器的它现在已经成了功能完善的 Python 开发环境，拿着手机打发时间的同时写个像样的 App 也不在话下。下面是这两天用 Pythonista 写的两个小玩具，顺便记录一下途中遇到的坑。&lt;/p&gt;

&lt;p&gt;先附上 Repo 地址：&lt;a href=&quot;https://github.com/Wildog/pythonista-toys&quot;&gt;https://github.com/Wildog/pythonista-toys&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;exifpy&quot;&gt;EXIF.py&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;//wil.dog/static/images/exif.jpg&quot; alt=&quot;EXIF&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这是一个用于查看照片信息的 Extension，能显示 EXIF 信息、RGB 直方图和解析后的地理位置，可以触发 Share Sheet 地方都能使用。&lt;/p&gt;

&lt;p&gt;Pythonista 自带了 PIL 模块，能很方便的获取 EXIF 信息和执行图片处理。但坑也是主要都在图片方面，获取图片 EXIF 信息只能从 PIL.Image 的实例中获取，而在 ImageView 上显示的图片又必须是 ui.Image 的实例，坑爹的是 ui 模块的文档里面一直把这两个类混淆，而且也没有自带两个类之间的转换方法，遂自己动手：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;pil2ui&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imgIn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BytesIO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;imgIn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'JPEG'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;imgOut&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getvalue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;imgOut&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;解决了图片转换的问题又发现由于在 Share Extension 中运行脚本时内存上限较小，尺寸较大的图片得先压缩才能显示，然而 Image.resize() 依然不能摆脱内存问题，最终用 Image.thumbnail() 解决，注意这个方法的效果是 in-place 的。&lt;/p&gt;

&lt;p&gt;现在图片能正常在 ImageView 中显示了，但又出现了新的问题：iOS 设备拍摄的照片无法正确显示方向，窥探了下这些照片的 EXIF 信息发现照片本身都是横向的，系统显示的时候才利用 EXIF 信息里的 ‘Orientation’ 决定方向，所以现在得自己实现这个过程，直接用 Image.rotate() 解决：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;n&quot;&gt;orientations&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;180&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;270&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;90&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exif&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'Orientation'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;img&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rotate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;orientations&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exif&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'Orientation'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;最后利用 &lt;a href=&quot;https://github.com/geopy/geopy&quot;&gt;GeoPy&lt;/a&gt; 实现坐标到地址的解码再显示出来就行了，安装包时还顺便发现了 &lt;a href=&quot;https://github.com/ywangd/stash&quot;&gt;StaSh&lt;/a&gt; 这个神器，运行在 Pythonista 上的模拟 shell，可以直接使用 pip 进行包管理。&lt;/p&gt;

&lt;h2 id=&quot;garfieldpy&quot;&gt;Garfield.py&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;//wil.dog/static/images/garfield.jpg&quot; alt=&quot;garfield&quot; /&gt;&lt;/p&gt;

&lt;p&gt;一个用来看每日加菲猫漫画的小脚本，除了显示和保存漫画以外还能查看从 GoComics 上抓取的评论。画 UI 的时候发现虽然有 Auto-Resizing / Flex 功能，但远不及 iOS 的 AutoLayout 来的完善，甚至都没法在同层级的 view 之间建立约束，不过整体代码只有百来行，远比写个真正的 App 轻松多了。&lt;/p&gt;

</description>
                <link>http://wil.dog/2016/09/03/script-ui-with-pythonista/</link>
                <guid>http://wil.dog/2016/09/03/script-ui-with-pythonista</guid>
                <pubDate>2016-09-03T00:00:00+00:00</pubDate>
        </item>

        <item>
                <title>Ariafred: 利用 Alfred 管理 Aria2</title>
                <description>&lt;p&gt;&lt;a href=&quot;https://aria2.github.io&quot;&gt;Aria2&lt;/a&gt; 对于我这种长期使用百度云和迅雷离线的 Mac 用户简直就是救星, 再不用忍受百度云蛋疼的同步盘客户端和万年不更新还死耗资源的迅雷客户端, 找了下 Aria2 基本只有 web 前端, 感觉不如配合 &lt;a href=&quot;https://www.alfredapp.com&quot;&gt;Alfred 2&lt;/a&gt; 来得方便, 趁着周末用 Python 写了个 Alfred Workflow, 支持 Aria2 的大多数操作和后台通知. 截图如下:
&lt;img src=&quot;//wil.dog/static/images/ariafred.gif&quot; alt=&quot;screenshot&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;下载&quot;&gt;下载&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;百度云直接下载可用: &lt;a href=&quot;http://pan.baidu.com/s/1skLQsG1&quot;&gt;http://pan.baidu.com/s/1skLQsG1&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Github Repo 地址: &lt;a href=&quot;https://github.com/Wildog/Ariafred&quot;&gt;https://github.com/Wildog/Ariafred&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;使用说明&quot;&gt;使用说明&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;h3 id=&quot;激活-ariafred&quot;&gt;激活 Ariafred&lt;/h3&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;安装 Workflow 后你可以在 Hotkey 里设置全局快捷键(个人推荐&lt;code class=&quot;highlighter-rouge&quot;&gt;Command&lt;/code&gt; + &lt;code class=&quot;highlighter-rouge&quot;&gt;Shift&lt;/code&gt; + &lt;code class=&quot;highlighter-rouge&quot;&gt;A&lt;/code&gt;)或者在 Alfred 里输入关键词 &lt;code class=&quot;highlighter-rouge&quot;&gt;aria&lt;/code&gt; 来激活 Ariafred&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;h3 id=&quot;过滤下载&quot;&gt;过滤下载&lt;/h3&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
  &lt;li&gt;直接输入搜索关键词可按名称搜索下载, 可同时输入多个搜索关键词&lt;/li&gt;
  &lt;li&gt;输入状态关键词 &lt;code class=&quot;highlighter-rouge&quot;&gt;active&lt;/code&gt; / &lt;code class=&quot;highlighter-rouge&quot;&gt;done&lt;/code&gt; / &lt;code class=&quot;highlighter-rouge&quot;&gt;paused&lt;/code&gt; / &lt;code class=&quot;highlighter-rouge&quot;&gt;pending&lt;/code&gt; / &lt;code class=&quot;highlighter-rouge&quot;&gt;error&lt;/code&gt; 可分别查看进行中/已完成/已暂停/等待中/出错的下载&lt;/li&gt;
  &lt;li&gt;状态关键词和搜索关键词可搭配使用:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;//wil.dog/static/images/filter.png&quot; alt=&quot;filter&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;h3 id=&quot;查看全局状态&quot;&gt;查看全局状态&lt;/h3&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;//wil.dog/static/images/stat.png&quot; alt=&quot;stat&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;输入关键词 &lt;code class=&quot;highlighter-rouge&quot;&gt;stat&lt;/code&gt; 可查看全局状态&lt;/li&gt;
  &lt;li&gt;在 Active / Waiting / Stopped 上按下 &lt;code class=&quot;highlighter-rouge&quot;&gt;Enter&lt;/code&gt; 可查看对应状态的下载&lt;/li&gt;
  &lt;li&gt;在 Download / Upload 上按下 &lt;code class=&quot;highlighter-rouge&quot;&gt;Enter&lt;/code&gt; 可进一步设置下载/上传限速&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;h3 id=&quot;新建下载&quot;&gt;新建下载&lt;/h3&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;输入关键词 &lt;code class=&quot;highlighter-rouge&quot;&gt;add&lt;/code&gt; 和下载链接, 支持 HTTP/FTP/SFTP/Magnet 链接&lt;/p&gt;

&lt;p&gt;建议在 &lt;code class=&quot;highlighter-rouge&quot;&gt;aria2.conf&lt;/code&gt; 配置文件中增加默认下载路径如 &lt;code class=&quot;highlighter-rouge&quot;&gt;dir=/foo/bar&lt;/code&gt;, 在 Ariafred 中创建的下载都会下载到这个路径下&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;h3 id=&quot;新建-bt-下载&quot;&gt;新建 BT 下载&lt;/h3&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;//wil.dog/static/images/bt.png&quot; alt=&quot;BT&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在 .torrent 文件上执行 &lt;a href=&quot;https://www.alfredapp.com/help/features/file-search/#file-actions&quot;&gt;File Action&lt;/a&gt; 后选择 ‘Add BT download to Aria2’&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;h3 id=&quot;显示下载文件&quot;&gt;显示下载文件&lt;/h3&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
  &lt;li&gt;对所选下载按下 &lt;code class=&quot;highlighter-rouge&quot;&gt;Enter&lt;/code&gt; 在 Finder 中显示下载文件&lt;/li&gt;
  &lt;li&gt;对所选下载按下 &lt;code class=&quot;highlighter-rouge&quot;&gt;Ctrl&lt;/code&gt; + &lt;code class=&quot;highlighter-rouge&quot;&gt;Enter&lt;/code&gt; 在 Alfred 中显示下载文件&lt;/li&gt;
  &lt;li&gt;点击下载通知可在 Finder 中显示相关下载文件&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;h3 id=&quot;暂停继续下载&quot;&gt;暂停/继续下载&lt;/h3&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
  &lt;li&gt;对所选下载按下 &lt;code class=&quot;highlighter-rouge&quot;&gt;Command&lt;/code&gt; + &lt;code class=&quot;highlighter-rouge&quot;&gt;Enter&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;或者输入关键词 &lt;code class=&quot;highlighter-rouge&quot;&gt;pause&lt;/code&gt; / &lt;code class=&quot;highlighter-rouge&quot;&gt;resume&lt;/code&gt; 后对所选下载按下 &lt;code class=&quot;highlighter-rouge&quot;&gt;Enter&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;使用关键词 &lt;code class=&quot;highlighter-rouge&quot;&gt;pauseall&lt;/code&gt; / &lt;code class=&quot;highlighter-rouge&quot;&gt;resumeall&lt;/code&gt; 暂停/继续所有下载&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;h3 id=&quot;移除下载&quot;&gt;移除下载&lt;/h3&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
  &lt;li&gt;对所选下载按下 &lt;code class=&quot;highlighter-rouge&quot;&gt;Option&lt;/code&gt; + &lt;code class=&quot;highlighter-rouge&quot;&gt;Enter&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;或者输入关键词 &lt;code class=&quot;highlighter-rouge&quot;&gt;remove&lt;/code&gt; 后对所选下载按下 &lt;code class=&quot;highlighter-rouge&quot;&gt;Enter&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;h3 id=&quot;复制下载链接到剪贴板&quot;&gt;复制下载链接到剪贴板&lt;/h3&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
  &lt;li&gt;对所选下载按下 &lt;code class=&quot;highlighter-rouge&quot;&gt;Control&lt;/code&gt; + &lt;code class=&quot;highlighter-rouge&quot;&gt;Enter&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;或者输入关键词 &lt;code class=&quot;highlighter-rouge&quot;&gt;url&lt;/code&gt; 后对所选下载按下 &lt;code class=&quot;highlighter-rouge&quot;&gt;Enter&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;h3 id=&quot;清除已停止下载&quot;&gt;清除已停止下载&lt;/h3&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;使用关键词 &lt;code class=&quot;highlighter-rouge&quot;&gt;clear&lt;/code&gt;, 会清除所有已完成和出错的下载&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;h3 id=&quot;设置限速和最大同时下载数&quot;&gt;设置限速和最大同时下载数&lt;/h3&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
  &lt;li&gt;输入关键词 &lt;code class=&quot;highlighter-rouge&quot;&gt;limit&lt;/code&gt; 和速度(KiB/s)设置下载限速&lt;/li&gt;
  &lt;li&gt;输入关键词 &lt;code class=&quot;highlighter-rouge&quot;&gt;limitup&lt;/code&gt; 和速度(KiB/s)设置上传限速&lt;/li&gt;
  &lt;li&gt;输入关键词 &lt;code class=&quot;highlighter-rouge&quot;&gt;limitnum&lt;/code&gt; 和数字设置最大同时下载数&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;h3 id=&quot;设置-aria2-rpc&quot;&gt;设置 Aria2 RPC&lt;/h3&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
  &lt;li&gt;默认连接的 RPC 地址为 &lt;code class=&quot;highlighter-rouge&quot;&gt;http://localhost:6800/rpc&lt;/code&gt;, 如果需要更改, 输入关键词 &lt;code class=&quot;highlighter-rouge&quot;&gt;rpc&lt;/code&gt; 和你自己的 RPC 地址. 请注意, Ariafred 使用的 RPC 格式为 xml-rpc 而不是 YAAW 一类 WebUI 所用的 json-rpc, 更改 RPC 地址时请确保你的 RPC 地址以 &lt;code class=&quot;highlighter-rouge&quot;&gt;/rpc&lt;/code&gt; 结束.&lt;/li&gt;
  &lt;li&gt;默认使用的 rpc-secret 为空, 如果你在 &lt;code class=&quot;highlighter-rouge&quot;&gt;aria2.conf&lt;/code&gt; 配置文件中设置了自己的 rpc-secret, 输入关键词 &lt;code class=&quot;highlighter-rouge&quot;&gt;secret&lt;/code&gt; 和你自己的 rpc-secret 以设置.&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;h3 id=&quot;启动和关闭-aria2&quot;&gt;启动和关闭 Aria2&lt;/h3&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;//wil.dog/static/images/run.png&quot; alt=&quot;filter&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;如果连接不上 Aria2, Ariafred 会提示启动 Aria2 或者设置 RPC, 选择启动以尝试启动 Aria2&lt;/li&gt;
  &lt;li&gt;使用关键词 &lt;code class=&quot;highlighter-rouge&quot;&gt;quit&lt;/code&gt; 关闭 Aria2&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;h3 id=&quot;获取帮助&quot;&gt;获取帮助&lt;/h3&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;使用关键词 &lt;code class=&quot;highlighter-rouge&quot;&gt;help&lt;/code&gt; 进入 Github Repo 地址获取帮助&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;h3 id=&quot;更新&quot;&gt;更新&lt;/h3&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ariafred 会从 Github 自动检查最新的 release 状态并提示更新&lt;/p&gt;
</description>
                <link>http://wil.dog/ariafred/</link>
                <guid>http://wil.dog/ariafred</guid>
                <pubDate>2016-04-10T00:00:00+00:00</pubDate>
        </item>

        <item>
                <title>在 Vim-airline 上显示天气</title>
                <description>&lt;p&gt;&lt;img src=&quot;//wil.dog/static/images/airline-weather-vim-screenshot.png&quot; alt=&quot;screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这两天把 Vim 的 Powerline 插件换成了 &lt;a href=&quot;https://github.com/bling/vim-airline&quot;&gt;vim-airline&lt;/a&gt;, 相比 Powerline, vim-airline 更轻量, 完全用 Vimscript 实现, 让我摆脱了 Vim 中的 Powerline 插件在我的 tmux 环境下经常闪烁的问题.&lt;/p&gt;

&lt;p&gt;但是 vim-airline 缺了 Powerline 插件的天气扩展, 于是自己写了一个 vim-airline 的天气扩展, 效果图如上, 在右下角显示天气图标和当前温度, 借助了 webapi-vim 插件和 OpenWeatherMap 的 API.&lt;/p&gt;

&lt;p&gt;Github Repo 地址: &lt;a href=&quot;https://github.com/Wildog/airline-weather.vim&quot;&gt;https://github.com/Wildog/airline-weather.vim&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;安装&quot;&gt;安装&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;h3 id=&quot;使用-vundle-安装&quot;&gt;使用 Vundle 安装&lt;/h3&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;扩展依赖 vim-airline 和 webapi-vim, 确保你的 .vimrc 中有以下几行:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-vim&quot; data-lang=&quot;vim&quot;&gt;Plugin &lt;span class=&quot;s1&quot;&gt;'bling/vim-airline'&lt;/span&gt;
Plugin &lt;span class=&quot;s1&quot;&gt;'mattn/webapi-vim'&lt;/span&gt;
Plugin &lt;span class=&quot;s1&quot;&gt;'Wildog/airline-weather.vim'&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;然后执行 &lt;code class=&quot;highlighter-rouge&quot;&gt;:PluginInstall&lt;/code&gt;&lt;/p&gt;

&lt;h2 id=&quot;自定义&quot;&gt;自定义&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;h3 id=&quot;设置位置&quot;&gt;设置位置&lt;/h3&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-vim&quot; data-lang=&quot;vim&quot;&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;g:weather&lt;/span&gt;#area &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'beijing,china'&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;h3 id=&quot;设置单位&quot;&gt;设置单位&lt;/h3&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;metric 为摄氏度, imperial 为华氏度&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-vim&quot; data-lang=&quot;vim&quot;&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;g:weather&lt;/span&gt;#unit &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'metric'&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;h3 id=&quot;设置-api-key&quot;&gt;设置 API Key&lt;/h3&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;默认为我的 API Key, 你最好申请并设置一个自己的 API Key: &lt;a href=&quot;http://openweathermap.org/appid&quot;&gt;http://openweathermap.org/appid&lt;/a&gt;&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-vim&quot; data-lang=&quot;vim&quot;&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;g:weather&lt;/span&gt;#appid &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'2de143494c0b295cca9337e1e96b00e0'&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;h3 id=&quot;设置缓存&quot;&gt;设置缓存&lt;/h3&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;缓存文件路径默认为&lt;code class=&quot;highlighter-rouge&quot;&gt;~/.cache/.weather&lt;/code&gt;, 更新周期默认为 1 小时. 如果要修改更新周期, 注意更新太过频繁会使 Vim 卡顿.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-vim&quot; data-lang=&quot;vim&quot;&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;g:weather&lt;/span&gt;#cache_file &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'~/.cache/.weather'&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;g:weather&lt;/span&gt;#cache_ttl &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'3600'&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;h3 id=&quot;设置格式&quot;&gt;设置格式&lt;/h3&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;%s&lt;/code&gt;为天气图标, &lt;code class=&quot;highlighter-rouge&quot;&gt;%f&lt;/code&gt;为温度数字&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-vim&quot; data-lang=&quot;vim&quot;&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;g:weather&lt;/span&gt;#format &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'%s %.0f'&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;h3 id=&quot;设置图标&quot;&gt;设置图标&lt;/h3&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;各图标数字代码含义可参考 OpenWeatherMap 的 API: &lt;a href=&quot;http://openweathermap.org/weather-conditions&quot;&gt;http://openweathermap.org/weather-conditions&lt;/a&gt;&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-vim&quot; data-lang=&quot;vim&quot;&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;g:weather&lt;/span&gt;#status_map &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;01d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;☀&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;02d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;☁&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;03d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;☁&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;04d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;☁&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;09d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;☂&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;10d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;☂&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;11d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;☈&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;13d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;❅&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;50d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;≡&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;01n&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;☽&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;02n&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;☁&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;03n&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;☁&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;04n&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;☁&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;09n&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;☂&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;10n&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;☂&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;11n&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;☈&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;13n&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;❅&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;50n&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;≡&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;h3 id=&quot;强制刷新天气&quot;&gt;强制刷新天气&lt;/h3&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-vim&quot; data-lang=&quot;vim&quot;&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;call&lt;/span&gt; RefreshWeather&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</description>
                <link>http://wil.dog/2015/12/07/airline-weather-vim/</link>
                <guid>http://wil.dog/2015/12/07/airline-weather-vim</guid>
                <pubDate>2015-12-07T00:00:00+00:00</pubDate>
        </item>

        <item>
                <title>把你的旧 Kindle 变成桌面气象站</title>
                <description>&lt;p&gt;&lt;img src=&quot;//wil.dog/static/images/kindle.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;参考 Repo: &lt;a href=&quot;https://github.com/cathay4t/kindle-weather&quot;&gt;https://github.com/cathay4t/kindle-weather&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;看到有人在 K3 上做了个锁屏天气, 自己也心痒痒跟着 DIY 了一下, 效果还不错, 也算是把长期闲置的 K4 派上了用场. 分享下自己的流程:&lt;/p&gt;

&lt;h2 id=&quot;硬件需求&quot;&gt;硬件需求&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;一台长期联网的 Kindle (K3, K4 测试通过)&lt;/li&gt;
  &lt;li&gt;一台长期联网的主机或者 VPS&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;配置&quot;&gt;配置&lt;/h2&gt;

&lt;h3 id=&quot;配置主机&quot;&gt;配置主机&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;安装好 httpd, pngcrush, librsvg2&lt;/li&gt;
  &lt;li&gt;启动 httpd 服务&lt;/li&gt;
  &lt;li&gt;建立文件夹 &lt;code class=&quot;highlighter-rouge&quot;&gt;/var/www/html/weather&lt;/code&gt; 并设置权限&lt;/li&gt;
  &lt;li&gt;下载上面这个 &lt;a href=&quot;https://github.com/cathay4t/kindle-weather&quot;&gt;Repo&lt;/a&gt; 里的源码&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.wunderground.com/weather/api/d/login.html&quot;&gt;申请 Weather Underground 的 API&lt;/a&gt; 并记录 KEY&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;crontab -e&lt;/code&gt; 添加定时任务, 在每天 6 点到 22 点期间每 29 分钟更新产生一次天气图片, &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;PATH&amp;gt;&lt;/code&gt; 改成源码所在路径, &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;API_KEY&amp;gt;&lt;/code&gt; 改成得到的 KEY, &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;LAT&amp;gt; &amp;lt;LON&amp;gt;&lt;/code&gt; 改成所在城市的经纬度, &lt;a href=&quot;http://wunderground.com&quot;&gt;Weather Underground&lt;/a&gt; 上搜索城市可以得到经纬度&lt;/li&gt;
&lt;/ul&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;/29 6-22 &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &amp;lt;PATH&amp;gt;/weather_script.py &amp;lt;API_KEY&amp;gt; &amp;lt;LAT&amp;gt; &amp;lt;LON&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;ul&gt;
  &lt;li&gt;运行 &lt;code class=&quot;highlighter-rouge&quot;&gt;weather_script.py &amp;lt;API_KEY&amp;gt; &amp;lt;LAT&amp;gt; &amp;lt;LON&amp;gt;&lt;/code&gt;, 测试 &lt;code class=&quot;highlighter-rouge&quot;&gt;http://&amp;lt;主机域名&amp;gt;/weather/weather.png&lt;/code&gt; 能否得到正确显示的图片.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;配置-kindle&quot;&gt;配置 Kindle&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;首先给 Kindle 越狱
    &lt;ol&gt;
      &lt;li&gt;K4 下载此&lt;a href=&quot;http://www.mobileread.com/forums/attachment.php?attachmentid=141180&amp;amp;d=1439936080&quot;&gt;压缩包&lt;/a&gt;, 
解压后把 &lt;code class=&quot;highlighter-rouge&quot;&gt;data.tar.gz&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;ENABLE_DIAGS&lt;/code&gt; 两个文件和 &lt;code class=&quot;highlighter-rouge&quot;&gt;diagnostic_logs&lt;/code&gt; 文件夹复制到 Kindle 根目录,&lt;/li&gt;
      &lt;li&gt;重启后自动进入诊断模式, 选择&lt;code class=&quot;highlighter-rouge&quot;&gt;D) Exit, Reboot or Disable Diags&lt;/code&gt; 后选择 &lt;code class=&quot;highlighter-rouge&quot;&gt;R) Reboot System&lt;/code&gt;, 再选择 &lt;code class=&quot;highlighter-rouge&quot;&gt;Q) To continue&lt;/code&gt;, 等待 20s 后会显示 Jailbreak 的画面, 越狱完会自动重启. (其他 Kindle 版本参考&lt;a href=&quot;http://www.mobileread.com/forums/showthread.php?t=88004&quot;&gt;此页&lt;/a&gt;)&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;越狱后安装 USB networking, 从&lt;a href=&quot;http://www.mobileread.com/forums/showthread.php?t=88004&quot;&gt;此贴&lt;/a&gt;下载对应 Kindle 的版本, 解压后把文件名结尾为 install.bin 的文件复制到 Kindle 根目录, 然后在 Kindle 设置界面的菜单里选择更新 Kindle.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;断开 Kindle 和电脑的连接&lt;/strong&gt;, 在主界面按键盘键, 输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;;debugOn&lt;/code&gt; 后回车, 接着输入 &lt;code class=&quot;highlighter-rouge&quot;&gt;~usbNetwork&lt;/code&gt; 并回车, 然后把 Kindle 连接到电脑上, 这时候电脑会检测到名为 RNDIS/Ethernet Gadget 的网络接口, K4 用户把这个接口的 IP 地址设为 &lt;code class=&quot;highlighter-rouge&quot;&gt;192.168.15.201&lt;/code&gt;, 其他版本的 Kindle 把这个 IP 设为 &lt;code class=&quot;highlighter-rouge&quot;&gt;192.168.2.1&lt;/code&gt;,  如图:
&lt;img src=&quot;//wil.dog/static/images/interface.png&quot; alt=&quot;&quot; /&gt;&lt;/li&gt;
  &lt;li&gt;通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;ssh root@192.168.15.244&lt;/code&gt; 连接到 K4, 其他版本 Kindle 通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;ssh root@192.168.2.2&lt;/code&gt; 连接, 密码通常为 mario&lt;/li&gt;
  &lt;li&gt;执行 &lt;code class=&quot;highlighter-rouge&quot;&gt;mntroot rw&lt;/code&gt; 挂载 rootfs 为可写&lt;/li&gt;
  &lt;li&gt;执行 &lt;code class=&quot;highlighter-rouge&quot;&gt;cd /etc/kdb.src/yoshi/system/daemon/powerd/&lt;/code&gt;, 进入 powerd 文件夹, 路径中的 yoshi 不是固定的, 不同版本的 Kindle 这个路径不同&lt;/li&gt;
  &lt;li&gt;执行 &lt;code class=&quot;highlighter-rouge&quot;&gt;vi suspend_levels&lt;/code&gt;, 查看 suspend_levels, 把最后一行的数字改为 1152, 表示可在睡眠模式下执行 crontab 任务.&lt;/li&gt;
  &lt;li&gt;执行 &lt;code class=&quot;highlighter-rouge&quot;&gt;mkdir /mnt/base-us/weather&lt;/code&gt; 创建文件夹
&lt;img src=&quot;//wil.dog/static/images/ssh.png&quot; alt=&quot;&quot; /&gt;&lt;/li&gt;
  &lt;li&gt;在主机上把 &lt;code class=&quot;highlighter-rouge&quot;&gt;display-weather.sh&lt;/code&gt; 里面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;URL&lt;/code&gt; 改为你自己主机上 weather.png 的 URL&lt;/li&gt;
  &lt;li&gt;通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;scp display-weather.sh root@192.168.15.244:/usr/bin/&lt;/code&gt; 把 &lt;code class=&quot;highlighter-rouge&quot;&gt;display-weather.sh&lt;/code&gt; 传到 Kindle 上的 &lt;code class=&quot;highlighter-rouge&quot;&gt;/usr/bin&lt;/code&gt; 目录下&lt;/li&gt;
  &lt;li&gt;回到 Kindle 的 shell, 添加 crontab 任务, 在每天6点到22点期间每小时获取一次天气图片:&lt;/li&gt;
&lt;/ul&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;*/60 6-22 * * * /usr/bin/display-weather.sh&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; /etc/crontab/root&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;ul&gt;
  &lt;li&gt;执行 &lt;code class=&quot;highlighter-rouge&quot;&gt;/usr/bin/display-weather.sh&lt;/code&gt;, 在 Kindle 上测试效果&lt;/li&gt;
  &lt;li&gt;执行 &lt;code class=&quot;highlighter-rouge&quot;&gt;reboot&lt;/code&gt; 以重启 Kindle&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;至此 Kindle 桌面气象站就设置好了, 每小时会自动更新天气, 如果中途解锁了再锁定, 锁屏界面会变成普通锁屏画面, 一小时内等到下次执行 crontab 任务的时候会重新显示天气界面.&lt;/p&gt;
</description>
                <link>http://wil.dog/2015/11/28/display-weather-on-kindle-lock-screen/</link>
                <guid>http://wil.dog/2015/11/28/display-weather-on-kindle-lock-screen</guid>
                <pubDate>2015-11-28T00:00:00+00:00</pubDate>
        </item>

        <item>
                <title>用 Matlab 训练一个简单的神经网络</title>
                <description>&lt;p&gt;抱着纯属玩票的心态看完了 &lt;a href=&quot;https://www.coursera.org/learn/machine-learning&quot;&gt;Andrew Ng 的机器学习入门课&lt;/a&gt;，又恰逢期末课设，就想着利用神经网络做一个简单数学表达式的识别和求值工具，主要的模型训练和识别部分都是由 Matlab 完成。&lt;/p&gt;

&lt;h2 id=&quot;神经网络模型&quot;&gt;神经网络模型&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;//wil.dog/static/images/nn-model.png&quot; alt=&quot;模型简化图&quot; /&gt;&lt;/p&gt;

&lt;p&gt;考虑到计算速度，我只建立了一个简单的三层 BP 神经网络模型作为分类器来识别单个字符，模型简化图如上。输入层是由 20x20 的图像得到的 1x400 的特征向量，隐藏层共 80 个隐藏单元，激活函数采用 sigmoid function，输出层是一个 1x17 的向量，可以用来描述 17 种识别结果：&lt;code class=&quot;highlighter-rouge&quot;&gt;数字 0-9 和 +, -, ⨯, ÷, ^, (, )&lt;/code&gt;。&lt;/p&gt;

&lt;h2 id=&quot;数据集和预处理&quot;&gt;数据集和预处理&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;同样也是出于计算速度的考虑，以及需要识别的除了数字还有符号，我没有使用只含数字且较大的 &lt;a href=&quot;http://yann.lecun.com/exdb/mnist/&quot;&gt;MNIST 数据集&lt;/a&gt;，而是找室友一起手写了大概 1000 个数字和符号作为训练数据。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;不管是用作训练数据的图像还是最终需要识别的图像都需要做预处理，把图片上表达式的每个字符分离成单独的图像再依次送至分类器识别，预处理也是用 Matlab 完成，具体过程如下：&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;图像转为灰度图像后反色处理，并进行中值滤波以平滑图像，然后进行整体&lt;a href=&quot;https://github.com/Wildog/handwritten-expr-evaluator/blob/master/xylimit.m&quot;&gt;边界限定&lt;/a&gt;，截取表达式区域图像。&lt;img src=&quot;//wil.dog/static/images/img-preprocess-1.png&quot; alt=&quot;&quot; /&gt;&lt;/li&gt;
      &lt;li&gt;横向提取并拼接有效行，纵向提取并分割有效字符。提取每个分割区域为独⽴图像，进行边界限定，计算灰度阈值并转为⼆值图像，最后大小归一化图像：填充至正方形并统一为 20x20 的图像。&lt;img src=&quot;//wil.dog/static/images/img-preprocess-2.png&quot; alt=&quot;&quot; /&gt;&lt;/li&gt;
    &lt;/ol&gt;

    &lt;p&gt;预处理部分还需要注意的主要问题是过滤噪音，除了平滑图像和边界限定以外，还要剔除掉高度过小的行、宽度过小的列以及面积过小的区域。完整代码见这里：&lt;a href=&quot;https://github.com/Wildog/handwritten-expr-evaluator/blob/master/getPicChar.m&quot;&gt;https://github.com/Wildog/handwritten-expr-evaluator/blob/master/getPicChar.m&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;训练模型&quot;&gt;训练模型&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;首先是随机初始化权重，避免每层的每个单元学习到同样的参数。本质就是给每个权重矩阵里面的每个权重设置一个介于 [-𝜺, 𝜺] 的值，这个 𝜺 一般取 &lt;code class=&quot;highlighter-rouge&quot;&gt;sqrt(6) / sqrt(input_layer_size + output_layer_size)&lt;/code&gt;，完整函数如下：&lt;/li&gt;
&lt;/ul&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-matlab&quot; data-lang=&quot;matlab&quot;&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;W&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;randInitializeWeights&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;L_in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;L_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;% 由于 bias unit 的存在，权重矩阵的实际大小是 L_out * (1 + L_in)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;W&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;zeros&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;L_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;L_in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;epsilon_init&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sqrt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sqrt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;L_in&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;L_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;W&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;rand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;L_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;L_in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;epsilon_init&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;epsilon_init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;ul&gt;
  &lt;li&gt;训练模型的本质就是不断地迭代学习利用梯度下降法最小化代价函数。在每轮迭代中，先使用前向传播算法计算各层输出值，再使用后向传播算法计算各层残差以计算各个权重矩阵的梯度，并计算代价函数，我写的一轮迭代如下：&lt;/li&gt;
&lt;/ul&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-matlab&quot; data-lang=&quot;matlab&quot;&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;J&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;grad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nnCostFunction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nn_params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;...&lt;/span&gt;
                                   &lt;span class=&quot;n&quot;&gt;input_layer_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;...&lt;/span&gt;
                                   &lt;span class=&quot;n&quot;&gt;hidden_layer_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;...&lt;/span&gt;
                                   &lt;span class=&quot;n&quot;&gt;num_labels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;...&lt;/span&gt;
                                   &lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lambda&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Theta1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;reshape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nn_params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hidden_layer_size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_layer_size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;...&lt;/span&gt;
                 &lt;span class=&quot;n&quot;&gt;hidden_layer_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_layer_size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Theta2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;reshape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nn_params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hidden_layer_size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_layer_size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))):&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;...&lt;/span&gt;
                 &lt;span class=&quot;n&quot;&gt;num_labels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hidden_layer_size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
         
&lt;span class=&quot;n&quot;&gt;J&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Theta1_grad&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;zeros&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Theta1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Theta2_grad&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;zeros&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Theta2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;%Forward Propagation 算法计算各层输出值&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;hidden_layer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;zeros&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hidden_layer_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;input_layer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,:)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;hidden_layer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sigmoid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Theta1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input_layer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)];&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;output_layer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;zeros&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_labels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;output_layer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sigmoid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Theta2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hidden_layer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;y_recode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;zeros&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_labels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;y_recode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;%Backpropagation 算法计算各层残差以计算 Theta1 和 Theta2 的梯度&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;delta3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_layer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_recode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;delta2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Theta2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;delta3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hidden_layer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hidden_layer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;delta2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;delta2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;%!!!丢弃 bias unit&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;%累加梯度&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Theta1_grad&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Theta1_grad&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;delta2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input_layer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Theta2_grad&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Theta2_grad&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;delta3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hidden_layer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;%累加 LR cost&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;J&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;J&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y_recode&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;' * log(output_layer) + (1 - y_recode'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output_layer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;%计算 NN cost&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;J&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;J&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lambda&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Theta1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(:,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.^&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Theta2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(:,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.^&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Theta1_temp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Theta1_grad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Theta1_grad&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Theta1_grad&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lambda&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Theta1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;tmp1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Theta1_temp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Theta1_grad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(:,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tmp1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(:,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Theta2_temp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Theta2_grad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Theta2_grad&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Theta2_grad&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lambda&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Theta2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;tmp2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Theta2_temp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Theta2_grad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(:,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tmp2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(:,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;grad&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Theta1_grad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(:)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Theta2_grad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(:)];&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;这个函数会返回一轮迭代后的代价函数值和各个权重矩阵的梯度，给这个函数创建一个句柄，设置一下学习率和最大迭代次数，和随机初始化后的各权重矩阵一起传给 &lt;a href=&quot;https://github.com/Wildog/handwritten-expr-evaluator/blob/master/fmincg.m&quot;&gt;fmincg 函数&lt;/a&gt;计算，然后静静地等着代价函数收敛就行了，不断的测试准确率再反复更改参数重新训练，最后得到满意的各权重矩阵。&lt;/p&gt;

&lt;h2 id=&quot;大功告成&quot;&gt;大功告成&lt;/h2&gt;

&lt;p&gt;得到最终的权重矩阵后，针对预处理后的每个字符图像的像素矩阵计算其输出层就可以识别出对应的字符了，依次识别每个字符最终得到整个表达式，剩下的计算表达式的方法太多了不再多提，晒下结果图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;//wil.dog/static/images/nn-result.png&quot; alt=&quot;result&quot; /&gt;&lt;/p&gt;

&lt;p&gt;完整项目地址：&lt;a href=&quot;https://github.com/Wildog/handwritten-expr-evaluator&quot;&gt;https://github.com/Wildog/handwritten-expr-evaluator&lt;/a&gt;&lt;/p&gt;

</description>
                <link>http://wil.dog/2015/01/18/train-a-simple-neural-network/</link>
                <guid>http://wil.dog/2015/01/18/train-a-simple-neural-network</guid>
                <pubDate>2015-01-18T00:00:00+00:00</pubDate>
        </item>

        <item>
                <title>在 Vim 里复制所有匹配结果</title>
                <description>&lt;p&gt;今天突发奇想地想在 Vim 中一次性复制所有的正则匹配结果, 先是直接想到用 MultipleCursors 这个插件带的 MultipleCursorsFind, 复制后提示 yank 操作不能在多光标情况下使用, 这种奇技淫巧果然行不通…
遂用 Vimscript 解决, 在 &lt;code class=&quot;highlighter-rouge&quot;&gt;.vimrc&lt;/code&gt; 中加入下列函数:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-vim&quot; data-lang=&quot;vim&quot;&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;!&lt;/span&gt; CopyMatches &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; @&lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;.=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;a:m&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;\n&quot;&lt;/span&gt; 
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;a:m&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;endfunction&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;复制方法:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-vim&quot; data-lang=&quot;vim&quot;&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; @&lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;''&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;%s&lt;span class=&quot;sr&quot;&gt;/regex/&lt;/span&gt;\&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;CopyMatches&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;submatch&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;/&lt;span class=&quot;k&quot;&gt;g&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;粘贴方法:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-vim&quot; data-lang=&quot;vim&quot;&gt;&lt;span class=&quot;c&quot;&gt;&quot;+p&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;嫌麻烦可以设个快捷键, 如:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-vim&quot; data-lang=&quot;vim&quot;&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;map &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;F4&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; @&lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;''&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;cr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;:&lt;/span&gt;%s&lt;span class=&quot;sr&quot;&gt;/regex/&lt;/span&gt;\&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;CopyMatches&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;submatch&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;/&lt;span class=&quot;k&quot;&gt;g&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;按 F4 后自己改掉 regex 就可以了.&lt;/p&gt;

&lt;p&gt;示例:&lt;br /&gt;
&lt;img src=&quot;//wil.dog/static/images/vim-1.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
&lt;img src=&quot;//wil.dog/static/images/vim-2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</description>
                <link>http://wil.dog/2014/10/21/copy-and-paste-multiple-search-results-in-vim/</link>
                <guid>http://wil.dog/2014/10/21/copy-and-paste-multiple-search-results-in-vim</guid>
                <pubDate>2014-10-21T00:00:00+00:00</pubDate>
        </item>

        <item>
                <title>用 Arduino + Processing 识别节拍</title>
                <description>&lt;p&gt;&lt;img src=&quot;//wil.dog/static/images/arduino.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;一个简单的 Arduino + Processing 工程，用 Minim 库中的 BeatDetect 实时读取 Buffer 中的数据并采集音频频率识别 Kick、Snare 和 Hi-Hat 三种鼓点，通过 Arduino 的 Firmata 库驱动 LED 发光。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;源码&lt;/strong&gt;:&lt;br /&gt;
&lt;a href=&quot;http://wil.dog/download/BeatWrite.zip&quot;&gt;点我下载&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&quot;把-beatwritepde-中的针脚配置改为你自己连接的针脚-连接图示点我查看&quot;&gt;把 &lt;code class=&quot;highlighter-rouge&quot;&gt;BeatWrite.pde&lt;/code&gt; 中的针脚配置改为你自己连接的针脚, 连接图示&lt;a href=&quot;/resources/circuit.jpg&quot;&gt;点我查看&lt;/a&gt;&lt;/h1&gt;
&lt;h1 id=&quot;把-mp3-文件放入-data-文件夹中-将-beatwritepde-中-minimloadfileaaamp3-2048-一行的-aaamp3-改为你自己的-mp3-文件名&quot;&gt;把 mp3 文件放入 &lt;code class=&quot;highlighter-rouge&quot;&gt;data&lt;/code&gt; 文件夹中, 将 &lt;code class=&quot;highlighter-rouge&quot;&gt;BeatWrite.pde&lt;/code&gt; 中 &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;minim.loadFile(&quot;aaa.mp3&quot;, 2048)&quot;&lt;/code&gt; 一行的 &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;aaa.mp3&quot;&lt;/code&gt; 改为你自己的 mp3 文件名&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;软件&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Arduino&lt;/li&gt;
  &lt;li&gt;Processing&lt;br /&gt;
# 务必下载 1.5.1 版本的 Processing, 个人测试 2.0 的版本会出错&lt;/li&gt;
  &lt;li&gt;Arduino 库&lt;br /&gt;
# 解压至 &lt;code class=&quot;highlighter-rouge&quot;&gt;Processing目录/modes/java/libraries/&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Minim 库&lt;br /&gt;
# 官方 Processing 已自带, 如果你的没有, 下载后解压至相同目录&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;步骤&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;将 Examples 中 Firmata 库里的 StandardFirmata 上传到 Arduino 中&lt;/li&gt;
  &lt;li&gt;上传完毕等到 Arduino 上的 RX/TX LED 停止闪烁后再运行 Processing 源码&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;实测 Linkin Park 的 New Divide, 这首鼓点比较清晰：&lt;a href=&quot;http://v.youku.com/v_show/id_XNTcyNDA5NjAw.html&quot;&gt;视频戳我&lt;/a&gt;&lt;br /&gt;
右边的是 Kick, 中间的是 Snare, 左边的是 Hi-Hat, 不同鼓的频率不同, 但各种鼓的 Kick 大部分都在&amp;lt;150Hz 频段, 可见 Kick 是最准的, 而 Snare 处于半残废状态, Hi-Hat 也一直把差不多的高频误判为 Hi-Hat&lt;/p&gt;
</description>
                <link>http://wil.dog/2014/07/11/arduino-processing-beat-detect/</link>
                <guid>http://wil.dog/2014/07/11/arduino-processing-beat-detect</guid>
                <pubDate>2014-07-11T00:00:00+00:00</pubDate>
        </item>


</channel>
</rss>
