<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Swift on Chen</title><link>https://hechen.github.io/zh-cn/tags/swift/</link><description>Recent content in Swift on Chen</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Sun, 29 Sep 2019 10:30:30 +0800</lastBuildDate><atom:link href="https://hechen.github.io/zh-cn/tags/swift/index.xml" rel="self" type="application/rss+xml"/><item><title>Alamofire 和 RxSwift 中的 .af 以及 .rx 扩展是怎么实现的</title><link>https://hechen.github.io/zh-cn/post/alamofire-rxswift-af-rx/</link><pubDate>Sun, 29 Sep 2019 10:30:30 +0800</pubDate><guid>https://hechen.github.io/zh-cn/post/alamofire-rxswift-af-rx/</guid><description>&lt;p&gt;今天继续来一篇水文，关于 Alamofire 和 RxSwift 两个组件中扩展后缀 .af 以及 .rx 是怎么实现的。相信用过的 (￣ω￣(￣ω￣〃 (￣ω￣〃)ゝ 都看过了。我这里权当自己的记录流水账了。&lt;/p&gt;
&lt;p&gt;先来看 Alamofire 中关于 af 扩展的实现：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Alamofire" loading="lazy" src="https://i.imgur.com/AbPIzgX.png"&gt;&lt;/p&gt;
&lt;p&gt;使用 Alamofire 扩展的方式如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;Demo&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;extension&lt;/span&gt; &lt;span class="nc"&gt;Demo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AlamofireExtended&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;extension&lt;/span&gt; &lt;span class="nc"&gt;AlamofireExtension&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;ExtendedType&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;Demo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;printSomething&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Something arrived.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;d&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Demo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;af&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;printSomething&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里主要有两点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;为将要使用的类 Demo 扩展 &lt;code&gt;AlamofireExtended&lt;/code&gt; 协议，也就是增加 af 扩展属性，其返回的是一个封装对象 &lt;code&gt;AlamofireExtension&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;为 &lt;code&gt;AlamofireExtension&lt;/code&gt; 对象（1 中返回的封装类型）扩展专属于 Demo 这个类型的方法；&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;说实话，这里用来封装元类型信息的结构体名称和扩展协议名称一样，我初次看的时候确实是蒙的。相对于 Alamofire 的命名来说，个人更喜欢 RxSwift 的命名。&lt;/p&gt;
&lt;p&gt;接下来是 RxSwift 中 Rx 扩展的实现：&lt;/p&gt;
&lt;p&gt;&lt;img alt="RxSwift" loading="lazy" src="https://i.imgur.com/m5O7lo5.png"&gt;&lt;/p&gt;
&lt;p&gt;可以看到，整个使用方式上是完全相同的。仅仅是各项名字不同。&lt;/p&gt;
&lt;p&gt;所以我们可以总结这种方式的使用路径，如何为我们自己开发的组件扩充命名空间，具体有三个地方：&lt;/p&gt;
&lt;h3 id="声明扩展属性"&gt;声明扩展属性&lt;/h3&gt;
&lt;p&gt;为了能够支持实例变量和类型两者，需要分别增加两个计算属性：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个是 static 的，提供给类型使用；&lt;/li&gt;
&lt;li&gt;一个是实例变量，提供给单个实例来用；&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="封装元类型信息"&gt;封装元类型信息&lt;/h3&gt;
&lt;p&gt;为了能够使得 &lt;code&gt;.af/.rx&lt;/code&gt; 这些扩展属性也能够获得当前实例或者类型的元信息，不约而同的使用了结构体封装的形式，通过泛型类型实现。我们知道这个封装对象内部就是记录的就是 self&lt;/p&gt;</description></item><item><title>Swift 中的消息派发</title><link>https://hechen.github.io/zh-cn/post/messagedispatchinswift/</link><pubDate>Sun, 15 Sep 2019 20:56:50 +0000</pubDate><guid>https://hechen.github.io/zh-cn/post/messagedispatchinswift/</guid><description>&lt;p&gt;&lt;img alt="What is the method dispatch" loading="lazy" src="https://i.imgur.com/ggQZyRp.png"&gt;&lt;/p&gt;
&lt;h1 id="什么是消息派发"&gt;什么是消息派发？&lt;/h1&gt;
&lt;p&gt;消息派发，英文名称 Method Dispatch，是指程序在运行过程中调用某个方法的时候决议使用哪个具体指令的过程。消息派发的行为在我们代码中时时刻刻的在发生。了解消息派发的机制对于我们日常写出相对高效的代码也是有利的，日常 Coding 的时候遇到一些派发相关的问题，也能做到心里有数。&lt;/p&gt;
&lt;p&gt;对于编译型语言来讲，有主要三种类型的方法派发方式：Direct Dispatch，Table Dispatch 以及 Message Dispatch，前者也被称作 Static Dispatch，后两个为 Dynamic Dispatch。&lt;/p&gt;
&lt;h1 id="方法派发类型"&gt;方法派发类型&lt;/h1&gt;
&lt;p&gt;本质上来讲，派发的目的就是告诉 CPU，在某个特定方法被调用的时候去内存的哪个地址去读执行代码。下面我们先了解下这三种类型的派发形式，这三种可以说各有优劣。&lt;/p&gt;
&lt;h2 id="direct-dispatch"&gt;Direct Dispatch&lt;/h2&gt;
&lt;p&gt;直接派发也叫静态派发。它是这三种形式中最快速的，不仅仅是底层翻译的汇编指令少，也是因为编译器可以针对这种情况做更多优化的事情，比如说函数内联等。C 语言就是这种的典型代表，然而，对于日渐复杂的编程场景来说，静态派发限制非常大，我们必须在代码运行之前把事情安排的明明白白的，这个极大的限制了代码书写的灵活性。一句话总结，编译器在编译期间就已经确定了的推断路径，运行期间按照既定路线走就行。&lt;/p&gt;
&lt;h2 id="table-dispatch"&gt;Table Dispatch&lt;/h2&gt;
&lt;p&gt;函数表派发这种是编译型语言中最常见的动态派发实现形式，编译器层面使用一个表格结构来存储类型声明中的每一个函数指针。C++ 语言把这种表格称作虚函数表（virtual table，简称 V-Table），而在 Swift 中，针对拥有继承的 Class 类型来说，依然采用了 V-Table 这种实现形式达到对多态的支持，而针对值类型由于 Protocol 产生的多态而采用了另外一种函数表派发形式，这个表格被称为协议目击表 （Protocol Witness Table，简称 PWT），这个暂时略去不表。&lt;/p&gt;
&lt;h3 id="v-table"&gt;V-Table&lt;/h3&gt;
&lt;p&gt;对于 V-Table 的应用场景下，每一个类都会维护一个函数表，里面记录着该类所有的函数指针，主要包含：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;由父类继承而来的方法执行地址；&lt;/li&gt;
&lt;li&gt;如果子类覆写了父类方法的话，表格里面就会保存被重载之后的函数。&lt;/li&gt;
&lt;li&gt;子类新添加的函数会插入到这个表格的末尾&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;在程序运行期间会根据这个表去查询真正要调用的函数是哪一个。这种特性就极大的提升了代码的灵活性，也是 Java，C++ 等语言得以支持多态这种语言特性的基石。当然，相对于静态派发而言，表格派发则多了很多的隐式花销，因为函数调用不再是直接调用了，而是需要通过先行查表的形式找到真实的函数指针来执行，编译器也无法再进行诸如 inline 等编译期优化了。&lt;/p&gt;
&lt;p&gt;我们以 WWDC 的某个例子来大概说明下 V-Table 的具体派发过程，如下代码，共有三个 Class，其中 Drawable 是基类，子类化的 Point 和 Line。&lt;/p&gt;
&lt;p&gt;&lt;img alt="image-20190919233507171" loading="lazy" src="https://i.imgur.com/49YUt1Y.png"&gt;&lt;/p&gt;</description></item><item><title>Protocol Extension</title><link>https://hechen.github.io/zh-cn/post/protocol-extension/</link><pubDate>Thu, 08 Aug 2019 19:00:20 +0800</pubDate><guid>https://hechen.github.io/zh-cn/post/protocol-extension/</guid><description>&lt;p&gt;Protocol 作为 Swift 生态的最重要的组成部分（没有之一），其搭建起了整个语言生态的各个组成部分。&lt;/p&gt;
&lt;p&gt;相对于继承而言，Protocol 的几个比较直观的优势：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;无需必须去强制继承某个类；&lt;/li&gt;
&lt;li&gt;通过协议可以改造已经存在的类型；&lt;/li&gt;
&lt;li&gt;适用范围更大一些，使得 struct 和 enum 这些值类型也能够继承能力&lt;/li&gt;
&lt;li&gt;Swift 语言本身是不支持多继承的，你需要花费精力去纠结继承哪个 class，一旦出现不同的能力分散在不同父类的情况，就更加纠结了，你甚至需要去修改原有的父类；&lt;/li&gt;
&lt;li&gt;继承的话，还需要考虑 override 父类方法的问题，比如调用 super 方法的时机问题；&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这篇文章想记录下 Protocol Extension 中的一点区别。&lt;/p&gt;
&lt;p&gt;我们知道 Swift Protocol 中是没有 optional 关键字的，因此只要是在 Protocol 定义的方法，即为 Protocol Requirements，符合该协议的类型就必须实现，否则是不完整的。比如，我们自己实现了 TableView，定义了这么多 delegate 协议方法，要求调用方全都提供，就太不人道了😢&lt;/p&gt;
&lt;p&gt;&lt;img alt="Protocol" loading="lazy" src="https://i.imgur.com/yA76SiN.png"&gt;&lt;/p&gt;
&lt;p&gt;因此，我们会通过对 Protocol 进行扩展，给该协议方法增加缺省实现，剩余的方法则是你需要调用方必须实现的方法。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Optional Protocol" loading="lazy" src="https://i.imgur.com/MRA0k6z.png"&gt;&lt;/p&gt;
&lt;p&gt;如果针对某个协议 P，我们扩展 P 给其增加某个协议方法有两种形式：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;直接在 Protocol 定义里定义新方法，该方法成为实现该协议必须实现的方法；&lt;/li&gt;
&lt;li&gt;为 Protocol 增加扩展方法，该方法并不一定要求符合该协议的类型进行实现；&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这两者有什么区别呢？ 我们看代码&lt;/p&gt;
&lt;p&gt;&lt;img alt="Static Dispatch" loading="lazy" src="https://i.imgur.com/T3filbG.png"&gt;&lt;/p&gt;
&lt;p&gt;发现 s 调用的是我们为 S 结构体增加的扩展方法，而 s2 调用的是 P 协议的扩展方法，尽管实体对象的动态类型是 S，但是因为声明的静态类型不一样，调用结果也不同。&lt;/p&gt;
&lt;p&gt;对于 1 中的方法，运行时进行动态派发，也就是真实根据其具体类型来做决议，2 中的方法是进行静态派发，也就是说你声明的该对象是什么类型，就调用该类型的方法。 以上述的代码为例，s1 的静态类型由编译器推导为 S，而 s2 的静态类型声明为 P ，因此二者在调用扩展协议方法时候的表现不一致。&lt;/p&gt;</description></item><item><title>Codable &amp;&amp; Tuple</title><link>https://hechen.github.io/zh-cn/post/tuple-codable/</link><pubDate>Mon, 10 Jun 2019 23:50:54 +0800</pubDate><guid>https://hechen.github.io/zh-cn/post/tuple-codable/</guid><description>&lt;p&gt;While learning Swift, I keep finding interesting things. Codable is one of them.&lt;/p&gt;
&lt;p&gt;Today, I defined a model with a tuple type, then Xcode gave me an error. The code might look like this:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Codable Person" loading="lazy" src="https://i.imgur.com/c67KrA8.png"&gt;&lt;/p&gt;
&lt;p&gt;You may want Xcode to automatically complete all the codable stuff. However, life is hard. Code like this cannot even be compiled. Xcode will tell you that it cannot synthesize Codable for Person because of FullName.&lt;/p&gt;</description></item><item><title>一道 Swift Quiz</title><link>https://hechen.github.io/zh-cn/post/a-swift-quiz/</link><pubDate>Tue, 14 May 2019 16:01:54 +0800</pubDate><guid>https://hechen.github.io/zh-cn/post/a-swift-quiz/</guid><description>&lt;p&gt;这两天在 &lt;a href="https://twitter.com/krzyzanowskim/status/1127875626579288064?s=12"&gt;Twitter&lt;/a&gt; 上看到一道题目，主要是考察 overload 和 type(of:) 的知识点，本文仅做记录，关于 MetaType 会单独写一篇文章来总结。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;// Swift Quiz
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;class View {}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;class A: View {}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;class B: View {}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;func add(_ v: View) { print(type(of: v)) }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;func add(_ v: A) { print(type(of: v)) }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;func add(_ v: B) { print(type(of: v)) }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;func build() -&amp;gt; View { return B() }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;add(build())
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;// What is the output?
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &amp;#34;View&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &amp;#34;A&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &amp;#34;B&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;答案的分布说明，大家对这个输出结果还是有一定的疑惑🤔&lt;/p&gt;</description></item><item><title>Mac 平台上那些 Dockless 的 App 都是如何实现的？</title><link>https://hechen.github.io/zh-cn/post/dockless-cocoaapps/</link><pubDate>Wed, 13 Mar 2019 17:29:54 +0800</pubDate><guid>https://hechen.github.io/zh-cn/post/dockless-cocoaapps/</guid><description>&lt;p&gt;Menu Only 算是 Cocoa App 中最常见的一项，它使得 App 不占用你的 Dock 栏，在多 workspace 的时候也不影响正常使用，随时都可以在屏幕的菜单栏中执行快捷操作。尤其是针对一些需要便捷性要求比较高的应用来讲，Menu bar 的功能必不可少。本文就简单介绍一下关于 Menu App 中关键的几个开发步骤。&lt;/p&gt;
&lt;h1 id="介绍"&gt;介绍&lt;/h1&gt;
&lt;p&gt;以 &lt;a href="https://www.shimovpn.com/"&gt;Shimo&lt;/a&gt; 这个 App 举例，&lt;/p&gt;
&lt;p&gt;&lt;img alt="Shimo" loading="lazy" src="https://i.imgur.com/vT7EEVK.png"&gt;&lt;/p&gt;
&lt;p&gt;在其设置选项卡中能看到 Show Shimo in 的选项菜单，其中有三项：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Menubar only&lt;/li&gt;
&lt;li&gt;Dock only&lt;/li&gt;
&lt;li&gt;Menubar &amp;amp; Dock&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt="Shimo Menu" loading="lazy" src="https://i.imgur.com/WywIVkV.png"&gt;&lt;/p&gt;
&lt;p&gt;这也是常见的 Cocoa 应用的模式支持，很多常见的 App 都支持，比如 DayOne，Dash，Todoist 等。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Dash" loading="lazy" src="https://i.imgur.com/ioUvpNw.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Todoist" loading="lazy" src="https://i.imgur.com/ORYAEcZ.png"&gt;&lt;/p&gt;
&lt;p&gt;其实核心功能有两点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;可以显示或者隐藏 Dock 图标；&lt;/li&gt;
&lt;li&gt;可以显示或者隐藏 Menu 菜单这两者的组合。&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id="核心步骤"&gt;核心步骤&lt;/h1&gt;
&lt;h2 id="dock"&gt;Dock&lt;/h2&gt;
&lt;p&gt;普通的 Cocoa Application 创建之后，默认都是 Dock 上展示的，如果想隐藏 Dock 图标，首先它需要有这个能力，这个能力是通过 info.plist 文件中的 Key 来指定的，这个 Key 就是 &lt;code&gt;LSUIElement&lt;/code&gt;，我们将其值设置为 true&lt;/p&gt;</description></item><item><title>为 NSView 增加 backgroundColor</title><link>https://hechen.github.io/post/nsview-backgroundcolor/</link><pubDate>Mon, 11 Mar 2019 15:19:49 +0800</pubDate><guid>https://hechen.github.io/post/nsview-backgroundcolor/</guid><description>&lt;p&gt;NSView 作为 Cocoa 中最基本的构成元素，是构成整个 Mac App 视图体系的基础，和 UIView 在 iOS 世界中的位置一样重要，可是在 UIView 里司空见惯的背景色设置，在 NSView 中却不见身影。&lt;/p&gt;
&lt;p&gt;在 iOS 中，设置 UIView 的背景色很简单。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;UIKit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;frame&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CGRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;zero&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;CGSize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;view&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;UIView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;backgroundColor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;而在 macOS 上，使用 NSView 背景色需要如下设置：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;Cocoa&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;frame&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CGRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;zero&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;CGSize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;view&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;NSView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wantsLayer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;backgroundColor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;NSColor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cgColor&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;UIView 自身有一个 backgroundColor 的属性，而在 NSView 中是没有的，你需要做的是设置 View 所包含的 layer 的背景色。而且 layer 是 Optional 的，也就意味着 NSView 默认是不包含 layer 的，所以你还需要设置 wantsLayer 为 true 让 NSView 创建 layer 出来。&lt;/p&gt;
&lt;p&gt;在 Apple 的&lt;a href="https://developer.apple.com/documentation/appkit/nsview/1483695-wantslayer"&gt;官方文档&lt;/a&gt; 有针对 wantsLayer 的一些说明&lt;/p&gt;</description></item><item><title>RxSwift 中的几种 Subject</title><link>https://hechen.github.io/zh-cn/post/rxswift-subjects/</link><pubDate>Tue, 26 Feb 2019 10:24:38 +0800</pubDate><guid>https://hechen.github.io/zh-cn/post/rxswift-subjects/</guid><description>&lt;blockquote&gt;
&lt;p&gt;文中所用插图均出自书籍 《RxSwift - Reactive Programming with Swift》&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Subject 在 Rx 的世界里是这么一种存在，其既可以作为观测者，也可以作为被观测者。自然而然想到的是 Subject 本身就可以作为一种过渡桥接信号的手段，它订阅某个信号，一旦信号收到序列，转头它就又把信号散发给自己的观测者了。&lt;/p&gt;
&lt;p&gt;在 RxSwift 的世界里涉及几种 Subject，这里先行做个记录：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;PublishSubject&lt;/li&gt;
&lt;li&gt;BehaviorSubject&lt;/li&gt;
&lt;li&gt;ReplaySubject&lt;/li&gt;
&lt;li&gt;Variable （预计在 5.0 废弃）&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="publishsubject"&gt;PublishSubject&lt;/h2&gt;
&lt;p&gt;PublishSubject 只给订阅者发送新元素，也就是订阅者只能接受到订阅之后发出的信号。一图胜千言，看👇&lt;/p&gt;
&lt;p&gt;&lt;img alt="publishSubject" loading="lazy" src="https://i.imgur.com/Cw4FjCT.png"&gt;&lt;/p&gt;
&lt;p&gt;其中第一排是某个作为 Observable 角色的 Subject，自左至右为严格时间线， 2 和 3 排均为订阅了该 Subject 的观测者，向上的虚线表明订阅的时机，可以看到观测者只能接收到订阅之后由 Subject 新发射出的信号。&lt;/p&gt;
&lt;h2 id="behaviorsubject"&gt;BehaviorSubject&lt;/h2&gt;
&lt;p&gt;BehaviorSubject 会在订阅者订阅之后发送最新的一个信号元素，自然需要你在初始化该对象的时候给其设定初始化值（否则对于第一次订阅的观测者来说，哪来的最新发射的元素呢？）。如果初始化的时候无法提供默认值，那可能你就需要用到上面的 PublishSubject 了。其中常用的 Variable 和 BehaviorRelay 都是对其的封装。&lt;/p&gt;
&lt;p&gt;&lt;img alt="BehaviorSubject" loading="lazy" src="https://i.imgur.com/ei6pCwT.png"&gt;&lt;/p&gt;
&lt;p&gt;从图中可以看到和上方 Publish 区别的是其在观测者订阅之后会收到最新的最后一个信号。&lt;/p&gt;
&lt;h2 id="replaysubject"&gt;ReplaySubject&lt;/h2&gt;
&lt;p&gt;顾名思义，ReplaySubject 会重新发射在订阅者订阅之间发出的信号，具体发多少是由 bufferSize 决定的，其指定需要 cache 多少个信号，如下图中所示，Buffer Size 为 2，因此当第二个订阅者订阅 Subject 之后，其会收到最新的 1 和 2 两个信号。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;/// 指定发射多少最新信号&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;subject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ReplaySubject&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bufferSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img alt="replaySubject" loading="lazy" src="https://i.imgur.com/uuHAQ9c.png"&gt;&lt;/p&gt;</description></item><item><title>Apple Event Sandboxing</title><link>https://hechen.github.io/zh-cn/post/appleevents-usage-description/</link><pubDate>Thu, 21 Feb 2019 23:25:38 +0800</pubDate><guid>https://hechen.github.io/zh-cn/post/appleevents-usage-description/</guid><description>&lt;h2 id="问题背景"&gt;问题背景&lt;/h2&gt;
&lt;p&gt;最近在修改某个 &lt;a href="https://github.com/hechen/vibe"&gt;Mac 应用&lt;/a&gt;，其原理就是通过执行一段 AppleScript 获取 OmniFocus 的信息，然后进行可视化展示，但是总取不到数据。&lt;/p&gt;
&lt;h2 id="原因"&gt;原因&lt;/h2&gt;
&lt;p&gt;关键数据获取阶段执行的 AppleScript 的时候总是取不到数据。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-applescript" data-lang="applescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;theProgressDetail&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;tell&lt;/span&gt; &lt;span class="nb"&gt;application&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;OmniFocus&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;tell&lt;/span&gt; &lt;span class="nb"&gt;front&lt;/span&gt; &lt;span class="na"&gt;document&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;theModifiedProjects&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="nb"&gt;every&lt;/span&gt; &lt;span class="nv"&gt;flattened&lt;/span&gt; &lt;span class="nv"&gt;project&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;repeat&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nv"&gt;a&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="nv"&gt;length&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nv"&gt;theModifiedProjects&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;theCompletedTasks&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;every&lt;/span&gt; &lt;span class="nv"&gt;flattened&lt;/span&gt; &lt;span class="nv"&gt;task&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;item&lt;/span&gt; &lt;span class="nv"&gt;a&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nv"&gt;theModifiedProjects&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;where&lt;/span&gt; &lt;span class="k"&gt;its&lt;/span&gt; &lt;span class="nv"&gt;number&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nv"&gt;tasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;theCompletedTasks&lt;/span&gt; &lt;span class="ow"&gt;is not&lt;/span&gt; &lt;span class="ow"&gt;equal to&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;repeat&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nv"&gt;b&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="nv"&gt;length&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nv"&gt;theCompletedTasks&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;theProgressDetail&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="nv"&gt;theProgressDetail&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;completion&lt;/span&gt; &lt;span class="nv"&gt;date&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;item&lt;/span&gt; &lt;span class="nv"&gt;b&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nv"&gt;theCompletedTasks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="no"&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="k"&gt;repeat&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="k"&gt;repeat&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;theInboxCompletedTasks&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;every&lt;/span&gt; &lt;span class="nv"&gt;inbox&lt;/span&gt; &lt;span class="nv"&gt;task&lt;/span&gt; &lt;span class="nb"&gt;where&lt;/span&gt; &lt;span class="k"&gt;its&lt;/span&gt; &lt;span class="nv"&gt;number&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nv"&gt;tasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;repeat&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nv"&gt;d&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="nv"&gt;length&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nv"&gt;theInboxCompletedTasks&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;theProgressDetail&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="nv"&gt;theProgressDetail&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;completion&lt;/span&gt; &lt;span class="nv"&gt;date&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;item&lt;/span&gt; &lt;span class="nv"&gt;d&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nv"&gt;theInboxCompletedTasks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="no"&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="k"&gt;repeat&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="k"&gt;tell&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="k"&gt;tell&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;display dialog&lt;/span&gt; &lt;span class="nv"&gt;theProgressDetail&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="no"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;theProgressDetail&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在 Cocoa 中是通过执行如下代码执行：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;myAppleScript&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;I am a piece of applescript&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;scriptObject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;NSAppleScript&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;myAppleScript&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;NSDictionary&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;output&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scriptObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;executeAndReturnError&lt;/span&gt;&lt;span class="p"&gt;(&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;进行 Debug 启动 App 或者 Archive 安装包执行可执行文件，均无法正常获取执行结果，直接报错。 具体错误原因：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;#34;NSAppleScriptErrorMessage&amp;#34; : &amp;#34;Not authorized to send Apple events to OmniFocus.&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;很显然，没有权限执行该脚本来发送 Apple Event 给 OmniFocus 从而执行对应操作。&lt;/p&gt;</description></item><item><title>关于一次 SS 流量丢失的过程记录</title><link>https://hechen.github.io/zh-cn/post/%E5%85%B3%E4%BA%8E%E4%B8%80%E6%AC%A1-ss-%E6%B5%81%E9%87%8F%E4%B8%A2%E5%A4%B1%E7%9A%84%E8%BF%87%E7%A8%8B%E8%AE%B0%E5%BD%95/</link><pubDate>Mon, 28 Jan 2019 17:51:35 +0000</pubDate><guid>https://hechen.github.io/zh-cn/post/%E5%85%B3%E4%BA%8E%E4%B8%80%E6%AC%A1-ss-%E6%B5%81%E9%87%8F%E4%B8%A2%E5%A4%B1%E7%9A%84%E8%BF%87%E7%A8%8B%E8%AE%B0%E5%BD%95/</guid><description>&lt;h2 id="2019-01-29-更新"&gt;2019-01-29 更新&lt;/h2&gt;
&lt;p&gt;早上一到办公室连接上公司网络，网速直接就飚到了 600KB/s，果不其然，还是 &lt;code&gt;com.apple.Safari.SafeBrowsing.Service&lt;/code&gt; 这个服务进程。这就尴尬了，看来并不是说你不用 Safari 就不会触发。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Surge Dashboard" loading="lazy" src="https://i.imgur.com/yifgIEZ.png"&gt;&lt;/p&gt;
&lt;p&gt;直接把 Surge 规则中的 Rule 由 Direct 改成了 Reject。&lt;/p&gt;
&lt;h2 id="问题追踪"&gt;问题追踪&lt;/h2&gt;
&lt;p&gt;在 2018 年 12 月底的时候，偶然间登录 AgentNEO 查看刚买的流量使用情况。但是发现，刚在前几天购买的 SS 流量在短短的 4 天之内就耗了将近 95GB，就在 1 月 1 日元旦当天就耗了接近 75GB，要知道，我平时的月均流量也就维持在 10 GB 不到，毕竟平时在公司办公，业余时间 Youtube 也看的相对少。&lt;/p&gt;
&lt;p&gt;&lt;img alt="AgentNEO 流量面板" loading="lazy" src="https://i.imgur.com/9tRs9vh.png"&gt;&lt;/p&gt;
&lt;p&gt;在最初看到这个统计数据之后，我一度怀疑是他们网站的流量统计有问题。&lt;/p&gt;
&lt;p&gt;后续几天，我实际上对流量耗损比较关注，有一天注意到，Surge 的流量监控速率一直持续不断的显示达五六百 KB/s 的下载流量，但是我知道自己当时并没有下载任何东西。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Surge Menu Bar" loading="lazy" src="https://i.imgur.com/nWz8JAt.png"&gt;&lt;/p&gt;
&lt;p&gt;打开 Surge Dashboard，看到如下的情形：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Surge Dashboard" loading="lazy" src="https://i.imgur.com/9kVX27V.png"&gt;&lt;/p&gt;
&lt;p&gt;可以看到当时在持续不断的进行下载动作的进程名称是 &lt;code&gt;com.apple.Safari.SafeBrowsing.Service&lt;/code&gt;，看请求地址是 safebrowsing.googleapis.com。&lt;/p&gt;
&lt;p&gt;从进程名字来看应该是 Safari 和安全流量相关的服务，通过 Snitch 的数据库查到对其的说明：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Safari has built-in support for Google’s Safe Browsing service to identify fraudulent and unsafe websites. Right before Safari navigates to a certain website, the website gets checked for possible security concerns using Google’s Safe Browsing online database. Accessing the online database requires connections to Google servers.&lt;/p&gt;</description></item><item><title>在 Swift Framework 中使用 C 文件的过程探索</title><link>https://hechen.github.io/zh-cn/post/swift-and-modules/</link><pubDate>Thu, 03 Jan 2019 21:19:24 +0000</pubDate><guid>https://hechen.github.io/zh-cn/post/swift-and-modules/</guid><description>&lt;h2 id="问题描述"&gt;问题描述&lt;/h2&gt;
&lt;p&gt;我们在开发线上诊断工具需求的时候，是以单个 Pod 的形式提供支持，并且代码文件中只有纯 Swift 文件，但是其中需要用到系统的 C 库的一些功能，本次就是使用了系统 C 库中 &lt;code&gt;resolv.h&lt;/code&gt; 这个文件来进行 DNS 解析所用。&lt;/p&gt;
&lt;p&gt;当后期 Pod 功能完善之后，在 Example 工程中也已经编译通过之后，接入主项目中之后遇到了下面这个编译错误：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Error When Compile" loading="lazy" src="https://i.imgur.com/ADh0sy6.png"&gt;&lt;/p&gt;
&lt;p&gt;具体文字错误信息如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-plaintext" data-lang="plaintext"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/Users/chen/Repos/Work/ZHDiagnosisTool/ZHDiagnosisTool/Classes/Core/Network/ZHDiagnosisTool-Network-Header.h:8:10:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Include of non-modular header inside framework module &amp;#39;Diagnosis.ZHDiagnosisTool_Network_Header&amp;#39;: &amp;#39;/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS12.1.sdk/usr/include/resolv.h&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;大概的意思就是在 Umbrella Header 中加入的 Bridging header 指定了 include 一个 C 文件，而该 C 文件本身并不是 Modular Header， 因此编译无法通过。这就涉及了目前 iOS 生态中普遍使用的 Modules 概念。&lt;/p&gt;
&lt;h2 id="几个概念"&gt;几个概念&lt;/h2&gt;
&lt;h3 id="modules"&gt;Modules&lt;/h3&gt;
&lt;p&gt;Modules 的概念是 XCode 5 带到 iOS 开发者面前的，从那时起 LLVM 编译器就已经内在支持了，具体关于其历史在 &lt;a href="https://onevcat.com/2013/06/new-in-xcode5-and-objc/"&gt;WWDC 2013 Session笔记 - Xcode5和ObjC新特性&lt;/a&gt; 这篇文章中已经讲解的非常详细了，不再赘述，总结一句话就是：&lt;/p&gt;</description></item><item><title>UserDefaults and Keychain</title><link>https://hechen.github.io/zh-cn/post/userdefaults-and-keychain/</link><pubDate>Fri, 21 Dec 2018 14:30:50 +0000</pubDate><guid>https://hechen.github.io/zh-cn/post/userdefaults-and-keychain/</guid><description>&lt;p&gt;Apple 提供了几种持久化方案，其中 UserDefaults 和 Keychain 是 App 开发过程中使用频率最高的方案，而且从以往和同事的探讨过程中发现对这两个概念中有一些细节还是理解不太透彻，因此本文会针对这二者展开讲一讲。&lt;/p&gt;
&lt;h2 id="userdefaults"&gt;UserDefaults&lt;/h2&gt;
&lt;p&gt;首先，阅读完 Apple 关于 UserDefaults 一节的文档描述之后，我觉得有两个需要注意的点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;UserDefaults 的构成&lt;/li&gt;
&lt;li&gt;UserDefaults 的目的&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="userdefaults-构成"&gt;UserDefaults 构成&lt;/h3&gt;
&lt;p&gt;我们可以看看具体 UserDefaults 存储的地方，如下图，我们在应用中写入 Demo 数据：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Write Key-Value to UserDefaults" loading="lazy" src="https://i.imgur.com/wq9onOa.png"&gt;&lt;/p&gt;
&lt;p&gt;打印 Library 路径，在 Finder 中打开路径:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Library" loading="lazy" src="https://i.imgur.com/65ybYX6.png"&gt;&lt;/p&gt;
&lt;p&gt;如下展示内容，可以看到在 Preferences 目录中存在某个文件，例如该 App Library 目录下存放的文件为 &lt;code&gt;app.chen.ios.PersistenceDemo.plist&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Cache" loading="lazy" src="https://i.imgur.com/ChqnDJa.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Preferences" loading="lazy" src="https://i.imgur.com/cPvlFdR.png"&gt;&lt;/p&gt;
&lt;p&gt;该 plist 内容如图所示，即为我们之前在 App 中写入的 Key-Value。&lt;/p&gt;
&lt;p&gt;&lt;img alt="plist" loading="lazy" src="https://i.imgur.com/shdXoZN.png"&gt;&lt;/p&gt;
&lt;p&gt;所以这也证明了，我们针对 UserDefaults 的读写实质上是针对 plist 文件的读写。&lt;/p&gt;
&lt;p&gt;iOS 系统会自动帮你做 &lt;code&gt;plist&lt;/code&gt; 文件的读入以及 Cache，根据情况会把你在内存中所做的操作同步到 &lt;code&gt;plist&lt;/code&gt; 文件（UserDefaults 同步到内存是同步的，同步到 Database 是异步的， iOS 8 开始，会有一个常驻进程 &lt;code&gt;cfprefsd&lt;/code&gt; 来负责同步）。&lt;/p&gt;</description></item><item><title>如何制作 Send to 2Do 的 Safari 书签</title><link>https://hechen.github.io/zh-cn/post/send-to-2do/</link><pubDate>Mon, 26 Nov 2018 14:59:50 +0000</pubDate><guid>https://hechen.github.io/zh-cn/post/send-to-2do/</guid><description>&lt;p&gt;使用鼠标拖拽下面这个链接到你的 Favorites Bar 上，默认点击行为实际上是模拟在当前页面进行导航，而目标地址由 &lt;code&gt;avascript:window.location&lt;/code&gt; 来指定，目标地址实际上就是各个 App 的 URL Scheme。
你可以在 &lt;a href="https://app-talk.com/"&gt;AppTalk&lt;/a&gt;查看各个主流 App 的 URL Scheme。&lt;/p&gt;
&lt;h2 id="2do"&gt;2Do&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://hechen.github.io/zh-cn/post/send-to-2do/#ZgotmplZ"&gt;Send to 2Do&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;然后编辑地址，将其替换为如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;javascript:window.location=&amp;#39;twodo://x-callback-url/add?task=&amp;#39;+encodeURIComponent(document.title)+&amp;#39;&amp;amp;note=&amp;#39;+encodeURIComponent(window.location)+&amp;#39;&amp;amp;action=url:&amp;#39;+encodeURIComponent(window.location)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img alt="Change Value" loading="lazy" src="https://i.imgur.com/7M0xN1g.jpg"&gt;&lt;/p&gt;
&lt;p&gt;所以，本质上是执行了一段 JS 代码，结合当前文档的上下文信息，调用 URL Scheme，其中&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;document.title&lt;/code&gt; 为标题&lt;/li&gt;
&lt;li&gt;&lt;code&gt;window.location&lt;/code&gt; 为当前页面链接&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="things"&gt;Things&lt;/h2&gt;
&lt;p&gt;我们知道 Things 的 URL Scheme 的规则如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;/// add a todo due when tomorrow
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nl"&gt;things&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;///add?title=iamtitle&amp;amp;notes=iamnotes&amp;amp;when=tomorrow
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href="https://hechen.github.io/zh-cn/post/send-to-2do/#ZgotmplZ"&gt;Add a todo to Things&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;比如你可以把链接内容改为支持 Send To Things：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;javascript&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;things:///add?title=&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nb"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;amp;notes=&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nb"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;amp;when=today&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="omnifocus"&gt;OmniFocus&lt;/h2&gt;
&lt;p&gt;同理，可以有 Send To OmniFocus ，如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;javascript&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;omnifocus:///add?note=&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nb"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;amp;name=&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nb"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href="https://hechen.github.io/zh-cn/post/send-to-2do/#ZgotmplZ"&gt;Add a todo to OmniFocus&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;或者直接去 &lt;a href="http://people.omnigroup.com/kc/OmniFocus/SendToOmniFocusBookmarklet.html"&gt;OmniFocus 页面&lt;/a&gt; 用相同方式把起已经设置好正确 Value 的链接拖到上方工具条上即可。&lt;/p&gt;</description></item><item><title>阅读流程</title><link>https://hechen.github.io/zh-cn/post/reading-process/</link><pubDate>Tue, 16 Oct 2018 17:01:44 +0000</pubDate><guid>https://hechen.github.io/zh-cn/post/reading-process/</guid><description>&lt;p&gt;最近统一了一下自己的阅读流程，更新一下，具体用到的工具有&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;DEVONThink Pro&lt;/li&gt;
&lt;li&gt;PDFExpert&lt;/li&gt;
&lt;li&gt;MarginNote&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;其中 1 是输入和归档源，3 是输出源。 大概就是如下所示：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Reading Process" loading="lazy" src="https://i.imgur.com/SrcVtP1.png"&gt;&lt;/p&gt;
&lt;h2 id="采集素材"&gt;采集素材&lt;/h2&gt;
&lt;p&gt;在平时浏览网页的时候，看到自己喜欢的文章之后，想进行一些标记或者做笔记，大概有几个选择：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;印象笔记直接 clip 当前网页，之后在印象笔记里进行阅读或者标记&lt;/li&gt;
&lt;li&gt;放进稍后读服务中，比如 Pocket 或者 Instapaper 等；&lt;/li&gt;
&lt;li&gt;用 Safari 自带的 Save as PDF 之后再用单独的 PDF 工具查看；&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;而最近几个月，因为 MarginNote 3 的发布，由于其提供的大纲脑图的自动生成，给我提供了一种便利的拆书功能，因此会把平时看到的文章都归集到一处来看。&lt;/p&gt;
&lt;p&gt;借助 DEVONthink 的 Clip To DEVONthink 的插件可以很方便的把文章归集到 DEONThink 中，最主要的是因为它能保存 WebArchive，即使后续文章被删除也没关系。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Clip To DEVONthink" loading="lazy" src="https://i.imgur.com/Kw7CoBl.png"&gt;&lt;/p&gt;
&lt;p&gt;当按下 Cmmd + S 进行归档之后，DEVONThink 对应 Group 里就会把该原始页面保存起来。&lt;/p&gt;
&lt;p&gt;&lt;img alt="DEVONthink" loading="lazy" src="https://i.imgur.com/m3jM9kx.png"&gt;&lt;/p&gt;
&lt;p&gt;由于 WebArchive 无法做 Highlight 标记，这时候就用到了 DEVONthink 提供的 OCR 功能，将 WebArchive 提取为 PDF 文件。&lt;/p&gt;</description></item><item><title>三个简单步骤让你测试使用系统单例的代码</title><link>https://hechen.github.io/zh-cn/post/testing-swift-code-that-uses-system-singletons-in-3-easy-steps/</link><pubDate>Tue, 17 Jul 2018 17:09:57 +0000</pubDate><guid>https://hechen.github.io/zh-cn/post/testing-swift-code-that-uses-system-singletons-in-3-easy-steps/</guid><description>&lt;blockquote&gt;
&lt;p&gt;原文：&lt;a href="https://www.swiftbysundell.com/articles/testing-swift-code-that-uses-system-singletons-in-3-easy-steps/"&gt;Testing Swift code that uses system singletons in 3 easy steps&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;原作者 &lt;a href="https://twitter.com/johnsundell"&gt;@johnsundell&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;大部分在 Apple 平台开发的 App 都会依赖基于单例的 API。从 UIScreen 到 UIApplication，再到 NSBundle，而 Foundation，UIKit 以及 AppKit 里到处充斥着静态的 API。&lt;/p&gt;
&lt;p&gt;尽管单例非常方便，并且随时随地都可以很轻易的获取到特定的 API，但是当它们一旦要面临代码解耦和测试的时候就会出现挑战。单例同时也是平时遇到 Bug 里最常见的一种，在单例中状态会以共享的方式终结，那些针对状态做的变更并不能很好的广播到整个系统层面。&lt;/p&gt;
&lt;p&gt;然而，尽管我们能够重构自己的代码，让它们只在真正需求的地方使用单例，我们针对系统 API 也做不了太多。但是，好消息是，这里有一些技巧让你使用系统单例的代码依然变得容易管理和测试。&lt;/p&gt;
&lt;p&gt;让我们看一些使用了 URLSession.shared 单例的代码：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DataLoader&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;enum&lt;/span&gt; &lt;span class="nc"&gt;Result&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;completionHandler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="n"&gt;escaping&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;task&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;URLSession&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shared&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dataTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;error&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;completionHandler&lt;/span&gt;&lt;span class="p"&gt;(.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;completionHandler&lt;/span&gt;&lt;span class="p"&gt;(.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resume&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;上面这个 &lt;strong&gt;DataLoader&lt;/strong&gt; 现在就变得很难测试，因为它内部会自主调用共享的 URL Session 来执行一个网络调用。这就需要我们为测试代码中增加一些等待和超时代码，然后很快这部分代码就会变得糟糕和不稳定。&lt;/p&gt;
&lt;h2 id="抽象成一个协议"&gt;抽象成一个协议&lt;/h2&gt;
&lt;p&gt;我们第一个任务就是把我们需要的 URLSession 的部分移到一个协议里，这样我们在测试中也能够很容易的进行 mock。在作者的&lt;a href="http://www.ustream.tv/recorded/101118612"&gt;一次 talk&lt;/a&gt;中作者建议尽可能的避免 mock，尽管它是一种很好的策略。但是当你和系统的单例打交道的时候，mock 就是一个增加可预测性的重要工具。&lt;/p&gt;
&lt;p&gt;让我们创建一个 &lt;strong&gt;NetworkEngine&lt;/strong&gt; 协议，然后让 URLSession 符合它:&lt;/p&gt;</description></item><item><title>避免在 Swift 中使用单例</title><link>https://hechen.github.io/zh-cn/post/avoiding-singletons-in-swift/</link><pubDate>Mon, 16 Jul 2018 17:34:36 +0000</pubDate><guid>https://hechen.github.io/zh-cn/post/avoiding-singletons-in-swift/</guid><description>&lt;blockquote&gt;
&lt;p&gt;原文：&lt;a href="https://www.swiftbysundell.com/articles/avoiding-singletons-in-swift/"&gt;Avoiding singletons in Swift&lt;/a&gt;
原作者 &amp;amp; Copyright &lt;a href="https://twitter.com/johnsundell"&gt;@johnsundell&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;“我知道单例不好，但是…”，这是开发者常常在讨论代码的时候会提到的。貌似社区大家有共识 ── 单例不好。但是同时，包括 Apple 和第三方的 Swift 开发者还是在 App 内部或者共享的 &lt;code&gt;frameworks&lt;/code&gt; 里不断在用它们。&lt;/p&gt;
&lt;p&gt;这周，让我们好好看一看单例的问题到底在哪里，探索更多的技巧以便我们日常能够避免使用它们。这就开始吧！&lt;/p&gt;
&lt;h3 id="为什么单例这么流行"&gt;为什么单例这么流行？&lt;/h3&gt;
&lt;p&gt;首先一点我们要想的是问问自己，为什么单例这么流行？热过大多数开发者都认同，单例应该被避免的话，为什么它们还不断的出现呢？&lt;/p&gt;
&lt;p&gt;我认为有两部分原因。&lt;/p&gt;
&lt;p&gt;我觉得日常开发 Apple 平台上的 App 时候大量使用单例，最大的原因是苹果自己也在经常的用。作为第三方开发者来讲，我们常常会把 Apple 的做法作为最佳实践来用，任何 Apple 自己常常使用的模式在社区也会发展的很迅速。&lt;/p&gt;
&lt;p&gt;这个谜题的第二个原因是单例非常方便。单例因为它可以在任何地方获取到，因而经常会扮演一个获取特定核心值或者对象的快捷方式。可以看下下面这个例子，在这个例子中，我们想在 &lt;strong&gt;ProfileViewController&lt;/strong&gt; 中展示当前登录用户的名字，同时当某个按钮按下的时候，退登当前用户。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProfileViewController&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;UIViewController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kr"&gt;lazy&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;nameLabel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;UILabel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;override&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;viewDidLoad&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kc"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;viewDidLoad&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;nameLabel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;UserManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shared&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;handleLogOutButtonTap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;UserManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shared&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logOut&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;按照上面这样，把一个 UserManager 单例的用户和账户处理的功能封装起来，确实很方便（而且很常见！），那这种模式的使用到底不好在哪里呢？&lt;/p&gt;
&lt;h3 id="为什么单例会很糟糕"&gt;为什么单例会很糟糕？&lt;/h3&gt;
&lt;p&gt;当讨论类似模式和架构这些东西的时候，很容易陷入一个特别理论化的圈套。虽然让我们的代码理论上“正确”，遵循最佳实践和原则是很美好的，但是现实经常会很受打击，我们需要寻找一些中间方案。&lt;/p&gt;
&lt;p&gt;所以，关于单例引起的实质性问题是什么呢？为什么它们要应该被避免呢？这里我倾向于三个原因：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;这些单例是全局的可变的共享状态。它们的状态是在整个 App 里自动共享的，因此当其状态发生不可预期的变化的时候，Bug 也就可能开始发生了；&lt;/li&gt;
&lt;li&gt;单例之间的关系以及依赖这些单例的代码常常不那么好定义。因为单例是如此方便和容易获得，大量的使用它们常常会导致很难维护&lt;a href="https://zh.wikipedia.org/wiki/%E9%9D%A2%E6%9D%A1%E5%BC%8F%E4%BB%A3%E7%A0%81"&gt;面条式代码&lt;/a&gt;，这些代码中对象和对象之间没有清晰的隔离；&lt;/li&gt;
&lt;li&gt;管理这些单例的生命周期非常的难。因为单例本身会在应用的整个生命周期内存活，管理它们就变得异常困难，因此常常会不得不依赖可选值来跟踪值的变化。这也使得依赖单例的代码变得很难测试，因为你没法在一个测试 case 里把状态重置。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;在我们前面的 &lt;strong&gt;ProfileViewController&lt;/strong&gt; 的例子中，我们已经看到了有这三个问题的信号出现。首先它依赖 UserManager 导致这二者关系很不清晰，其次，它不得不让 currentUser 作为可选值出现，因为我们没办法在编译期就能够确定视图控制器实际上出现的时候这个数据一定在。听起来就感觉要有 bug 发生的感觉 😬!&lt;/p&gt;
&lt;h3 id="依赖注入"&gt;依赖注入&lt;/h3&gt;
&lt;p&gt;相对于使用 &lt;strong&gt;ProfileViewController&lt;/strong&gt; 通过单例来获取它所需的依赖项，我们要在其初始化方法中将依赖项传入。这里我们是将当前的 User 作为非可选传入的，同样，传入一个 &lt;strong&gt;LogOutService&lt;/strong&gt; 来进行登出操作：&lt;/p&gt;</description></item><item><title>Swift 中几种不同的依赖注入方式</title><link>https://hechen.github.io/zh-cn/post/different-flavors-of-dependency-injection-in-swift/</link><pubDate>Mon, 16 Jul 2018 13:57:24 +0000</pubDate><guid>https://hechen.github.io/zh-cn/post/different-flavors-of-dependency-injection-in-swift/</guid><description>&lt;blockquote&gt;
&lt;p&gt;原文：&lt;a href="https://www.swiftbysundell.com/articles/different-flavors-of-dependency-injection-in-swift/"&gt;Different flavors of dependency injection in Swift&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;原作者 &amp;amp; Copyright &lt;a href="https://twitter.com/johnsundell"&gt;@johnsundell&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在之前的几篇博客中，我们已经了解了几种使用依赖注入方式使得某个 swift app 拥有一个更加解耦可测试的架构。比如在 &lt;a href="https://hechen.github.io/2017/11/19/Dependency-injection-using-factories-in-Swift/"&gt;在 Swift 中使用工厂模式进行依赖注入&lt;/a&gt; 中和工厂模式结合，以及在&lt;a href="https://hechen.github.io/2018/07/16/Avoiding-singletons-in-Swift/"&gt;避免在 Swift 中使用单例&lt;/a&gt;中替换程序中的单例对象等方式进行依赖注入。&lt;/p&gt;
&lt;p&gt;到目前为止，大部分我的博文以及例子中都使用了基于初始化的依赖注入方式。然而，就像大部分的编程技巧一样，还有很多 “口味” 的进行依赖注入的方式 ── 每一种都有其优缺点。 这周，让我们来看看其中三种方式以及如何在 Swift 中使用它们。&lt;/p&gt;
&lt;h3 id="基于初始化方法"&gt;基于初始化方法&lt;/h3&gt;
&lt;p&gt;让我们快速回顾一下用的最为广泛的依赖注入方式 ── initializer-based。这个想法是在某个类被初始化的时候传入其所需的依赖的方式。这种形式最大的好处就是，它保证了能够立即满足该类完成其功能所需要的所有东西。&lt;/p&gt;
&lt;p&gt;让我们建一个类 ── FileLoader 来从磁盘加载文件。为了实现该 Loader 的功能，需要两个依赖项，系统所提供的实例对象 ── &lt;strong&gt;FileManager&lt;/strong&gt; 和 &lt;strong&gt;Cache&lt;/strong&gt;。使用基于初始化的依赖注入的执行代码如下所示：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FileLoader&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;fileManager&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;FileManager&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Cache&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fileManager&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;FileManager&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Cache&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;init&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kc"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fileManager&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fileManager&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kc"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以关注下初始化方法里默认参数的设置，其可以避免每次都需要自行创建的问题。这样能够简化我们在生产环境下使用 &lt;strong&gt;FileLoader&lt;/strong&gt; 类创建文件加载器的工作，而且也能够能够传入 mock 数据或者我们的测试代码中的创建的实例进行测试。&lt;/p&gt;
&lt;h3 id="基于属性"&gt;基于属性&lt;/h3&gt;
&lt;p&gt;虽然前一种基于初始化的依赖注入形式常常给我们自定义类带来很多好处，但是有时候，当你不得不继承自某个系统类的时候会遇到一些困难。其中一个例子就是，当我们构建 view controller，尤其是你在使用 XIB 以及 Storyboard 类定义它们的时候，因为此时你不再能够掌控它们的初始化方法了。&lt;/p&gt;
&lt;p&gt;对于这些情况，基于属性的依赖注入方式是一个更好的选择。相对于初始化的时候进行对象的依赖项注入，基于属性的形式可以在之后通过简单的赋值来做到，其能够让你减少模板代码的书写，尤其是在你确实没有必要进行注入的时候能够有更好的默认实现。&lt;/p&gt;
&lt;p&gt;让我们再来看一个例子，在这个例子中，我们在构建一个 &lt;strong&gt;PhotoEditorViewController&lt;/strong&gt; ，这个视图控制器使得用户编辑它们照片库中的图片。为了达到这个功能，该视图控制器需要用到系统提供的 PHPhotoLibrary 类型的一个实例（该类本身是一个单例）以及我们自己实现的类 &lt;strong&gt;PhotoEditorEngine&lt;/strong&gt; 的一个实例。那为了不通过自定义初始化方法进行依赖注入的话，我们可以创建一些具有默认值的可变属性，例如下面这样：&lt;/p&gt;</description></item><item><title>那些我恢复 Mac 系统之后要安装的 Apps</title><link>https://hechen.github.io/zh-cn/post/my-favorite-mac-apps/</link><pubDate>Wed, 11 Jul 2018 12:58:37 +0000</pubDate><guid>https://hechen.github.io/zh-cn/post/my-favorite-mac-apps/</guid><description>&lt;p&gt;记录一下自己常用的 Mac Apps，每次重装之后到了用的时候发现忘记安装了(不是每次都用 TimeMachine 恢复)，而且强烈简易大家如果某个 App 有对应的独立安装版本的话，购买其独立安装版本为好，沙盒环境以及和 Apple ID 绑定的特性就使得功能性以及灵活度大减。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;更新时间 2018-10-07
更换图床，更新图片&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;更新时间 2018-08-10
不久前，已经订阅了一年的 Setapp，发现上面好多 App 自己都之前买过了，Sigh！
如果你使用 Ulysses 或者 MoneyWiz 3.0 的话，订阅 Setapp 就会非常的值得！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img alt="Mac Apps" loading="lazy" src="https://i.imgur.com/q3yToeZ.png"&gt;&lt;/p&gt;
&lt;h2 id="办公工具"&gt;办公工具&lt;/h2&gt;
&lt;p&gt;离开这些工具，貌似无法办公的。&lt;/p&gt;
&lt;h3 id="airmail"&gt;AirMail&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://itunes.apple.com/cn/app/airmail-3/id918858936?l=en&amp;amp;mt=12"&gt;下载地址&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="AirMai" loading="lazy" src="https://i.imgur.com/19ADIxn.jpg"&gt;&lt;/p&gt;
&lt;p&gt;邮箱客户端，看中 AirMail 主要是因为其强大的第三方服务集成。&lt;/p&gt;
&lt;h3 id="slack"&gt;Slack&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://itunes.apple.com/cn/app/slack/id803453959?mt=12"&gt;下载地址&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Slack" loading="lazy" src="https://i.imgur.com/W1wtsje.jpg"&gt;&lt;/p&gt;
&lt;p&gt;公司的日常沟通交流主要战场，其实整个操作体验我是不太适应的，不过它强大在第三方服务的集成上，可以做很多自动化的事情。&lt;/p&gt;
&lt;h3 id="fantastical-2"&gt;Fantastical 2&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://flexibits.com/fantastical"&gt;下载地址&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Fantastical" loading="lazy" src="https://i.imgur.com/1naxpaM.jpg"&gt;&lt;/p&gt;
&lt;p&gt;日历工具，查看满满的一屏幕日程，你会感觉很舒服的。&lt;/p&gt;
&lt;h4 id="tunnelblick"&gt;Tunnelblick&lt;/h4&gt;
&lt;p&gt;&lt;a href="https://tunnelblick.net/downloads.html"&gt;下载地址&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Tunnelblick-menu" loading="lazy" src="https://i.imgur.com/ghIEHNQ.png"&gt;&lt;/p&gt;
&lt;p&gt;公司需要使用该 App 来接入办公室网络。远程办公必备。&lt;/p&gt;
&lt;h4 id="office-三件套"&gt;Office 三件套&lt;/h4&gt;
&lt;p&gt;微软的 Office 三件套，Word，Excel，PowerPoint。 用的频率不高，但是用的时候得有。&lt;/p&gt;
&lt;h2 id="gtd"&gt;GTD&lt;/h2&gt;
&lt;p&gt;日常 GTD 工具，基本就是各家评测常提起的几款 App。&lt;/p&gt;
&lt;h3 id="2do"&gt;2Do&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.2doapp.com/"&gt;下载地址&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="2Do on Mac" loading="lazy" src="https://i.imgur.com/st89Smb.jpg"&gt;&lt;/p&gt;
&lt;p&gt;最近才完全转向 2Do，这个 GTD 工具是我最早买入的，但是后来因为其自动化太弱，被打入冷宫。最近几个月又开始捡起来了，吸引我的几个点：&lt;/p&gt;</description></item><item><title>What is LLVM</title><link>https://hechen.github.io/zh-cn/post/what-is-llvm/</link><pubDate>Tue, 10 Jul 2018 12:05:58 +0000</pubDate><guid>https://hechen.github.io/zh-cn/post/what-is-llvm/</guid><description>&lt;h1 id="什么是-llvm隐藏在-swiftrustclang-等语言背后的奥秘"&gt;什么是 LLVM？隐藏在 Swift，Rust，Clang 等语言背后的奥秘&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;了解编译器是如何生成机器原生代码会使得倒腾新语言或者加强已经存在的编程语言变得比以往更加容易了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;新的编程语言，针对现存语言的优化如同雨后春笋般产生。 Mozilla 的 &lt;strong&gt;Rust&lt;/strong&gt;， Apple 的 &lt;strong&gt;Swift&lt;/strong&gt;，JetBrain 的 &lt;strong&gt;Kotlin&lt;/strong&gt;等语言给开发者提供了在速度，安全性，便捷性，可移植性以及性能等方面更多的选择。&lt;/p&gt;
&lt;p&gt;为什么是这个时间点呢？一个重大的原因是那些构建具体语言相关的新式工具的出现。在这一堆工具中，最重要的就是 LLVM，其全称是 Low-Level Virtual Machine，它是一个开源项目，其最早是由 Swift 语言的发明人 Chris Lattner 在伊利诺斯州州立大学期间的一个研究项目而来。&lt;/p&gt;
&lt;p&gt;LLVM 不仅仅使得创建新式语言更加容易，也使得针对现存编程语言进行增强完善更加便捷。它提供了一堆工具使得创造语言的过程中需要的那些令人头疼的事情变得自动化：创建一个编译器，输出代码需要适配多平台和架构，编写代码处理语言当中通常都存在的那些比较晦涩的部分，比如异常。LLVM 的自由开发许可使得它可以被自由的作为一个软件组件或者被部署成服务来使用。&lt;/p&gt;
&lt;p&gt;使用了 LLVM 的编程语言花名册种有很多熟悉的名字。Apple 的 Swift 语言使用 LLVM 作为其编译器框架，Rust 语言使用 LLVM 作为其工具链中的一个核心组件。同样，许多编译器也有其 LLVM 版本，例如 Clang，一个 C/C++ 编译器，它是和 LLVM 功能特别相近的一个项目。还有 Kotlin 语言，其名义上是一门 JVM 语言，也正在开发该语言的一个版本： &lt;a href="https://www.infoworld.com/article/3187370/application-development/kotlin-compiles-directly-to-native-code-via-llvm.html"&gt;Kotlin Native&lt;/a&gt;，其使用 LLVM 编译成机器原生代码。&lt;/p&gt;
&lt;h2 id="llvm-是什么"&gt;LLVM 是什么&lt;/h2&gt;
&lt;p&gt;LLVM 最核心的功能就是，可以通过编码方式创造机器原生代码。一个开发者可以使用其 API 创造指令，该指令是一种 intermediate representation 格式，简称 IR。之后，LLVM 能够将 IR 编译成独立的二进制文件，或者对其执行即时编译以运行在另外一个程序，例如某编程语言的解释器的上下文中。&lt;/p&gt;
&lt;p&gt;很多编程语言中很常见的结构体或者模式，LLVM 的 API 都提供了原始语义的支持。例如，几乎所有的语言都有函数或者全局变量的概念，LLVM 在其 IR 中把函数和全局变量作为独立元素定义出来，因此你不需要花太多时间和精力来重复造这些特定的轮子，你只需要使用 LLVM 的实现，专注于你的语言最需要关注的地方即可。&lt;/p&gt;</description></item><item><title>In App Purchase</title><link>https://hechen.github.io/zh-cn/post/in-app-purchase/</link><pubDate>Thu, 24 May 2018 10:40:39 +0000</pubDate><guid>https://hechen.github.io/zh-cn/post/in-app-purchase/</guid><description>&lt;p&gt;对于在 App Store 中上架的应⽤来说，应⽤内购买(In-App Purchase，简称 IAP) 应该是一个避不开的话题，尤其是去年微信打赏和 Apple 之间的争执更让 IAP 火了一把，不仅仅大公司，作为个人开发者来讲，IAP 也是非常重要的，说不定就是你养家糊口的工具呢。&lt;/p&gt;
&lt;p&gt;整个 IAP 的过程，在客户端的实现依赖于 &lt;code&gt;StoreKit&lt;/code&gt; 这个框架，所有的支付相关操作都是交由 StoreKit 来完成的。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://i.imgur.com/EkBwhM4.png"&gt;&lt;/p&gt;
&lt;p&gt;首先，我们先复习一下 IAP 的整个流程。&lt;/p&gt;
&lt;h2 id="in-app-purchase-的整体流程"&gt;In App Purchase 的整体流程&lt;/h2&gt;
&lt;h3 id="阶段-1"&gt;阶段 1&lt;/h3&gt;
&lt;p&gt;&lt;img alt="In App Purchase 过程" loading="lazy" src="http://7xilk1.com1.z0.glb.clouddn.com/2018-09-03-4DDE8387-EDB1-42CC-B18C-3DC6CA1DC688.png"&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;加载 In-App Identifier&lt;/li&gt;
&lt;li&gt;客户端从 AppStore 中获取本地化的商品信息。&lt;/li&gt;
&lt;li&gt;把 IAP 的购买界⾯面展示给⽤用户，⽤用户可以同意购买并点击购买按钮。&lt;/li&gt;
&lt;li&gt;⽤用户授权购买，客户端向服务器器发送购买请求。&lt;/li&gt;
&lt;li&gt;服务器器处理理购买请求，并把结果返回给 &lt;code&gt;StoreKit&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;如果购买请求验证通过，客户端此时解锁内容或者提供金币。&lt;/li&gt;
&lt;li&gt;至此，整个交易易流程结束。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="阶段-2"&gt;阶段 2&lt;/h3&gt;
&lt;p&gt;具体到和 Apple Store 打交道的话，如下所示：&lt;/p&gt;
&lt;p&gt;&lt;img alt="IAP With App Store" loading="lazy" src="http://7xilk1.com1.z0.glb.clouddn.com/2018-08-27-intro_2x.png"&gt;&lt;/p&gt;
&lt;h3 id="阶段-3"&gt;阶段 3&lt;/h3&gt;
&lt;p&gt;如果涉及到自己 App 端 Server 的参与，基本如下图所示：&lt;/p&gt;
&lt;p&gt;&lt;img alt="IAP With Business Server" loading="lazy" src="http://7xilk1.com1.z0.glb.clouddn.com/2018-08-29-1*MwLeHUGSupbb9j-oYQyflw.png"&gt;&lt;/p&gt;
&lt;p&gt;正常多出的几步：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;获取 Product 标识列表；&lt;/li&gt;
&lt;li&gt;客户端上传 Receipt 数据给 Server，Server 自行校验或者使用 Apple 的接口校验；&lt;/li&gt;
&lt;li&gt;Server 端告知给客户端付费内容&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="注册商品"&gt;注册商品&lt;/h3&gt;
&lt;p&gt;商品注册是通过 iTunes Connect 后台进行的，其中商品类型需要提前明确好。&lt;/p&gt;</description></item><item><title>模块化 Xcode 工程</title><link>https://hechen.github.io/zh-cn/post/modular-xcode-projects/</link><pubDate>Sun, 19 Nov 2017 18:17:15 +0000</pubDate><guid>https://hechen.github.io/zh-cn/post/modular-xcode-projects/</guid><description>&lt;blockquote&gt;
&lt;p&gt;原文：&lt;a href="https://ppinera.es/2017/09/29/modular-xcode-projects.html"&gt;Modular Xcode projects&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;原作者 &amp;amp; Copyright &lt;a href="http://twitter.com/pepibumur"&gt;@pepibumur&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;翻译：&lt;a href="https://twitter.com/OgreMergO"&gt;@OgreMergO&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;使用 Xcode 构建模块化的工程就需要对工程结构以及其基础概念有很好的理解才行。&lt;/p&gt;
&lt;p&gt;我们平时不怎么关注工程结构本身，只有在工程逐渐变大，要添加更多依赖的时候才会注意的到。而即使到了这个时候，我们大多数的工程都会使用 &lt;a href="https://cocoapods.org/"&gt;CocoaPods&lt;/a&gt; 来设置那些依赖项，或者 &lt;a href="https://github.com/carthage"&gt;Carthage&lt;/a&gt;, 后者虽然没有帮我们做依赖性的设置，但是使得我们会更容易的，通过在工程的 build phase 选项中添加一些内容，达到同样的目的。当配置项越来越复杂，我们就很容易产生困惑，这是因为我们并没有完全掌握 Xcode 工程中所涉及的所有元素。我经常被问到的问题如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;我能不能在工程里同时使用 Carthage，Cocoapods 以及自己个人的依赖设置？&lt;/li&gt;
&lt;li&gt;我添加了依赖，但是当模拟器打开 App 的时候 Crash 了。&lt;/li&gt;
&lt;li&gt;为什么我需要只在某些 targets 里嵌入 framework？&lt;/li&gt;
&lt;li&gt;我的 framework 应该是静态的还是动态的？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在这篇博文中，我会引导你遍历 Xcode Project 中的各个元素，指导你如何通过改变这些元素来模块化你的设置项。我希望下次你遇到上面这些问题的时候，你不需要再花大量时间取 Stack Overflow 上查这些确定的答案。&lt;/p&gt;
&lt;h3 id="elements-"&gt;Elements ⚒&lt;/h3&gt;
&lt;h4 id="target"&gt;Target&lt;/h4&gt;
&lt;p&gt;工程（Projects）都是由多个更小的叫做 target 的单元组成的。这些 target 包含编译特定平台产品，比如 frameworks, libraries, apps, testing bundles, extensions 等所需要的配置。 你可以在&lt;a href="https://github.com/xcodeswift/xcproj/blob/master/Sources/xcproj/PBXProductType.swift"&gt;这里&lt;/a&gt;看到 target 所有可用的类型。 Target 可以相互依赖，当一个 target A 依赖另外一个 target B 的时候，target B 就会被先构建出来以便 target A 使用其产出。而 target 的配置项会涉及以下几个地方：&lt;/p&gt;</description></item><item><title>在 Swift 中使用工厂模式进行依赖注入</title><link>https://hechen.github.io/zh-cn/post/dependency-injection-using-factories-in-swift/</link><pubDate>Sun, 19 Nov 2017 18:17:15 +0000</pubDate><guid>https://hechen.github.io/zh-cn/post/dependency-injection-using-factories-in-swift/</guid><description>&lt;blockquote&gt;
&lt;p&gt;原文：&lt;a href="https://www.swiftbysundell.com/articles/dependency-injection-using-factories-in-swift/"&gt;Dependency injection using factories in Swift&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;原作者 &amp;amp; Copyright &lt;a href="https://twitter.com/johnsundell"&gt;@johnsundell&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;依赖注入是一项使得代码更加可测试的关键工具。我们不需要持有某些对象，或者创建这些对象的自有依赖，或者通过单例来获取他们，而是尽可能使那些对象能够正常运转所必须的一切内容（其他对象）通过外界传入，这样做的好处在于，一方面能清晰的看得到某个对象的所有依赖项，另一方便也使得测试工作变得更为简单（因为我们可以模拟这些依赖项来捕获、验证状态以及值。）&lt;/p&gt;
&lt;p&gt;然而，尽管依赖注入确实很有用，但是当在工程中广泛使用的时候还是会有一些痛点。随着某个对象的依赖项越来越多，初始化该对象就变得越来越蹩脚。虽然使得代码可测没毛病，但是如果像下面这种每次需要这样来写初始化方法，也太不爽了。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;class UserManager {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; init(dataLoader: DataLoader, database: Database, cache: Cache,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; keychain: Keychain, tokenManager: TokenManager) {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; // ...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;所以，这周咱们来深入了解一下某种依赖注入的技巧，使得我们的代码不失去可测性，我们也不需要再强迫自己去写一团初始化方法或者复杂的依赖管理的代码。&lt;/p&gt;
&lt;h2 id="传递依赖项"&gt;传递依赖项&lt;/h2&gt;
&lt;p&gt;我们遇到上面代码 demo 中的问题，最主要的原因是我们需要把这么多依赖项传递给某个对象，以便之后来使用。举例来说，我们在构建一个消息收发的 App，这里有一个 view controller 需要展示某个用户所有的消息：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;class MessageListViewController: UITableViewController {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; private let loader: MessageLoader
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; init(loader: MessageLoader) {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; self.loader = loader
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; super.init(nibName: nil, bundle: nil)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; override func viewWillAppear(_ animated: Bool) {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; super.viewWillAppear(animated)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; loader.load { [weak self]() messages in
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; self?.reloadTableView(with: messages)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如上代码能看到，我们给 &lt;code&gt;MessageListViewController&lt;/code&gt; 传入某个依赖项 &lt;code&gt;MessageLoader&lt;/code&gt;，之后其被用来加载数据。这里其实并没有太大的问题，因为仅仅只有一个依赖而已。然而，我们的列表视图并不是一个死气沉沉的展示而已，某些状态下还需要们进行导航到某视图控制器的工作。&lt;/p&gt;</description></item><item><title>处理 Swift 中非可选的可选值类型</title><link>https://hechen.github.io/zh-cn/post/handling-non-optional-optionals-in-swift/</link><pubDate>Tue, 14 Nov 2017 21:53:52 +0000</pubDate><guid>https://hechen.github.io/zh-cn/post/handling-non-optional-optionals-in-swift/</guid><description>&lt;blockquote&gt;
&lt;p&gt;原文：&lt;a href="https://www.swiftbysundell.com/articles/handling-non-optional-optionals-in-swift/"&gt;Handling non-optional optionals in Swift&lt;/a&gt;
原作者 &amp;amp; Copyright &lt;a href="https://twitter.com/johnsundell"&gt;@johnsundell&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;可选值（optionals）无可争议的是 &lt;code&gt;swift&lt;/code&gt; 语言中最重要的特性之一，也是和其他语言，例如 &lt;code&gt;Objective-C&lt;/code&gt; 的最大区别。通过强制处理那些有可能出现 &lt;code&gt;nil&lt;/code&gt; 的地方，我们就能写出更有预测性的以及更健壮的代码。&lt;/p&gt;
&lt;p&gt;然而，有些时候可选值可能会致你于尴尬的境地，尤其是你作为开发者了解（甚至是有些猜测的成分在），有的特定变量始终是非空（non-nil）的，即使它是一个可选类型。例如，我们在一个视图控制器中处理视图的时候：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TableViewController&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;UIViewController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;tableView&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;UITableView&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;override&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;viewDidLoad&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kc"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;viewDidLoad&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;tableView&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;UITableView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addSubview&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tableView&lt;/span&gt;&lt;span class="p"&gt;!)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;viewModelDidUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;_&lt;/span&gt; &lt;span class="n"&gt;viewModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ViewModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;tableView&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;reloadData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这也是对于很多 Swift 程序员争论比较激烈的地方，程度不亚于讨论 tabs 和 spaces 的用法。有的人会说：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;既然它是一个可选值，你就应该时刻使用 &lt;code&gt;if let&lt;/code&gt; 或者 &lt;code&gt;guard let&lt;/code&gt; 的方式进行解包。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;然而另外一些人则采用完全相反，说：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;既然你知道这个变量在使用的时候不会为 &lt;code&gt;nil&lt;/code&gt;，使用 &lt;code&gt;!&lt;/code&gt; 强制解包多好。崩溃也要比让你的程序处于一个未知状态要好吧。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;本质上来讲，我们这里讨论的是要不要采用防御性编程（&lt;a href="https://en.wikipedia.org/wiki/Defensive_programming"&gt;defensive programming&lt;/a&gt;）的问题。我们是试图让程序从一个未知状态恢复还是简单的放弃，然后让它崩溃掉？&lt;/p&gt;
&lt;p&gt;如果非得让我对这个问题给出一个答案的话，我更倾向于后者。未知状态真的很难追踪 bug，会导致执行很多不想执行的逻辑，采用防御性编程就会使得你的代码很难追踪，出现问题很难追踪。&lt;/p&gt;
&lt;p&gt;但是，我不太喜欢给出一个二选一的答案。相反，我们可以寻找一些技术手法，用更精妙的方式的解决上面提到的问题。&lt;/p&gt;
&lt;h2 id="它真的可选的吗"&gt;它真的可选的吗？&lt;/h2&gt;
&lt;p&gt;那些可选类型的，但是被代码逻辑真实需要的变量和属性，实际上是架构瑕疵的一个体现。如果在某些地方确实需要它，但是它又不在，就会使得你的代码逻辑处于未知状态，那么它就不应该是可选类型的。&lt;/p&gt;
&lt;p&gt;当然，在某些特定场景下，可选值确实很难避免（尤其是和特定的系统 API 交互的时候），那对于大部分这种情况，我们有一些技术来处理从而避免可选值。&lt;/p&gt;
&lt;h3 id="lazy-要比非可选的可选值更好"&gt;lazy 要比非可选的可选值更好&lt;/h3&gt;
&lt;p&gt;某些属性的值需要在其父类创建之后再生成（比如视图控制器中的那些视图，应该在 &lt;code&gt;loadView()&lt;/code&gt;或者 &lt;code&gt;viewDidLoad()&lt;/code&gt;方法中被创建），对于这种属性要避免其可选类型的方法就是使用 &lt;code&gt;lazy&lt;/code&gt; 属性。一个&lt;code&gt;lazy&lt;/code&gt;属性是可以是非可选类型的，同时也不在其父类的初始化方法里被需要，它会在其第一次被获取的时候创建出来。&lt;/p&gt;
&lt;p&gt;让我们改一下上面的代码，使用 lazy 来改造 tableView 属性：&lt;/p&gt;</description></item><item><title>理解响应者和响应链</title><link>https://hechen.github.io/zh-cn/post/understanding-responders-and-the-responder-chain/</link><pubDate>Mon, 13 Nov 2017 21:22:44 +0000</pubDate><guid>https://hechen.github.io/zh-cn/post/understanding-responders-and-the-responder-chain/</guid><description>&lt;p&gt;Apps 是通过响应者（responder）对象来接收和处理事件的。一个响应者对象是 UIResponder 类的一个实例，我们常见的 UIView，UIViewController 以及 UIApplication 都是 UIResponder 的子类。 UIKit 自动帮你管理着这些 responder 相关的行为，包括事件是如何从一个 responder 传递给另一个 responder 的等等。当然，你也可以修改你的 app 中事件传递的默认行为。&lt;/p&gt;
&lt;p&gt;UIKit 会把大部分的事件都传递给最适合的 responder 对象来处理。如果该 responder 无法处理该事件，UIKit 就会继续把该事件沿着当前的响应者链传递到下一个 responder。响应者链就是你的 App 中所有响应者的动态配置，也因为其是动态的，你的 App 中不可能只存在单一的响应者链。由于事件总是从特定的响应者那里流转到更通用的响应者那里，因此很容易确定某响应者链中下一个响应者是谁。举个例子，一个 view 的下一个响应者是其 superview 或者负责管理它的 view controller。事件就是这样在响应者链中传递直到其被处理掉。&lt;/p&gt;
&lt;p&gt;下图展示了一个界面中包含了 一个 Label，一个 TextField，一个 Button 以及两个容器 view 的 App 中响应链是什么样子的。如果 TextField 不处理某个事件，UIKit 就会把该事件发送给 TextField 的父级 UIView 对象，同样的，如果该对象依然处理不了，就会传递给该 view 对象的 window。如果 window 对象也依然无法处理该事件，UIKit 最终会把该事件传递给 UIApplication 对象，一般上该 UIApplication 对象是 App 的 delegate 对象并且是 UIResponder 实例，当然这个时候已经脱离了响应者链了。&lt;/p&gt;</description></item><item><title>Capturing objects in Swift closures</title><link>https://hechen.github.io/zh-cn/post/capturing-objects-in-swift-closures/</link><pubDate>Sun, 12 Nov 2017 20:56:50 +0000</pubDate><guid>https://hechen.github.io/zh-cn/post/capturing-objects-in-swift-closures/</guid><description>&lt;blockquote&gt;
&lt;p&gt;原文：&lt;a href="https://www.swiftbysundell.com/posts/capturing-objects-in-swift-closures"&gt;Capturing objects in Swift closures&lt;/a&gt;
原作者 &lt;a href="https://twitter.com/johnsundell"&gt;@johnsundell&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;自从 Block 在 iOS4 被引入 Objective-C 的世界之后就成为了 Apple 各平台上最时髦的 API 的重要组成部分了。当 Swift 语言出现的时候，blocks 的概念就摇身一变通过 closure 的形式引入，成为了目前我们可能每一天都在用的语言特性之一了。&lt;/p&gt;
&lt;p&gt;Closure 目前已经被我们广泛的使用了，即使如此，我们在使用它的时候还是需要有很多需要注意的点，并且需要做很多额外的操作。这篇文章，我们来近距离的了解 closure，主要是了解其捕获变量的机制以及那些能够更好的让我们来处理变量捕获的技术。&lt;/p&gt;
&lt;h2 id="伟大的-escape"&gt;伟大的 escape&lt;/h2&gt;
&lt;p&gt;Closure 有两种类型：escaping 和 non-escaping。当一个 closure 是 escaping（使用 &lt;code&gt;@escaping&lt;/code&gt; 修饰闭包参数）的，也就意味着其会被以各种形式存储下来（无论是通过 property 还是被其他 closure 捕获）。相反，Non-Escaping 的　closure 意味着其不能被存储，而且在使用它的地方就必须直接被执行。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;译者注： 可以参见 &lt;a href="http://hechen.info/2017/11/12/autoclosure-escape/"&gt;《@autoclosure &amp;amp;&amp;amp; @escape》&lt;/a&gt; 一文。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;一个显而易见的例子就是当你在一个集合类型上使用函数式操作的时候，例如 &lt;code&gt;forEach&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[1, 2, 3].forEach { number in
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如上代码所示，closure 都直接作用于集合的每一个元素上，也就无需把该闭包变为 escaping 的。&lt;/p&gt;
&lt;p&gt;而 escaping 的 closures 最常见的就是在那些异步 API 中，例如 &lt;code&gt;DispatchQueue&lt;/code&gt;。例如，当你异步执行某 closure 的时候，这个 closure 就会 escape。&lt;/p&gt;</description></item><item><title>@autoclosure &amp;&amp; @escape</title><link>https://hechen.github.io/zh-cn/post/autoclosure-escape/</link><pubDate>Sun, 12 Nov 2017 20:01:54 +0000</pubDate><guid>https://hechen.github.io/zh-cn/post/autoclosure-escape/</guid><description>&lt;p&gt;我们知道在 &lt;code&gt;swift&lt;/code&gt; 中，闭包（closure）是一等公民，因此可以被当作参数传递，在学习 swift 的过程中经常会看到某些关键字修饰该闭包，&lt;code&gt;@autoclosure&lt;/code&gt;， &lt;code&gt;@escape&lt;/code&gt; 就是其中比较常见的两种关键字。&lt;/p&gt;
&lt;h3 id="escape-和-nonescape"&gt;@escape 和 @nonescape&lt;/h3&gt;
&lt;p&gt;当一个闭包被当作参数传递给一个函数，但是当该函数内容执行完毕返回之后，该闭包才会被执行，我们就称该闭包要 escape 某个函数，那 &lt;code&gt;@escape&lt;/code&gt; 关键字就是用来表示该闭包是允许在函数返回之后被调用的。&lt;/p&gt;
&lt;p&gt;我们用 swift 官方文档的例子来看，如下所示&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;completionHandlers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[()&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Void&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;someFunctionWithEscapingClosure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;completionHandler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="n"&gt;escaping&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;completionHandlers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;completionHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;someFunctionWithEscapingClosure(_:)&lt;/code&gt; 的参数是一个闭包，函数内部会把传入的闭包存到之前声明的数组里以便之后进行调用，可以看到，在函数参数的声明部分添加了 &lt;code&gt;@escaping&lt;/code&gt; 关键字，如果这里不添加的话，就会在编译的时候报错：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;error: passing non-escaping parameter &lt;span class="s1"&gt;&amp;#39;completionHandler&amp;#39;&lt;/span&gt; to &lt;span class="k"&gt;function&lt;/span&gt; expecting an @escaping closure
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; completionHandlers.append&lt;span class="o"&gt;(&lt;/span&gt;completionHandler&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;针对标记了 @escaping 关键字含义代表你必须在该闭包内部显式的使用 self 关键字，官方文档中又列举了另外一个例子，如下所示：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;someFunctionWithNonescapingClosure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;closure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;closure&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SomeClass&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;someFunctionWithEscapingClosure&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kc"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;someFunctionWithNonescapingClosure&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;instance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SomeClass&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="bp"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Prints &amp;#34;200&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;completionHandlers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="bp"&gt;first&lt;/span&gt;&lt;span class="p"&gt;?()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="bp"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Prints &amp;#34;100”&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;someFunctionWithEscapingClosure(_:)&lt;/code&gt; 是一个可逃逸的闭包，意味着你需要显示的调用 self 关键字， 而 &lt;code&gt;someFunctionWithNonescapingClosure(_:)&lt;/code&gt; 是非逃逸的闭包，意味着你可以隐式的调用 self。&lt;/p&gt;</description></item><item><title>关于 iOS10 Notification 的那些事儿</title><link>https://hechen.github.io/zh-cn/post/ios-notification/</link><pubDate>Sat, 20 May 2017 10:10:54 +0000</pubDate><guid>https://hechen.github.io/zh-cn/post/ios-notification/</guid><description>&lt;h2 id="概览"&gt;概览&lt;/h2&gt;
&lt;p&gt;推送通知我们大家都不陌生，可以说几乎每个使用智能手机的人每天都会被不同的通知 &lt;em&gt;打扰&lt;/em&gt; 到，正式因为合适的推送是吸引用户注意力的利器，其成为了各 App 吸引用户，将用户带回到 App 本身，提升用户的活跃度的一种必要的方式。&lt;/p&gt;
&lt;p&gt;当然，过度的推送本身则是一件对用户影响特别大的事情，毕竟注意力被打断，因此合适的推送时机也是各个 App 开发者所要注意的，否则他的 App 就会成为用户勿扰名单里的一员了。&lt;/p&gt;
&lt;p&gt;之前刚开始学习 iOS 开发的时候还整理了下当时部署 iOS 远程推送的流程，详见：&lt;a href="http://hechen.info/2015/07/30/iOS-Push-Notification/"&gt;iOS 远端推送部署详解&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;接下来，我们大致回顾一下 iOS 平台关于推送都有哪些历程？&lt;/p&gt;
&lt;h2 id="历史"&gt;历史&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&amp;lt;= iOS 6&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;远程推送通知 （iOS 3）&lt;/li&gt;
&lt;li&gt;本地通知 （iOS 4）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;iOS 7 参考&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;引入 Silent Remote Notifications&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;iOS 8 参考 &lt;a href="https://developer.apple.com/videos/play/wwdc2014/713/"&gt;WWDC 2014 Session 713&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;引入 Actionable Notifications&lt;/li&gt;
&lt;li&gt;修改 Notification 的权限请求&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;iOS 9 参考 &lt;a href="https://developer.apple.com/videos/play/wwdc2015/720/"&gt;WWDC 2015 Session 720&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;引入 Text Input Action&lt;/li&gt;
&lt;li&gt;UIUserNotificationActionBehavior&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;iOS 10 参考 &lt;a href="https://developer.apple.com/videos/play/wwdc2016/707"&gt;WWDC 707 Introduction to Notifications&lt;/a&gt; &amp;amp;&amp;amp; &lt;a href="https://developer.apple.com/videos/play/wwdc2016/708"&gt;WWDC 708 Advanced Notifications&lt;/a&gt;&lt;/p&gt;</description></item><item><title>如何使用UIBezierPath实现一个弹性视图动画</title><link>https://hechen.github.io/zh-cn/post/elastic-view-animation-using-uibezierpath/</link><pubDate>Wed, 02 Dec 2015 18:53:51 +0000</pubDate><guid>https://hechen.github.io/zh-cn/post/elastic-view-animation-using-uibezierpath/</guid><description>&lt;blockquote&gt;
&lt;p&gt;原文：&lt;a href="https://hackernoon.com/elastic-view-animation-or-how-i-built-dgelasticpulltorefresh-269a3ba8636e"&gt;Elastic view animation, or how I built DGElasticPullToRefresh&lt;/a&gt;
原作者 &lt;a href="https://twitter.com/gontovnik"&gt;@gontovnik&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/gontovnik/DGElasticPullToRefresh"&gt;DGElasticPullToRefresh&lt;/a&gt; 展示了如何实现一个弹性效果。效果如下图所示：&lt;/p&gt;
&lt;p&gt;&lt;img alt="DGElasticPullToRefresh.gif" loading="lazy" src="https://i.imgur.com/NzdoFS9.gif"&gt;&lt;/p&gt;
&lt;p&gt;开发环境：
Xcode 7
Swift 2.0&lt;/p&gt;
&lt;p&gt;要求：
开发者至少对 UIBezierPath 和 UIGestureRecognizer 有一定的了解.&lt;/p&gt;
&lt;h3 id="理解业务逻辑"&gt;理解业务逻辑&lt;/h3&gt;
&lt;p&gt;你可能从上面的效果图上可以看到一些端倪，这个动画中我们主要使用到了UIBezierPath来实现上面这种效果。
我们首先创建一个贝塞尔曲线路径的CAShapeLayer，然后当你的手指在屏幕上移动的时候我们将移动所有的控制点来呈现动画。每一个控制点会使用一个可见的UIView来表示。下面有几张图来演示它们是如何工作的，我将所有的控制点标识成了红色：&lt;/p&gt;
&lt;p&gt;&lt;img alt="1_vAmk8ulHh4OJji_X35zW4g" loading="lazy" src="media/1_vAmk8ulHh4OJji_X35zW4g.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="1_ru8KwJ6DZWXgJm1tm_Sw_Q" loading="lazy" src="media/1_ru8KwJ6DZWXgJm1tm_Sw_Q.png"&gt;&lt;/p&gt;
&lt;p&gt;第二章图片中将每个表示控制点的 View 的变量名称标记出来了，如L3，L2等。&lt;/p&gt;
&lt;p&gt;当你的手指释放的时候，我们就播放Spring动画，让所有的控制点向其初始位置以一定的回弹效果移动。当所有的视图在动画播放过程中，我们需要时刻计算并我们的贝塞尔曲线（每一帧都进行计算）。因此，我们准备使用CADisplayLink，CADisplayLink在主循环中运行而且每一帧都会去执行指定的方法。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A CADisplayLink object is a timer object that allows your application to synchronize its drawing to the refresh rate of the display. - Apple doc&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;让我们开始Code吧~&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="build"&gt;Build&lt;/h3&gt;
&lt;p&gt;创建一个单视图控制器，然后将下面这些代码粘贴到viewController.swift文件的类声明中。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;// MARK: -
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;// MARK: Vars
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;private let minimalHeight: CGFloat = 50.0
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;private let shapeLayer = CAShapeLayer()
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;// MARK: -
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;override func loadView() {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; super.loadView()
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; shapeLayer.frame = CGRect(x: 0.0, y: 0.0, width: view.bounds.width, height: minimalHeight)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; shapeLayer.backgroundColor = UIColor(red: 57/255.0, green: 67/255.0, blue: 89/255.0, alpha: 1.0).CGColor
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; view.layer.addSublayer(shapeLayer)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; view.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: &amp;#34;panGestureDidMove:&amp;#34;))
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;// MARK: -
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;// MARK: Methods
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;func panGestureDidMove(gesture: UIPanGestureRecognizer) {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; if gesture.state == .Ended || gesture.state == .Failed || gesture.state == .Cancelled {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; } else {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; shapeLayer.frame.size.height = minimalHeight + max(gesture.translationInView(view).y, 0)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;override func preferredStatusBarStyle() -&amp;gt; UIStatusBarStyle {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; return .LightContent
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;以上代码中，我们做的工作主要有以下几点：&lt;/p&gt;</description></item></channel></rss>