<?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>Cocoapods on Chen</title><link>https://hechen.github.io/zh-cn/tags/cocoapods/</link><description>Recent content in Cocoapods 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/cocoapods/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>三个简单步骤让你测试使用系统单例的代码</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>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></channel></rss>