<?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>Carthage on Chen</title><link>https://hechen.github.io/zh-cn/tags/carthage/</link><description>Recent content in Carthage on Chen</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Tue, 17 Jul 2018 17:09:57 +0000</lastBuildDate><atom:link href="https://hechen.github.io/zh-cn/tags/carthage/index.xml" rel="self" type="application/rss+xml"/><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>