<?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>Singleton on Chen</title><link>https://hechen.github.io/zh-cn/tags/singleton/</link><description>Recent content in Singleton on Chen</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Sun, 15 Sep 2019 20:56:50 +0000</lastBuildDate><atom:link href="https://hechen.github.io/zh-cn/tags/singleton/index.xml" rel="self" type="application/rss+xml"/><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>在 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>如何制作 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></channel></rss>