Jekyll2020-06-26T21:08:26+00:00https://hisoka0917.github.io/atom.xmlHisoka BlogI'm an iOS developer. 5+ years Objective-C experience. Using Swift now. Know React and Hybrid Develop. Want to learn more.Hisoka利用Swift的反射将Struct转换成Dictionary2018-10-19T00:00:00+00:002018-10-19T00:00:00+00:00https://hisoka0917.github.io/swift/2018/10/19/convert-struct-to-dictionary<p>最近在做接口参数校验的时候碰到了一个问题。在参数格式转换的时候某些中文会变成编码的字符串。下面就来详细描述一下这个问题。</p>
<p>我们的接口参数校验需要将参数升序排列后拼接起来。在项目中接口的参数是以 Json 的格式传递的,所以这里我用了 <code class="language-plaintext highlighter-rouge">Swift 4</code> 中的 <code class="language-plaintext highlighter-rouge">JsonEncoder</code> 来做 Json 序列化,这样就可以直接将一个 <code class="language-plaintext highlighter-rouge">struct</code> 转成 <code class="language-plaintext highlighter-rouge">Json</code> 。那么这里我们需要将参数排序,所以我们需要将参数转成 <code class="language-plaintext highlighter-rouge">Dictionary</code> 类型,因为 <code class="language-plaintext highlighter-rouge">Dictionary</code> 有排序方法。于是最开始的时候我使用了系统提供的简单的转换函数,如下所示:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 定义数据类型</span>
<span class="kd">struct</span> <span class="kt">ContactSimpleModel</span><span class="p">:</span> <span class="kt">Codable</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">relation</span><span class="p">:</span> <span class="kt">String</span>
<span class="k">var</span> <span class="nv">name</span><span class="p">:</span> <span class="kt">String</span>
<span class="p">}</span>
<span class="c1">// 扩展 Encodable 协议</span>
<span class="kd">extension</span> <span class="kt">Encodable</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">dictionary</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">Any</span><span class="p">]?</span> <span class="p">{</span>
<span class="k">if</span> <span class="k">let</span> <span class="nv">data</span> <span class="o">=</span> <span class="k">try</span><span class="p">?</span> <span class="kt">JSONEncoder</span><span class="p">()</span><span class="o">.</span><span class="nf">encode</span><span class="p">(</span><span class="k">self</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="k">let</span> <span class="nv">dict</span> <span class="o">=</span> <span class="k">try</span><span class="p">?</span> <span class="kt">JSONSerialization</span><span class="o">.</span><span class="nf">jsonObject</span><span class="p">(</span><span class="nv">with</span><span class="p">:</span> <span class="n">data</span><span class="p">)</span> <span class="k">as?</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">Any</span><span class="p">]</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">dict</span>
<span class="p">}</span>
<span class="k">return</span> <span class="kc">nil</span>
<span class="p">}</span>
<span class="k">return</span> <span class="kc">nil</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>来试着运行一下:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">contact1</span> <span class="o">=</span> <span class="kt">ContactSimpleModel</span><span class="p">(</span><span class="nv">relation</span><span class="p">:</span> <span class="s">"朋友"</span><span class="p">,</span> <span class="nv">name</span><span class="p">:</span> <span class="s">"宙斯"</span><span class="p">)</span>
<span class="k">if</span> <span class="k">let</span> <span class="nv">dict</span> <span class="o">=</span> <span class="n">contact1</span><span class="o">.</span><span class="n">dictionary</span> <span class="p">{</span>
<span class="nf">print</span><span class="p">(</span><span class="n">dict</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">// 打印: ["relation": 朋友, "name": 宙斯]</span>
</code></pre></div></div>
<p>OK,到目前为止都没有问题。但是当遇到复杂一点的参数时,问题就出来了。</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">PersonModel</span><span class="p">:</span> <span class="kt">Codable</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">job</span><span class="p">:</span> <span class="kt">String</span><span class="p">?</span>
<span class="k">var</span> <span class="nv">contacts</span><span class="p">:</span> <span class="p">[</span><span class="kt">ContactSimpleModel</span><span class="p">]</span>
<span class="k">var</span> <span class="nv">manager</span><span class="p">:</span> <span class="kt">ManagerSimpleModel</span><span class="p">?</span>
<span class="p">}</span>
<span class="kd">struct</span> <span class="kt">ContactSimpleModel</span><span class="p">:</span> <span class="kt">Codable</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">relation</span><span class="p">:</span> <span class="kt">String</span>
<span class="k">var</span> <span class="nv">name</span><span class="p">:</span> <span class="kt">String</span>
<span class="p">}</span>
<span class="kd">struct</span> <span class="kt">ManagerSimpleModel</span><span class="p">:</span> <span class="kt">Codable</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">name</span><span class="p">:</span> <span class="kt">String</span>
<span class="k">var</span> <span class="nv">age</span><span class="p">:</span> <span class="kt">Int</span>
<span class="p">}</span>
</code></pre></div></div>
<p>这里我们定义了一个嵌套的数据结构。然后我们试着像刚才那样来处理。</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">contact1</span> <span class="o">=</span> <span class="kt">ContactSimpleModel</span><span class="p">(</span><span class="nv">relation</span><span class="p">:</span> <span class="s">"朋友"</span><span class="p">,</span> <span class="nv">name</span><span class="p">:</span> <span class="s">"宙斯"</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">contact2</span> <span class="o">=</span> <span class="kt">ContactSimpleModel</span><span class="p">(</span><span class="nv">relation</span><span class="p">:</span> <span class="s">"同学"</span><span class="p">,</span> <span class="nv">name</span><span class="p">:</span> <span class="s">"奥丁"</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">manager</span> <span class="o">=</span> <span class="kt">ManagerSimpleModel</span><span class="p">(</span><span class="nv">name</span><span class="p">:</span> <span class="s">"拉斐尔"</span><span class="p">,</span> <span class="nv">age</span><span class="p">:</span> <span class="mi">31</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">job</span> <span class="o">=</span> <span class="s">"火枪手"</span>
<span class="k">let</span> <span class="nv">person</span> <span class="o">=</span> <span class="kt">PersonModel</span><span class="p">(</span><span class="nv">job</span><span class="p">:</span> <span class="n">job</span><span class="p">,</span> <span class="nv">contacts</span><span class="p">:</span> <span class="p">[</span><span class="n">contact1</span><span class="p">,</span> <span class="n">contact2</span><span class="p">],</span> <span class="nv">manager</span><span class="p">:</span> <span class="n">manager</span><span class="p">)</span>
<span class="k">if</span> <span class="k">let</span> <span class="nv">dict</span> <span class="o">=</span> <span class="n">person</span><span class="o">.</span><span class="n">dictionary</span> <span class="p">{</span>
<span class="nf">print</span><span class="p">(</span><span class="n">dict</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>打印出来的结果为:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>["contacts": <__NSArrayI 0x600002471980>(
{
name = "\U5b99\U65af";
relation = "\U670b\U53cb";
},
{
name = "\U5965\U4e01";
relation = "\U540c\U5b66";
}
)
, "manager": {
age = 31;
name = "\U62c9\U6590\U5c14";
}, "job": 火枪手]
</code></pre></div></div>
<p>问题出现了,中文被转成了utf-8编码的字符串。这个问题是在 <code class="language-plaintext highlighter-rouge">JSONSerialization.jsonObject()</code> 这一步,这个方法会将嵌套结构中的中文字符转成编码的字符串,第一层 <code class="language-plaintext highlighter-rouge">key-value</code> 中的中文值不会受影响。那么这样一来我如果拿这个字典去做排序拼接就会和后端计算的值会不一样。于是就要想办法来手动解析,以保证字符的编码不变。</p>
<p>于是我准备利用 <code class="language-plaintext highlighter-rouge">Swift</code> 的反射机制来做这件事情。反射的机制在这篇文章里就不介绍了,感兴趣的同学可以搜索相关文章。这里我介绍一下具体怎么实现。</p>
<p>我们来看一段代码:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">allProperties</span><span class="p">(</span><span class="nv">model</span><span class="p">:</span> <span class="kt">Codable</span><span class="p">)</span> <span class="k">throws</span> <span class="o">-></span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">Any</span><span class="p">]</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">result</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">Any</span><span class="p">]</span> <span class="o">=</span> <span class="p">[:]</span>
<span class="k">let</span> <span class="nv">mirror</span> <span class="o">=</span> <span class="kt">Mirror</span><span class="p">(</span><span class="nv">reflecting</span><span class="p">:</span> <span class="n">model</span><span class="p">)</span>
<span class="k">guard</span> <span class="k">let</span> <span class="nv">style</span> <span class="o">=</span> <span class="n">mirror</span><span class="o">.</span><span class="n">displayStyle</span><span class="p">,</span> <span class="n">style</span> <span class="o">==</span> <span class="kt">Mirror</span><span class="o">.</span><span class="kt">DisplayStyle</span><span class="o">.</span><span class="kd">struct</span> <span class="o">||</span> <span class="n">style</span> <span class="o">==</span> <span class="kt">Mirror</span><span class="o">.</span><span class="kt">DisplayStyle</span><span class="o">.</span><span class="kd">class</span> <span class="k">else</span> <span class="p">{</span>
<span class="c1">//throw some error</span>
<span class="k">throw</span> <span class="kt">NSError</span><span class="p">(</span><span class="nv">domain</span><span class="p">:</span> <span class="s">"hris.to"</span><span class="p">,</span> <span class="nv">code</span><span class="p">:</span> <span class="mi">777</span><span class="p">,</span> <span class="nv">userInfo</span><span class="p">:</span> <span class="kc">nil</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">for</span> <span class="p">(</span><span class="n">labelMaybe</span><span class="p">,</span> <span class="n">valueMaybe</span><span class="p">)</span> <span class="k">in</span> <span class="n">mirror</span><span class="o">.</span><span class="n">children</span> <span class="p">{</span>
<span class="k">guard</span> <span class="k">let</span> <span class="nv">label</span> <span class="o">=</span> <span class="n">labelMaybe</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">continue</span>
<span class="p">}</span>
<span class="n">result</span><span class="p">[</span><span class="n">label</span><span class="p">]</span> <span class="o">=</span> <span class="n">valueMaybe</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">result</span>
<span class="p">}</span>
</code></pre></div></div>
<p>首先这段代码中新建了一个 <code class="language-plaintext highlighter-rouge">Mirror</code> 类。这个 <code class="language-plaintext highlighter-rouge">Mirror</code> 类可以将一个对象反射到一个结构中。它有一个 <code class="language-plaintext highlighter-rouge">displayStyle</code> 属性,其定义如下:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">enum</span> <span class="kt">DisplayStyle</span> <span class="p">{</span>
<span class="k">case</span> <span class="p">`</span><span class="nv">struct</span><span class="p">`</span>
<span class="k">case</span> <span class="p">`</span><span class="nv">class</span><span class="p">`</span>
<span class="k">case</span> <span class="p">`</span><span class="nv">enum</span><span class="p">`</span>
<span class="k">case</span> <span class="n">tuple</span>
<span class="k">case</span> <span class="kd">optional</span>
<span class="k">case</span> <span class="n">collection</span>
<span class="k">case</span> <span class="n">dictionary</span>
<span class="k">case</span> <span class="k">set</span>
<span class="p">}</span>
</code></pre></div></div>
<p>基本上涵盖了所有对象类型。这个属性可以用来判断当前对象是什么类型。</p>
<p>然后还有一个 <code class="language-plaintext highlighter-rouge">children</code> 属性,这个属性是 <code class="language-plaintext highlighter-rouge">(label: String?, value: Any)</code> 类型的一个集合。</p>
<p>这个函数做的事情就是将一个对象的所有属性转成一个 <code class="language-plaintext highlighter-rouge">Dictionary</code> 对象返回。以 <code class="language-plaintext highlighter-rouge">label</code> 为键,<code class="language-plaintext highlighter-rouge">value</code> 作为值。那么我们来看一下之前定义的 <code class="language-plaintext highlighter-rouge">Person</code> 对象转成字典后的结果。</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="k">let</span> <span class="nv">dict</span> <span class="o">=</span> <span class="k">try</span><span class="p">?</span> <span class="nf">allProperties</span><span class="p">(</span><span class="nv">model</span><span class="p">:</span> <span class="n">person</span><span class="p">)</span> <span class="p">{</span>
<span class="nf">print</span><span class="p">(</span><span class="n">dict</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">// 打印结果:["job": Optional("火枪手"), "manager": Optional(__lldb_expr_1.ManagerSimpleModel(name: "拉斐尔", age: 31)), "contacts": [__lldb_expr_1.ContactSimpleModel(relation: "朋友", name: "宙斯"), __lldb_expr_1.ContactSimpleModel(relation: "同学", name: "奥丁")]] </span>
</code></pre></div></div>
<p>从这个打印的结果中我们可以看到,值的类型很清楚的打印了出来。到这里为止,这个字典还是不能用的,因为这个里面的值有太多东西了,有类型名称,还有 <code class="language-plaintext highlighter-rouge">Optional</code>。所以要彻底的把嵌套的子结构也解析出来。于是我们用递归的方法来修改一下这个方法。</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">allProperties</span><span class="p">(</span><span class="nv">model</span><span class="p">:</span> <span class="kt">Codable</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Any</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">result</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">Any</span><span class="p">]</span> <span class="o">=</span> <span class="p">[:]</span>
<span class="k">let</span> <span class="nv">mirror</span> <span class="o">=</span> <span class="kt">Mirror</span><span class="p">(</span><span class="nv">reflecting</span><span class="p">:</span> <span class="n">model</span><span class="p">)</span>
<span class="k">guard</span> <span class="k">let</span> <span class="nv">style</span> <span class="o">=</span> <span class="n">mirror</span><span class="o">.</span><span class="n">displayStyle</span><span class="p">,</span> <span class="n">style</span> <span class="o">==</span> <span class="kt">Mirror</span><span class="o">.</span><span class="kt">DisplayStyle</span><span class="o">.</span><span class="kd">struct</span> <span class="o">||</span> <span class="n">style</span> <span class="o">==</span> <span class="kt">Mirror</span><span class="o">.</span><span class="kt">DisplayStyle</span><span class="o">.</span><span class="kd">class</span> <span class="k">else</span> <span class="p">{</span>
<span class="c1">//throw some error</span>
<span class="k">return</span> <span class="n">mirror</span>
<span class="p">}</span>
<span class="k">for</span> <span class="p">(</span><span class="n">labelMaybe</span><span class="p">,</span> <span class="n">valueMaybe</span><span class="p">)</span> <span class="k">in</span> <span class="n">mirror</span><span class="o">.</span><span class="n">children</span> <span class="p">{</span>
<span class="k">guard</span> <span class="k">let</span> <span class="nv">label</span> <span class="o">=</span> <span class="n">labelMaybe</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">continue</span>
<span class="p">}</span>
<span class="n">result</span><span class="p">[</span><span class="n">label</span><span class="p">]</span> <span class="o">=</span> <span class="nf">allProperties</span><span class="p">(</span><span class="nv">model</span><span class="p">:</span> <span class="n">valueMaybe</span> <span class="k">as!</span> <span class="kt">Codable</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">result</span>
<span class="p">}</span>
</code></pre></div></div>
<p>然后运行一下,打印结果如下:</p>
<pre><code class="language-plaint">["contacts": Mirror for Array<ContactSimpleModel>, "manager": Mirror for Optional<ManagerSimpleModel>, "job": Mirror for Optional<String>]
</code></pre>
<p>嗯,类型很准确,但是值没了。为什么会这样呢?原因是这个函数中我们只处理了 <code class="language-plaintext highlighter-rouge">struct</code> 和 <code class="language-plaintext highlighter-rouge">class</code> 两种类型。当我们将 <code class="language-plaintext highlighter-rouge">contacts</code> 、<code class="language-plaintext highlighter-rouge">manager</code> 和 <code class="language-plaintext highlighter-rouge">job</code> 3个属性的值递归调用的时候都在 <code class="language-plaintext highlighter-rouge">guard</code> 语句这里直接返回了。</p>
<p>我们仔细地来看这3个属性的类型。第一个 <code class="language-plaintext highlighter-rouge">contacts</code> 它是一个数组,所以它的 <code class="language-plaintext highlighter-rouge">displayStyle</code> 应该是 <code class="language-plaintext highlighter-rouge">collection</code> 。第二个 <code class="language-plaintext highlighter-rouge">manager</code> 它应该是个 <code class="language-plaintext highlighter-rouge">struct</code> 啊,那它应该可以走到下面的代码并且解析出来。实际上不是,它的类型是 <code class="language-plaintext highlighter-rouge">ManagerSimpleModel?</code>,所以它的 <code class="language-plaintext highlighter-rouge">displayStyle</code> 实际上是 <code class="language-plaintext highlighter-rouge">optional</code>。同理,第三个 <code class="language-plaintext highlighter-rouge">job</code> 属性它的类型是 <code class="language-plaintext highlighter-rouge">Optional<String></code>,所以它也是 <code class="language-plaintext highlighter-rouge">optional</code> 类型。所以说这3个属性都被提前返回了,打印出来的结果显示它们是一个 <code class="language-plaintext highlighter-rouge">Mirror</code> 对象。</p>
<p>于是接下来我们就要来解决其它类型时的处理。这里我们先来分析几种情况。</p>
<p>第一,<code class="language-plaintext highlighter-rouge">String</code>,<code class="language-plaintext highlighter-rouge">Int</code> 这类基础类型怎么办?从上面的 <code class="language-plaintext highlighter-rouge">DisplayStyle</code> 枚举中我们可以看到,基础类型没有在枚举里面。所以基础类型的 <code class="language-plaintext highlighter-rouge">displayStyle</code> 是 <code class="language-plaintext highlighter-rouge">nil</code>。</p>
<p>第二,数组我们怎么处理?<code class="language-plaintext highlighter-rouge">DisplayStyle</code> 枚举中有 <code class="language-plaintext highlighter-rouge">collection</code>,所以数组可以用这个类型来处理。要注意的是,数组是没有 <code class="language-plaintext highlighter-rouge">key</code> 的,所以 <code class="language-plaintext highlighter-rouge">collection</code> 类型的对象的每一个子元素是没有 <code class="language-plaintext highlighter-rouge">label</code> 的。我们只需将 <code class="language-plaintext highlighter-rouge">value</code> 取出来递归处理就行了。</p>
<p>第三,<code class="language-plaintext highlighter-rouge">optional</code>要怎么处理?这里我们就需要将值先解包后再处理。于是我们先实现解包的方法。</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="n">unwrap</span><span class="o"><</span><span class="kt">T</span><span class="o">></span><span class="p">(</span><span class="n">_</span> <span class="nv">any</span><span class="p">:</span> <span class="kt">T</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Any</span>
<span class="p">{</span>
<span class="k">let</span> <span class="nv">mirror</span> <span class="o">=</span> <span class="kt">Mirror</span><span class="p">(</span><span class="nv">reflecting</span><span class="p">:</span> <span class="n">any</span><span class="p">)</span>
<span class="k">guard</span> <span class="n">mirror</span><span class="o">.</span><span class="n">displayStyle</span> <span class="o">==</span> <span class="o">.</span><span class="kd">optional</span><span class="p">,</span> <span class="k">let</span> <span class="nv">first</span> <span class="o">=</span> <span class="n">mirror</span><span class="o">.</span><span class="n">children</span><span class="o">.</span><span class="n">first</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">any</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">first</span><span class="o">.</span><span class="n">value</span>
<span class="p">}</span>
</code></pre></div></div>
<p>好,<code class="language-plaintext highlighter-rouge">optional</code> 的问题解决了,接下来我们要修改之前的函数来解决上述的那些问题。</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="n">convert2Dict</span><span class="o"><</span><span class="kt">T</span><span class="o">></span><span class="p">(</span><span class="n">_</span> <span class="nv">any</span><span class="p">:</span> <span class="kt">T</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Any</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">mirror</span> <span class="o">=</span> <span class="kt">Mirror</span><span class="p">(</span><span class="nv">reflecting</span><span class="p">:</span> <span class="n">any</span><span class="p">)</span>
<span class="k">if</span> <span class="k">let</span> <span class="nv">style</span> <span class="o">=</span> <span class="n">mirror</span><span class="o">.</span><span class="n">displayStyle</span> <span class="p">{</span>
<span class="k">if</span> <span class="n">style</span> <span class="o">==</span> <span class="o">.</span><span class="n">collection</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">array</span><span class="p">:</span> <span class="p">[</span><span class="kt">Any</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="p">(</span><span class="n">_</span><span class="p">,</span> <span class="n">valueMaybe</span><span class="p">)</span> <span class="k">in</span> <span class="n">mirror</span><span class="o">.</span><span class="n">children</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">value</span> <span class="o">=</span> <span class="nf">unwrap</span><span class="p">(</span><span class="n">valueMaybe</span><span class="p">)</span>
<span class="n">array</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="nf">convert2Dict</span><span class="p">(</span><span class="n">value</span><span class="p">))</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">array</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">dict</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">Any</span><span class="p">]</span> <span class="o">=</span> <span class="p">[:]</span>
<span class="k">for</span> <span class="p">(</span><span class="n">labelMaybe</span><span class="p">,</span> <span class="n">valueMaybe</span><span class="p">)</span> <span class="k">in</span> <span class="n">mirror</span><span class="o">.</span><span class="n">children</span> <span class="p">{</span>
<span class="k">guard</span> <span class="k">let</span> <span class="nv">label</span> <span class="o">=</span> <span class="n">labelMaybe</span> <span class="k">else</span> <span class="p">{</span> <span class="k">continue</span> <span class="p">}</span>
<span class="k">let</span> <span class="nv">value</span> <span class="o">=</span> <span class="nf">unwrap</span><span class="p">(</span><span class="n">valueMaybe</span><span class="p">)</span>
<span class="n">dict</span><span class="p">[</span><span class="n">label</span><span class="p">]</span> <span class="o">=</span> <span class="nf">convert2Dict</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">dict</span>
<span class="p">}</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">any</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>修改完之后的方法处理了之前提到的几个问题。来看一下运行的结果。</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="k">let</span> <span class="nv">dict</span> <span class="o">=</span> <span class="nf">convert2Dict</span><span class="p">(</span><span class="n">person</span><span class="p">)</span> <span class="k">as?</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">Any</span><span class="p">]</span> <span class="p">{</span>
<span class="nf">print</span><span class="p">(</span><span class="n">dict</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">// 打印结果</span>
<span class="c1">// ["contacts": [["relation": "朋友", "name": "宙斯"], ["relation": "同学", "name": "奥丁"]], "job": "火枪手", "manager": ["name": "拉斐尔", "age": 31]]</span>
</code></pre></div></div>
<p>打印的结果完全符合我们的预期。那么到此为止,我们利用反射来将 <code class="language-plaintext highlighter-rouge">struct</code> 转成 <code class="language-plaintext highlighter-rouge">Dictionary</code> 的功能已经完全实现了。<a href="https://gist.github.com/hisoka0917/efcfbe006db929abecbe37376cf9e56b">Gist地址可以点击这里查看</a>。</p>
<p>再说点额外的。</p>
<p>接下来就只是我这个项目中的工程问题了。因为我要把字典按 <code class="language-plaintext highlighter-rouge">key</code> 排序。我们试着来给转换以后的字典排序。</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="k">let</span> <span class="nv">dict</span> <span class="o">=</span> <span class="nf">convert2Dict</span><span class="p">(</span><span class="n">person</span><span class="p">)</span> <span class="k">as?</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">Any</span><span class="p">]</span> <span class="p">{</span>
<span class="nf">print</span><span class="p">(</span><span class="n">dict</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">sortedQuery</span> <span class="o">=</span> <span class="n">dict</span><span class="o">.</span><span class="nf">sorted</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="p">{</span><span class="nv">$0</span><span class="o">.</span><span class="mi">0</span> <span class="o"><</span> <span class="nv">$1</span><span class="o">.</span><span class="mi">0</span><span class="p">})</span>
<span class="nf">print</span><span class="p">(</span><span class="n">sortedQuery</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">// 打印结果</span>
<span class="c1">// ["contacts[[\"relation\": \"朋友\", \"name\": \"宙斯\"], [\"relation\": \"同学\", \"name\": \"奥丁\"]]", "job火枪手", "manager[\"age\": 31, \"name\": \"拉斐尔\"]"]</span>
</code></pre></div></div>
<p>这个结果并不是很好,因为数组里面的子结构体对象并没有按升序排列。所以还要继续处理里面嵌套结构的排序。当然这里处理方法并不唯一。由于我的项目需要,我的要求是把参数排序、拼接、删除多余字符集。于是在这个项目中我采用了比较 hack 的做法。就是排序、拼接、删除多余字符集的操作直接在转换的过程中完成。也就是说之前的转换函数不返回字典,而是直接返回一个已经处理完了的字符串。这么处理在我这个工程中是适用的。</p>
<p>如果同学们在工程中有类似需求,也可以寻找更好的实现方法。</p>Hisoka最近在做接口参数校验的时候碰到了一个问题。在参数格式转换的时候某些中文会变成编码的字符串。下面就来详细描述一下这个问题。全局自定义Navigation Bar的返回按钮2018-07-26T00:00:00+00:002018-07-26T00:00:00+00:00https://hisoka0917.github.io/swift/2018/07/26/customize-navigationbar-back-button<p>在开发中更改导航栏返回按钮样式是非常常见的设计。当然我们不希望在每个ViewController里设置返回按钮。有一种解决方案是写一个ViewController基类,在基类里面设置返回按钮,然后项目中所有的ViewController都从这个基类中继承。这样做并不优雅。我们希望全局设置返回按钮的图片。</p>
<p>最简单的全局设置返回按钮图片的方法是在<code class="language-plaintext highlighter-rouge">AppDelegate</code>的<code class="language-plaintext highlighter-rouge">didFinishLaunchingWithOptions</code>方法中写入以下代码</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">barApperance</span> <span class="o">=</span> <span class="kt">UINavigationBar</span><span class="o">.</span><span class="nf">appearance</span><span class="p">()</span>
<span class="n">barApperance</span><span class="o">.</span><span class="n">backIndicatorImage</span> <span class="o">=</span> <span class="kt">UIImage</span><span class="p">(</span><span class="nv">named</span><span class="p">:</span> <span class="s">"navigation_back"</span><span class="p">)</span>
<span class="n">barApperance</span><span class="o">.</span><span class="n">backIndicatorTransitionMaskImage</span> <span class="o">=</span> <span class="kt">UIImage</span><span class="p">(</span><span class="nv">named</span><span class="p">:</span> <span class="s">"navigation_back"</span><span class="p">)</span>
<span class="c1">//barApperance.backItem?.title = " " //在这里设置返回按钮文本并没有用</span>
</code></pre></div></div>
<p>当然如果你的UINavigationController是在Storyboard里设置的,也可以去IB里面设置,有对应的属性。</p>
<p>这样就全局替换了返回按钮的图片。但是我们会遇到一个问题就是有时候我们需要获取用户点击返回按钮的事件,这样我们不得不新建一个<code class="language-plaintext highlighter-rouge">UIBarButtonItem</code>设置为<code class="language-plaintext highlighter-rouge">leftBarButtonItem</code>。但是新建的item的图片如果和我们全局设置的图片一样的话那么在页面跳转的时候2个返回按钮的图片会错位。因为新建的<code class="language-plaintext highlighter-rouge">UIBarButtonItem</code>放到NavigationBar左边的时候系统会默认留出8pt的margin。而全局默认的<code class="language-plaintext highlighter-rouge">backButton</code>左边是不留空的。所以2个不同的按钮位置就会错位。于是我们只能切2张图,一张左边有8pt的空白,一张左边不留空。全局的图片使用左边留空的那张,自定义的按钮使用不留空的图片。这样一来水平方向就对齐了。但是垂直方向还是不对的。因为系统默认的<code class="language-plaintext highlighter-rouge">backButton</code>在图片右边有一个<code class="language-plaintext highlighter-rouge">UILabel</code>,于是图片的垂直位置会往下1pt。所以我们左边不留空的那张图在垂直方向上还要往下1pt才能和系统的按钮完全对齐。</p>
<p>我们处理完返回按钮的图片后再来处理返回文本。因为很多设计都不想要返回按钮上的文本。我们可以用以下代码来让返回按钮上的文本不显示</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">barButtonAppearance</span> <span class="o">=</span> <span class="kt">UIBarButtonItem</span><span class="o">.</span><span class="nf">appearance</span><span class="p">()</span>
<span class="n">barButtonAppearance</span><span class="o">.</span><span class="nf">setBackButtonTitlePositionAdjustment</span><span class="p">(</span><span class="kt">UIOffset</span><span class="p">(</span><span class="nv">horizontal</span><span class="p">:</span> <span class="o">-</span><span class="mi">500</span><span class="p">,</span> <span class="nv">vertical</span><span class="p">:</span> <span class="mi">0</span><span class="p">),</span> <span class="nv">for</span><span class="p">:</span> <span class="kt">UIBarMetrics</span><span class="o">.</span><span class="k">default</span><span class="p">)</span>
</code></pre></div></div>
<p>这段代码是让返回按钮的文本往左偏移500pt,这样文本就在屏幕外面了。不过这样做有一点点小的问题,就是在页面跳转动画的时候是看得到title的横向移动的。强迫症表示不是很舒服。</p>
<p>于是我们就想把返回按钮的文本颜色设置成透明</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">barButtonAppearance</span> <span class="o">=</span> <span class="kt">UIBarButtonItem</span><span class="o">.</span><span class="nf">appearance</span><span class="p">()</span>
<span class="n">barButtonAppearance</span><span class="o">.</span><span class="nf">setTitleTextAttributes</span><span class="p">([</span><span class="kt">NSAttributedStringKey</span><span class="o">.</span><span class="nv">foregroundColor</span><span class="p">:</span> <span class="kt">UIColor</span><span class="o">.</span><span class="n">clear</span><span class="p">],</span> <span class="nv">for</span><span class="p">:</span> <span class="o">.</span><span class="n">normal</span><span class="p">)</span>
<span class="n">barButtonAppearance</span><span class="o">.</span><span class="nf">setTitleTextAttributes</span><span class="p">([</span><span class="kt">NSAttributedStringKey</span><span class="o">.</span><span class="nv">foregroundColor</span><span class="p">:</span> <span class="kt">UIColor</span><span class="o">.</span><span class="n">clear</span><span class="p">],</span> <span class="nv">for</span><span class="p">:</span> <span class="o">.</span><span class="n">highlighted</span><span class="p">)</span>
</code></pre></div></div>
<p>这样一来转场动画时title的移动就舒服多了。</p>Hisoka在开发中更改导航栏返回按钮样式是非常常见的设计。当然我们不希望在每个ViewController里设置返回按钮。有一种解决方案是写一个ViewController基类,在基类里面设置返回按钮,然后项目中所有的ViewController都从这个基类中继承。这样做并不优雅。我们希望全局设置返回按钮的图片。你经常忘记写[weak self]吗?这里有一个解决方案2018-07-11T00:00:00+00:002018-07-11T00:00:00+00:00https://hisoka0917.github.io/swift/2018/07/11/preventing-memory-leaks-with-swift-compile-time-safety<blockquote>
<p>感谢原文作者Oleg Dreyman授权翻译本文<br />
原作者:Oleg Dreyman<br />
原文链接:https://medium.com/anysuggestion/preventing-memory-leaks-with-swift-compile-time-safety-49b845df4dc6</p>
</blockquote>
<p><em>这篇文章将讨论基于闭包的委托、循环引用和范型</em></p>
<p>今天我们讨论一下如何让Swift的委托(delegation)变得更优雅。我们先来看一下Swift中标准的Cocoa风格的委托例子。</p>
<p>1.首先我们创建一个仅限于类的委托协议</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">protocol</span> <span class="kt">ImageDownloaderDelegate</span><span class="p">:</span> <span class="kd">class</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">imageDownloader</span><span class="p">(</span><span class="n">_</span> <span class="nv">imageDownloader</span><span class="p">:</span> <span class="kt">ImageDownloader</span><span class="p">,</span> <span class="n">didDownload</span> <span class="nv">image</span><span class="p">:</span> <span class="kt">UIImage</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>2.然后我们来实现<code class="language-plaintext highlighter-rouge">ImageDownloader</code></p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">ImageDownloader</span> <span class="p">{</span>
<span class="k">weak</span> <span class="k">var</span> <span class="nv">delegate</span><span class="p">:</span> <span class="kt">ImageDownloaderDelegate</span><span class="p">?</span>
<span class="kd">func</span> <span class="nf">downloadImage</span><span class="p">(</span><span class="k">for</span> <span class="nv">url</span><span class="p">:</span> <span class="kt">URL</span><span class="p">)</span> <span class="p">{</span>
<span class="nf">download</span><span class="p">(</span><span class="nv">url</span><span class="p">:</span> <span class="n">url</span><span class="p">)</span> <span class="p">{</span> <span class="n">image</span> <span class="k">in</span>
<span class="k">self</span><span class="o">.</span><span class="n">delegate</span><span class="p">?</span><span class="o">.</span><span class="nf">imageDownloader</span><span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="nv">didDownload</span><span class="p">:</span> <span class="n">image</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>注意<code class="language-plaintext highlighter-rouge">delegate</code>被标记为<code class="language-plaintext highlighter-rouge">weak</code>来防止循环引用。假如你在阅读本文,那么你应该知道为什么我们需要这么做。如果你不知道,那么你应该看看<a href="https://medium.com/@natashatherobot">NatashaTheRobot</a>写的这篇文章<a href="https://www.natashatherobot.com/ios-weak-delegates-swift/#">iOS: How To Make Weak Delegates In Swift.</a></p>
<p>3.接下来我们实现一个<code class="language-plaintext highlighter-rouge">ImageDownloader</code>的使用者</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">Controller</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">downloader</span> <span class="o">=</span> <span class="kt">ImageDownloader</span><span class="p">()</span>
<span class="k">var</span> <span class="nv">image</span><span class="p">:</span> <span class="kt">UIImage</span><span class="p">?</span>
<span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
<span class="n">downloader</span><span class="o">.</span><span class="n">delegate</span> <span class="o">=</span> <span class="k">self</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">updateImage</span><span class="p">()</span> <span class="p">{</span>
<span class="n">downloader</span><span class="o">.</span><span class="nf">downloadImage</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="cm">/* some image url */</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">extension</span> <span class="kt">Controller</span><span class="p">:</span> <span class="kt">ImageDownloaderDelegate</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">imageDownloader</span><span class="p">(</span><span class="n">_</span> <span class="nv">imageDownloader</span><span class="p">:</span> <span class="kt">ImageDownloader</span><span class="p">,</span> <span class="n">didDownload</span> <span class="nv">image</span><span class="p">:</span> <span class="kt">UIImage</span><span class="p">)</span> <span class="p">{</span>
<span class="k">self</span><span class="o">.</span><span class="n">image</span> <span class="o">=</span> <span class="n">image</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>好了,我们有了一个干净、熟悉的API,同时我们不必担心内存泄漏因为我们的<code class="language-plaintext highlighter-rouge">delegate</code>是<code class="language-plaintext highlighter-rouge">weak</code>的。说明一下,这个例子非常好,完全没有问题。</p>
<p>那么这篇文章的点在哪儿呢?</p>
<h3 id="现代swift">现代Swift</h3>
<p>如今这种Cocoa风格的委托模式在Swift开发者中越来越不流行了。原因很简单:这种代码看起来不是很<strong>“现代”</strong>,它需要大量的样板并且缺乏灵活性(举个例子:尝试一下给范型编写这样的委托)。</p>
<p>这就是为什么越来越多的开发者选择通过闭包委托(delegation through closure)模式。我们来应用到<code class="language-plaintext highlighter-rouge">ImageDownloader</code>的例子中。</p>
<p>首先,我们删除<code class="language-plaintext highlighter-rouge">ImageDownloaderDelegate</code>协议以及<code class="language-plaintext highlighter-rouge">ImageDownloader</code>中的委托。取而代之的是一个闭包属性。</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">ImageDownloader</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">didDownload</span><span class="p">:</span> <span class="p">((</span><span class="kt">UIImage</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span><span class="p">)?</span>
<span class="kd">func</span> <span class="nf">downloadImage</span><span class="p">(</span><span class="k">for</span> <span class="nv">url</span><span class="p">:</span> <span class="kt">URL</span><span class="p">)</span> <span class="p">{</span>
<span class="nf">download</span><span class="p">(</span><span class="nv">url</span><span class="p">:</span> <span class="n">url</span><span class="p">)</span> <span class="p">{</span> <span class="n">image</span> <span class="k">in</span>
<span class="k">self</span><span class="o">.</span><span class="nf">didDownload</span><span class="p">?(</span><span class="n">image</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>相应的修改我们的<code class="language-plaintext highlighter-rouge">Controller</code></p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">Controller</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">downloader</span> <span class="o">=</span> <span class="kt">ImageDownloader</span><span class="p">()</span>
<span class="k">var</span> <span class="nv">image</span><span class="p">:</span> <span class="kt">UIImage</span><span class="p">?</span>
<span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
<span class="n">downloader</span><span class="o">.</span><span class="n">didDownload</span> <span class="o">=</span> <span class="p">{</span> <span class="n">image</span> <span class="k">in</span>
<span class="k">self</span><span class="o">.</span><span class="n">image</span> <span class="o">=</span> <span class="n">image</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">updateImage</span><span class="p">()</span> <span class="p">{</span>
<span class="n">downloader</span><span class="o">.</span><span class="nf">downloadImage</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="cm">/* some image url */</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>我们的代码现在更加紧凑,并且(可能)更容易阅读和推理。但是有经验的开发者会注意到这里有个问题:<strong>内存泄漏了!</strong></p>
<p>当我们取消<code class="language-plaintext highlighter-rouge">ImageDownloader</code>里的<code class="language-plaintext highlighter-rouge">delegate</code>属性时,我们同时失去了<code class="language-plaintext highlighter-rouge">weak</code>的特性。所以现在<code class="language-plaintext highlighter-rouge">Controller</code>持有一个<code class="language-plaintext highlighter-rouge">ImageDownloader</code>的引用,同时<code class="language-plaintext highlighter-rouge">ImageDownloader</code>的<code class="language-plaintext highlighter-rouge">didDownload</code>闭包里面也持有一个<code class="language-plaintext highlighter-rouge">Controller</code>的引用。这是经典的循环引用的定义并且伴随着内存泄漏。</p>
<p>有经验的开发者会知道一个简单的解决方案:使用<code class="language-plaintext highlighter-rouge">[weak self]</code>!</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">Controller</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">downloader</span> <span class="o">=</span> <span class="kt">ImageDownloader</span><span class="p">()</span>
<span class="k">var</span> <span class="nv">image</span><span class="p">:</span> <span class="kt">UIImage</span><span class="p">?</span>
<span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
<span class="n">downloader</span><span class="o">.</span><span class="n">didDownload</span> <span class="o">=</span> <span class="p">{</span> <span class="p">[</span><span class="k">weak</span> <span class="k">self</span><span class="p">]</span> <span class="n">image</span> <span class="k">in</span>
<span class="k">self</span><span class="p">?</span><span class="o">.</span><span class="n">image</span> <span class="o">=</span> <span class="n">image</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">updateImage</span><span class="p">()</span> <span class="p">{</span>
<span class="n">downloader</span><span class="o">.</span><span class="nf">downloadImage</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="cm">/* some image url */</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>现在代码又可以正常运行了。</p>
<h3 id="但是">但是…</h3>
<p>你可以看到,从API设计的角度来讲,这种新方法实际上使事情变得更糟。之前<code class="language-plaintext highlighter-rouge">ImageDownloader</code>的设计者负责防止产生内存泄漏。现在这是API使用者的独家义务。</p>
<p>而<code class="language-plaintext highlighter-rouge">[weak self]</code>是非常容易被忽视的东西。我确信这个世界上有很多代码(即使在生产环境)会遇到这个简单却无所不在的问题。</p>
<p><a href="https://swift.org/documentation/api-design-guidelines/#fundamentals">Swift API 设计指南</a>指出:</p>
<blockquote>
<p>方法和属性等实体仅声明一次,但可以重复使用。</p>
</blockquote>
<p>这点很重要。我们甚至无法保证在自己的app里每个需要的地方都加上<code class="language-plaintext highlighter-rouge">weak self</code>。我们当然不能指望我们所有的潜在API用户都这样做。作为API设计者,我们需要关注使用时的安全性。</p>
<p>当然,Swift可以让我们使用更好的方法。</p>
<p>我们来看问题的核心点:在分配委托回调时,99%的时候都应当有一个<code class="language-plaintext highlighter-rouge">[weak self]</code>捕获列表。但实际上并没有什么能阻止我们忽略它。没有error,没有warning,什么都没有。我们如何才能强制执行正确的行为呢?</p>
<p>这正是我在讨论的最基本的方式:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">ImageDownloader</span> <span class="p">{</span>
<span class="kd">private</span> <span class="k">var</span> <span class="nv">didDownload</span><span class="p">:</span> <span class="p">((</span><span class="kt">UIImage</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span><span class="p">)?</span>
<span class="kd">func</span> <span class="n">setDidDownload</span><span class="o"><</span><span class="kt">Object</span> <span class="p">:</span> <span class="kt">AnyObject</span><span class="o">></span><span class="p">(</span><span class="nv">delegate</span><span class="p">:</span> <span class="kt">Object</span><span class="p">,</span> <span class="nv">callback</span><span class="p">:</span> <span class="kd">@escaping</span> <span class="p">(</span><span class="kt">Object</span><span class="p">,</span> <span class="kt">UIImage</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span><span class="p">)</span> <span class="p">{</span>
<span class="k">self</span><span class="o">.</span><span class="n">didDownload</span> <span class="o">=</span> <span class="p">{</span> <span class="p">[</span><span class="k">weak</span> <span class="n">delegate</span><span class="p">]</span> <span class="n">image</span> <span class="k">in</span>
<span class="k">if</span> <span class="k">let</span> <span class="nv">delegate</span> <span class="o">=</span> <span class="n">delegate</span> <span class="p">{</span>
<span class="nf">callback</span><span class="p">(</span><span class="n">delegate</span><span class="p">,</span> <span class="n">image</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">downloadImage</span><span class="p">(</span><span class="k">for</span> <span class="nv">url</span><span class="p">:</span> <span class="kt">URL</span><span class="p">)</span> <span class="p">{</span>
<span class="nf">download</span><span class="p">(</span><span class="nv">url</span><span class="p">:</span> <span class="n">url</span><span class="p">)</span> <span class="p">{</span> <span class="n">image</span> <span class="k">in</span>
<span class="k">self</span><span class="o">.</span><span class="nf">didDownload</span><span class="p">?(</span><span class="n">image</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>于是现在我们的<code class="language-plaintext highlighter-rouge">didDownload</code>是私有的了,并且使用者需要调用<code class="language-plaintext highlighter-rouge">setDidDownload(delegate:callback:)</code>,它将传进来的委托对象包装成弱引用,从而强制执行我们想要的行为。以下是<code class="language-plaintext highlighter-rouge">Controller</code>的样子:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">Controller</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">downloader</span> <span class="o">=</span> <span class="kt">ImageDownloader</span><span class="p">()</span>
<span class="k">var</span> <span class="nv">image</span><span class="p">:</span> <span class="kt">UIImage</span><span class="p">?</span>
<span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
<span class="n">downloader</span><span class="o">.</span><span class="nf">setDidDownload</span><span class="p">(</span><span class="nv">delegate</span><span class="p">:</span> <span class="k">self</span><span class="p">)</span> <span class="p">{</span> <span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="n">image</span><span class="p">)</span> <span class="k">in</span>
<span class="k">self</span><span class="o">.</span><span class="n">image</span> <span class="o">=</span> <span class="n">image</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">updateImage</span><span class="p">()</span> <span class="p">{</span>
<span class="n">downloader</span><span class="o">.</span><span class="nf">downloadImage</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="cm">/* some image url */</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>欢呼吧,没有循环引用也没有内存泄漏,非常棒!我们的代码仍然紧凑且易读,而且在内存泄漏方面更安全。 并且没有丑陋的<code class="language-plaintext highlighter-rouge">[weak self]</code>!</p>
<p>注意:这种智能技术实际上也被<code class="language-plaintext highlighter-rouge">Foundation</code>的<a href="https://developer.apple.com/documentation/foundation/undomanager/2427208-registerundo">UndoManager</a>(以及其他一些Cocoa API)使用。</p>
<h3 id="更深入一步">更深入一步</h3>
<p>不过这还是不够理想。我们需要在<code class="language-plaintext highlighter-rouge">ImageDownloader</code>里面写很多额外的、不能重用的代码。利用Swift范型的强大功能,我们可以做得更好:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">DelegatedCall</span><span class="o"><</span><span class="kt">Input</span><span class="o">></span> <span class="p">{</span>
<span class="kd">private(set)</span> <span class="k">var</span> <span class="nv">callback</span><span class="p">:</span> <span class="p">((</span><span class="kt">Input</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span><span class="p">)?</span>
<span class="k">mutating</span> <span class="kd">func</span> <span class="n">delegate</span><span class="o"><</span><span class="kt">Object</span> <span class="p">:</span> <span class="kt">AnyObject</span><span class="o">></span><span class="p">(</span><span class="n">to</span> <span class="nv">object</span><span class="p">:</span> <span class="kt">Object</span><span class="p">,</span> <span class="n">with</span> <span class="nv">callback</span><span class="p">:</span> <span class="kd">@escaping</span> <span class="p">(</span><span class="kt">Object</span><span class="p">,</span> <span class="kt">Input</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span><span class="p">)</span> <span class="p">{</span>
<span class="k">self</span><span class="o">.</span><span class="n">callback</span> <span class="o">=</span> <span class="p">{</span> <span class="p">[</span><span class="k">weak</span> <span class="n">object</span><span class="p">]</span> <span class="n">input</span> <span class="k">in</span>
<span class="k">guard</span> <span class="k">let</span> <span class="nv">object</span> <span class="o">=</span> <span class="n">object</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">return</span>
<span class="p">}</span>
<span class="nf">callback</span><span class="p">(</span><span class="n">object</span><span class="p">,</span> <span class="n">input</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>这样样板就消失了,我们重新回到了一个非常薄的API。</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">ImageDownloader</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">didDownload</span> <span class="o">=</span> <span class="kt">DelegatedCall</span><span class="o"><</span><span class="kt">UIImage</span><span class="o">></span><span class="p">()</span>
<span class="kd">func</span> <span class="nf">downloadImage</span><span class="p">(</span><span class="k">for</span> <span class="nv">url</span><span class="p">:</span> <span class="kt">URL</span><span class="p">)</span> <span class="p">{</span>
<span class="nf">download</span><span class="p">(</span><span class="nv">url</span><span class="p">:</span> <span class="n">url</span><span class="p">)</span> <span class="p">{</span> <span class="n">image</span> <span class="k">in</span>
<span class="k">self</span><span class="o">.</span><span class="n">didDownload</span><span class="o">.</span><span class="nf">callback</span><span class="p">?(</span><span class="n">image</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>而且<code class="language-plaintext highlighter-rouge">Controller</code>的代码看上去更好了(从我的观点看)</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">Controller</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">downloader</span> <span class="o">=</span> <span class="kt">ImageDownloader</span><span class="p">()</span>
<span class="k">var</span> <span class="nv">image</span><span class="p">:</span> <span class="kt">UIImage</span><span class="p">?</span>
<span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
<span class="n">downloader</span><span class="o">.</span><span class="n">didDownload</span><span class="o">.</span><span class="nf">delegate</span><span class="p">(</span><span class="nv">to</span><span class="p">:</span> <span class="k">self</span><span class="p">)</span> <span class="p">{</span> <span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="n">image</span><span class="p">)</span> <span class="k">in</span>
<span class="k">self</span><span class="o">.</span><span class="n">image</span> <span class="o">=</span> <span class="n">image</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">updateImage</span><span class="p">()</span> <span class="p">{</span>
<span class="n">downloader</span><span class="o">.</span><span class="nf">downloadImage</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="cm">/* some image url */</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>现在我们只用了14行代码就帮助我们避免了大量无意的内存泄漏。</p>
<p>这就是我喜欢Swift和她的类型系统的地方。它挑战我用有创意的设计来对抗常见的陷阱。<code class="language-plaintext highlighter-rouge">DelegateCall</code>API说明了一切,它使我们能够编写干净、富有表现力和安全的代码。</p>
<p>这项技术结合了Cocoa风格的委托和基于闭包的委托的最好的部分,同时解决了它们的大多数问题。我一直在使用它。于是我决定将它作为一个包发布。它具有更通用的语法,称为<code class="language-plaintext highlighter-rouge">Delegated</code>。一定要看一看:<a href="https://github.com/dreymonde/Delegated">dreymonde/Delegated</a>。</p>
<p>感谢阅读这篇文章。有任何问题或建议可以写在评论里。</p>Hisoka感谢原文作者Oleg Dreyman授权翻译本文 原作者:Oleg Dreyman 原文链接:https://medium.com/anysuggestion/preventing-memory-leaks-with-swift-compile-time-safety-49b845df4dc6添加现有的repo为Git Subtree2018-04-23T00:00:00+00:002018-04-23T00:00:00+00:00https://hisoka0917.github.io/git/2018/04/23/add-exist-repo-as-subtree<p>前一篇讲述了从现有项目中拆分subtree的步骤,这一篇说一下如何把一个现有的项目当成subtree加到当前项目中来。</p>
<h4 id="1-add-remote">1. Add Remote</h4>
<p>我们先把要添加的subtree repo添加为remote,这样能简化后续的操作命令。</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git remote add sub_origin GIT_REMOTE_REPO
git fetch sub_origin
</code></pre></div></div>
<h4 id="2-add-subtree">2. Add Subtree</h4>
<p>用<code class="language-plaintext highlighter-rouge">git subtree add --prefix=local/subtree/path GIT_REMOTE_REPO branch</code>来添加subtree</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git subtree add <span class="nt">--prefix</span><span class="o">=</span>/path/to/sub sub_origin master <span class="nt">--squash</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">--squash</code>参数表示将subtree repo中的提交记录合并成一次commit,这样就不用拉取子项目的完整历史记录。</p>
<p>也有用<code class="language-plaintext highlighter-rouge">read-tree</code>来把子repo添加进来的,比如</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git merge <span class="nt">-s</span> ours <span class="nt">--no-commit</span> sub_origin/master
git read-tree <span class="nt">--prefix</span><span class="o">=</span>/path/to/sub <span class="nt">-u</span> sub_origin/master
git commit <span class="nt">-m</span> <span class="s2">"Import sub as a subtree"</span>
</code></pre></div></div>
<h4 id="3-更新subtree">3. 更新subtree</h4>
<p>将远程repo的代码拉取下来</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git pull <span class="nt">-s</span> subtree sub_origin master <span class="nt">--allow-unrelated-histories</span> <span class="nt">--squash</span>
</code></pre></div></div>
<p>或者用<code class="language-plaintext highlighter-rouge">git subtree pull</code>来更新</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git subtree pull <span class="nt">--prefix</span><span class="o">=</span>/path/to/sub sub_origin master <span class="nt">--squash</span>
</code></pre></div></div>
<p>这样会有2个commit,一个是squash的commit,一个是merge的commit。
如果不加<code class="language-plaintext highlighter-rouge">--squash</code>参数的话会报错<code class="language-plaintext highlighter-rouge">fatal: refusing to merge unrelated histories</code>。因为目前<code class="language-plaintext highlighter-rouge">git subtree</code>命令还不支持<code class="language-plaintext highlighter-rouge">--allow-unrelated-histories</code>参数。</p>
<h4 id="4-改动代码更新">4. 改动代码,更新</h4>
<p>之后可以正常的写代码了,等到需要更新subtree的代码的时候把更新push上去就可以了。</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git subtree push <span class="nt">--prefix</span><span class="o">=</span>/path/to/sub sub_origin master
</code></pre></div></div>Hisoka前一篇讲述了从现有项目中拆分subtree的步骤,这一篇说一下如何把一个现有的项目当成subtree加到当前项目中来。从现有项目中拆分Git Subtree2018-03-29T00:00:00+00:002018-03-29T00:00:00+00:00https://hisoka0917.github.io/git/2018/03/29/split-git-subtree<p>使用Git Subtree来管理不同项目中的公共部分。这是平时非常常见的需求。</p>
<p>比如说一个node后端服务使用express框架,前端静态页面放在dist文件夹中。前端工程在另外一个git仓库中,编译生成的静态文件放到这个dist文件夹里面。我们把前端工程叫做P1项目,后端项目叫做P2项目,dist文件夹所在的git项目叫做S项目。
通常情况下都是P1和P2项目都已经存在了,发现这两个项目需要提取公共模块,于是第一步是要拆分现有的工程项目。</p>
<h4 id="1-拆分现有项目">1. 拆分现有项目</h4>
<p>前端项目P1中已经有了<code class="language-plaintext highlighter-rouge">dist</code>文件夹,我们不想更改文件夹的名字。那么将现有的文件夹当作subtree的路径。</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd </span>P1_PATH
git subtree <span class="nb">split</span> <span class="nt">-P</span> <S项目的相对路径> <span class="nt">-b</span> <临时branch>
// 注: <span class="nt">-P</span> 与 <span class="nt">--prefix</span> 等价
</code></pre></div></div>
<p>在这个例子中具体的命令如下,临时分支名叫做<code class="language-plaintext highlighter-rouge">subtree</code></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git subtree <span class="nb">split</span> <span class="nt">-P</span> dist <span class="nt">-b</span> subtree
</code></pre></div></div>
<h4 id="2-创建子repo">2. 创建子repo</h4>
<p>在<code class="language-plaintext highlighter-rouge">dist</code>目录下新建一个git repo。由于现在<code class="language-plaintext highlighter-rouge">dist</code>目录下是有文件的,在pull之前的历史记录时会有问题,所以这个时候把现有的文件都删掉。</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd </span>dist
<span class="nb">rm</span> <span class="nt">-rf</span> <span class="k">*</span>
git init
git pull <P1项目路径> <临时branch>
git remote add origin <S项目的git仓库>
git push origin <span class="nt">-u</span> master
</code></pre></div></div>
<p>在这里具体的pull命令如下</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git pull ../ subtree
</code></pre></div></div>
<h4 id="3-清理数据">3. 清理数据</h4>
<p>在原项目中清除<code class="language-plaintext highlighter-rouge">dist</code>下的文件。清理步骤如下:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd </span>P1_PATH
git <span class="nb">rm</span> <span class="nt">-rf</span> <S项目相对路径>
git commit <span class="c">#提交删除记录</span>
git branch <span class="nt">-D</span> <临时branch> <span class="c">#删除临时分支</span>
</code></pre></div></div>
<p>本例中具体命令如下:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd</span> ..
git <span class="nb">rm</span> <span class="nt">-rf</span> dist
git commit <span class="nt">-m</span> <span class="s1">'删除dist'</span>
git branch <span class="nt">-D</span> subtree
<span class="nb">rm</span> <span class="nt">-rf</span> dist
</code></pre></div></div>
<p>在这里把<code class="language-plaintext highlighter-rouge">dist</code>物理文件都删除没有关系。</p>
<h4 id="4-添加subtree">4. 添加subtree</h4>
<p>接下来将subtree仓库地址链接到当前项目,命令如下:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git subtree add <span class="nt">--prefix</span><span class="o">=</span><S项目相对路径> <S项目git地址> <分支> <span class="nt">--squash</span>
</code></pre></div></div>
<p>S项目目前只有一个master分支,所以现在具体的命令如下:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git subtree add <span class="nt">--prefix</span><span class="o">=</span>dist S_GIT_REMOTE_PATH master <span class="nt">--squash</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">--squash</code>参数表示将subtree repo中的提交记录合并成一次commit,这样就不用拉取子项目的完整历史记录。</p>
<p>然后我们用<code class="language-plaintext highlighter-rouge">git push</code>把当前改动提交到远程就可以了。</p>
<h4 id="5-提交与更新subtree">5. 提交与更新subtree</h4>
<p>完成subtree的分离和添加以后我们可以正常的改动代码和提交。这些改动可能涉及到S项目的改动,这些都没有关系。等到我们需要同步S项目的时候,例如前端完成了一个新版本的开发,编译出一份静态资源,需要同步到后端测试。这时候用<code class="language-plaintext highlighter-rouge">git subtree push</code>命令来提交S项目。</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git subtree push <span class="nt">--prefix</span><span class="o">=</span><S项目相对路径> <S项目git地址> <分支>
</code></pre></div></div>
<p>Git会遍历之前的commit,找到对S目录的更改,然后把这些更改记录提交到S的Git仓库中。在本例中具体命令如下:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git subtree push <span class="nt">--prefix</span><span class="o">=</span>dist S_GIT_REMOTE_PATH master
</code></pre></div></div>
<p>在后端的P2项目中使用<code class="language-plaintext highlighter-rouge">git subtree pull --prefix=<S项目相对路径> <S项目git地址> <分支></code>来更新</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git subtree pull <span class="nt">--prefix</span><span class="o">=</span>dist S_GIT_REMOTE_PATH master
</code></pre></div></div>
<p>当然在P2项目中重复上述步骤添加subtree,之后就可以愉快的使用git subtree来使用公共模块了。
在本例中我们是分离一个现有的目录当作subtree,如果是新建一个目录来来当作subtree就会稍微简单一点。修改一下步骤2就可以了。</p>
<p>需要注意的是,并不是任意的git仓库都可以添加为subtree的,因为subtree的commit会被合并到当前项目历史记录中,所以如果两个仓库的历史记录无关的话是无法合并的。所以正确的姿势就应该是从当前项目中split出一个子项目来当作subtree。</p>Hisoka使用Git Subtree来管理不同项目中的公共部分。这是平时非常常见的需求。Swift编码规范2018-03-13T00:00:00+00:002018-03-13T00:00:00+00:00https://hisoka0917.github.io/swift/2018/03/13/swift-code-style<p>根据SwiftLint的规则自己撸了一份Swift编码规范。整个顺序都是按照SwiftLint的顺序,基本上属于搬运。</p>
<h4 id="1-委托delegate协议应当为class类可以被弱引用">1. 委托(Delegate)协议应当为class类,可以被弱引用</h4>
<h4 id="2-闭合小括号内有闭合大括号中间不能有任何空格">2. 闭合小括号内有闭合大括号中间不能有任何空格</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="p">[]</span><span class="o">.</span><span class="nf">map</span><span class="p">({</span> <span class="p">})</span>
<span class="p">[]</span><span class="o">.</span><span class="nf">map</span><span class="p">(</span>
<span class="p">{</span> <span class="p">}</span>
<span class="p">)</span>
<span class="c1">// Wrong</span>
<span class="p">[]</span><span class="o">.</span><span class="nf">map</span><span class="p">({</span> <span class="p">}</span> <span class="p">)</span>
<span class="p">[]</span><span class="o">.</span><span class="nf">map</span><span class="p">({</span> <span class="p">}</span><span class="o">.</span> <span class="p">)</span>
</code></pre></div></div>
<h4 id="3-闭包的封闭端和开始端应当有相同的缩进">3. 闭包的封闭端和开始端应当有相同的缩进</h4>
<h4 id="4-闭包的参数应当和左括号在同一行">4. 闭包的参数应当和左括号在同一行</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="n">number</span> <span class="k">in</span>
<span class="n">number</span> <span class="o">+</span> <span class="mi">1</span>
<span class="p">}</span>
<span class="c1">// Wrong</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span>
<span class="err">↓</span><span class="n">number</span> <span class="k">in</span>
<span class="n">number</span> <span class="o">+</span> <span class="mi">1</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="5-闭包表达式在每个大括号内应该有一个空格">5. 闭包表达式在每个大括号内应该有一个空格</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="nf">map</span><span class="p">({</span> <span class="nv">$0</span> <span class="p">})</span>
<span class="p">[]</span><span class="o">.</span><span class="nf">map</span> <span class="p">({</span> <span class="nv">$0</span><span class="o">.</span><span class="n">description</span> <span class="p">})</span>
<span class="c1">// Wrong</span>
<span class="nf">map</span><span class="p">({</span><span class="nv">$0</span> <span class="p">})</span>
<span class="nf">map</span><span class="p">({</span> <span class="nv">$0</span><span class="p">})</span>
<span class="nf">map</span><span class="p">({</span><span class="nv">$0</span><span class="p">})</span>
<span class="p">[]</span><span class="o">.</span><span class="nf">map</span> <span class="p">({</span><span class="nv">$0</span><span class="o">.</span><span class="n">description</span> <span class="p">})</span>
</code></pre></div></div>
<h4 id="6-冒号应当紧靠所定义的标识符即左侧没有空格右侧与类型之间必须只有一个空格如果在dictionary中紧靠key与value之间必须有且只有一个空格">6. 冒号<code class="language-plaintext highlighter-rouge">:</code>应当紧靠所定义的标识符,即左侧没有空格,右侧与类型之间必须只有一个空格。如果在<code class="language-plaintext highlighter-rouge">Dictionary</code>中,紧靠Key,与Value之间必须有且只有一个空格</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="k">let</span> <span class="nv">abc</span><span class="p">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="s">"jun"</span>
<span class="k">let</span> <span class="nv">abc</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">:</span> <span class="p">[</span><span class="mi">3</span><span class="p">:</span> <span class="mi">2</span><span class="p">],</span> <span class="mi">3</span><span class="p">:</span> <span class="mi">4</span><span class="p">]</span>
<span class="c1">// Wrong</span>
<span class="k">let</span> <span class="nv">jun</span><span class="p">:</span><span class="kt">Void</span>
<span class="k">let</span> <span class="nv">jun</span> <span class="p">:</span> <span class="kt">Void</span>
<span class="k">let</span> <span class="nv">jun</span> <span class="p">:</span><span class="kt">Void</span>
<span class="k">let</span> <span class="nv">jun</span><span class="p">:</span> <span class="kt">Void</span>
</code></pre></div></div>
<h4 id="7-逗号前面没有空格后面有且只有一个空格">7. 逗号<code class="language-plaintext highlighter-rouge">,</code>前面没有空格,后面有且只有一个空格</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="p">[</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">c</span><span class="p">,</span> <span class="n">d</span><span class="p">]</span>
<span class="kd">func</span> <span class="nf">abc</span><span class="p">(</span><span class="nv">a</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">b</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
<span class="kd">func</span> <span class="nf">abc</span><span class="p">(</span>
<span class="nv">a</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span>
<span class="nv">bcd</span><span class="p">:</span> <span class="kt">String</span>
<span class="p">)</span> <span class="p">{</span>
<span class="p">}</span>
<span class="c1">// Wrong</span>
<span class="p">[</span><span class="n">a</span> <span class="p">,</span><span class="n">b</span><span class="p">]</span>
<span class="kd">func</span> <span class="nf">abc</span><span class="p">(</span><span class="nv">a</span><span class="p">:</span> <span class="kt">String</span><span class="err">↓</span> <span class="p">,</span><span class="nv">b</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
<span class="k">let</span> <span class="nv">result</span> <span class="o">=</span> <span class="nf">plus</span><span class="p">(</span>
<span class="nv">first</span><span class="p">:</span> <span class="mi">3</span><span class="err">↓</span> <span class="p">,</span> <span class="c1">// Comment</span>
<span class="nv">second</span><span class="p">:</span> <span class="mi">4</span>
<span class="p">)</span>
</code></pre></div></div>
<h4 id="8-编译器协议中声明的初始化函数不应该直接调用">8. 编译器协议中声明的初始化函数不应该直接调用</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="k">let</span> <span class="nv">set</span><span class="p">:</span> <span class="kt">Set</span><span class="o"><</span><span class="kt">Int</span><span class="o">></span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span>
<span class="k">let</span> <span class="nv">set</span> <span class="o">=</span> <span class="kt">Set</span><span class="p">(</span><span class="n">array</span><span class="p">)</span>
<span class="c1">// Wrong</span>
<span class="k">let</span> <span class="nv">set</span> <span class="o">=</span> <span class="kt">Set</span><span class="p">(</span><span class="nv">arrayLiteral</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">set</span> <span class="o">=</span> <span class="kt">Set</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">arrayLiteral</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
</code></pre></div></div>
<h4 id="9-控制语句-forwhiledocatch语句中的条件不能包含在中">9. 控制语句 for,while,do,catch语句中的条件不能包含在()中</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="k">if</span> <span class="n">condition</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span> <span class="o">==</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Wrong</span>
<span class="k">if</span> <span class="p">(</span><span class="n">condition</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span><span class="p">(</span><span class="n">condition</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">((</span><span class="n">a</span> <span class="o">||</span> <span class="n">b</span><span class="p">)</span> <span class="o">&&</span> <span class="p">(</span><span class="n">c</span> <span class="o">||</span> <span class="n">d</span><span class="p">))</span> <span class="p">{</span>
</code></pre></div></div>
<h4 id="10-函数体复杂度应当被限制">10. 函数体复杂度应当被限制</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 建议</span>
<span class="kd">func</span> <span class="nf">f1</span><span class="p">()</span> <span class="p">{</span>
<span class="k">if</span> <span class="kc">true</span> <span class="p">{</span>
<span class="k">for</span> <span class="n">_</span> <span class="k">in</span> <span class="mi">1</span><span class="o">..</span><span class="mi">5</span> <span class="p">{</span> <span class="p">}</span>
<span class="p">}</span>
<span class="k">if</span> <span class="kc">false</span> <span class="p">{</span> <span class="p">}</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">f</span><span class="p">(</span><span class="nv">code</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Int</span> <span class="p">{</span>
<span class="k">switch</span> <span class="n">code</span> <span class="p">{</span>
<span class="k">case</span> <span class="mi">0</span><span class="p">:</span> <span class="k">fallthrough</span>
<span class="k">case</span> <span class="mi">0</span><span class="p">:</span> <span class="k">return</span> <span class="mi">1</span>
<span class="k">case</span> <span class="mi">0</span><span class="p">:</span> <span class="k">return</span> <span class="mi">1</span>
<span class="k">case</span> <span class="mi">0</span><span class="p">:</span> <span class="k">return</span> <span class="mi">1</span>
<span class="k">case</span> <span class="mi">0</span><span class="p">:</span> <span class="k">return</span> <span class="mi">1</span>
<span class="k">case</span> <span class="mi">0</span><span class="p">:</span> <span class="k">return</span> <span class="mi">1</span>
<span class="k">case</span> <span class="mi">0</span><span class="p">:</span> <span class="k">return</span> <span class="mi">1</span>
<span class="k">case</span> <span class="mi">0</span><span class="p">:</span> <span class="k">return</span> <span class="mi">1</span>
<span class="k">case</span> <span class="mi">0</span><span class="p">:</span> <span class="k">return</span> <span class="mi">1</span>
<span class="k">default</span><span class="p">:</span> <span class="k">return</span> <span class="mi">1</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">f1</span><span class="p">()</span> <span class="p">{</span><span class="k">if</span> <span class="kc">true</span> <span class="p">{};</span> <span class="k">if</span> <span class="kc">true</span> <span class="p">{};</span> <span class="k">if</span> <span class="kc">true</span> <span class="p">{};</span> <span class="k">if</span> <span class="kc">true</span> <span class="p">{};</span> <span class="k">if</span> <span class="kc">true</span> <span class="p">{};</span> <span class="k">if</span> <span class="kc">true</span> <span class="p">{}</span>
<span class="kd">func</span> <span class="nf">f2</span><span class="p">()</span> <span class="p">{</span>
<span class="k">if</span> <span class="kc">true</span> <span class="p">{};</span> <span class="k">if</span> <span class="kc">true</span> <span class="p">{};</span> <span class="k">if</span> <span class="kc">true</span> <span class="p">{};</span> <span class="k">if</span> <span class="kc">true</span> <span class="p">{};</span> <span class="k">if</span> <span class="kc">true</span> <span class="p">{}</span>
<span class="p">}}</span>
<span class="c1">// 不建议</span>
<span class="kd">func</span> <span class="nf">f1</span><span class="p">()</span> <span class="p">{</span>
<span class="k">if</span> <span class="kc">true</span> <span class="p">{</span>
<span class="k">if</span> <span class="kc">true</span> <span class="p">{</span>
<span class="k">if</span> <span class="kc">false</span> <span class="p">{}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">if</span> <span class="kc">false</span> <span class="p">{}</span>
<span class="k">let</span> <span class="nv">i</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">switch</span> <span class="n">i</span> <span class="p">{</span>
<span class="k">case</span> <span class="mi">1</span><span class="p">:</span> <span class="k">break</span>
<span class="k">case</span> <span class="mi">2</span><span class="p">:</span> <span class="k">break</span>
<span class="k">case</span> <span class="mi">3</span><span class="p">:</span> <span class="k">break</span>
<span class="k">case</span> <span class="mi">4</span><span class="p">:</span> <span class="k">break</span>
<span class="k">default</span><span class="p">:</span> <span class="k">break</span>
<span class="p">}</span>
<span class="k">for</span> <span class="n">_</span> <span class="k">in</span> <span class="mi">1</span><span class="o">...</span><span class="mi">5</span> <span class="p">{</span>
<span class="k">guard</span> <span class="kc">true</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">return</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="11-使用block注册通知时应当储存返回的观察者以便之后移除">11. 使用block注册通知时应当储存返回的观察者,以便之后移除</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="k">let</span> <span class="nv">foo</span> <span class="o">=</span> <span class="n">nc</span><span class="o">.</span><span class="nf">addObserver</span><span class="p">(</span><span class="nv">forName</span><span class="p">:</span> <span class="o">.</span><span class="kt">NSSystemTimeZoneDidChange</span><span class="p">,</span> <span class="nv">object</span><span class="p">:</span> <span class="kc">nil</span><span class="p">,</span> <span class="nv">queue</span><span class="p">:</span> <span class="kc">nil</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
<span class="k">let</span> <span class="nv">foo</span> <span class="o">=</span> <span class="n">nc</span><span class="o">.</span><span class="nf">addObserver</span><span class="p">(</span><span class="nv">forName</span><span class="p">:</span> <span class="o">.</span><span class="kt">NSSystemTimeZoneDidChange</span><span class="p">,</span> <span class="nv">object</span><span class="p">:</span> <span class="kc">nil</span><span class="p">,</span> <span class="nv">queue</span><span class="p">:</span> <span class="kc">nil</span><span class="p">,</span> <span class="nv">using</span><span class="p">:</span> <span class="p">{</span> <span class="p">})</span>
<span class="kd">func</span> <span class="nf">foo</span><span class="p">()</span> <span class="o">-></span> <span class="kt">Any</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">nc</span><span class="o">.</span><span class="nf">addObserver</span><span class="p">(</span><span class="nv">forName</span><span class="p">:</span> <span class="o">.</span><span class="kt">NSSystemTimeZoneDidChange</span><span class="p">,</span> <span class="nv">object</span><span class="p">:</span> <span class="kc">nil</span><span class="p">,</span> <span class="nv">queue</span><span class="p">:</span> <span class="kc">nil</span><span class="p">,</span> <span class="nv">using</span><span class="p">:</span> <span class="p">{</span> <span class="p">})</span>
<span class="p">}</span>
<span class="c1">// Wrong</span>
<span class="n">nc</span><span class="o">.</span><span class="nf">addObserver</span><span class="p">(</span><span class="nv">forName</span><span class="p">:</span> <span class="o">.</span><span class="kt">NSSystemTimeZoneDidChange</span><span class="p">,</span> <span class="nv">object</span><span class="p">:</span> <span class="kc">nil</span><span class="p">,</span> <span class="nv">queue</span><span class="p">:</span> <span class="kc">nil</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
<span class="n">nc</span><span class="o">.</span><span class="nf">addObserver</span><span class="p">(</span><span class="nv">forName</span><span class="p">:</span> <span class="o">.</span><span class="kt">NSSystemTimeZoneDidChange</span><span class="p">,</span> <span class="nv">object</span><span class="p">:</span> <span class="kc">nil</span><span class="p">,</span> <span class="nv">queue</span><span class="p">:</span> <span class="kc">nil</span><span class="p">,</span> <span class="nv">using</span><span class="p">:</span> <span class="p">{</span> <span class="p">})</span>
<span class="kd">@discardableResult</span> <span class="kd">func</span> <span class="nf">foo</span><span class="p">()</span> <span class="o">-></span> <span class="kt">Any</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">nc</span><span class="o">.</span><span class="nf">addObserver</span><span class="p">(</span><span class="nv">forName</span><span class="p">:</span> <span class="o">.</span><span class="kt">NSSystemTimeZoneDidChange</span><span class="p">,</span> <span class="nv">object</span><span class="p">:</span> <span class="kc">nil</span><span class="p">,</span> <span class="nv">queue</span><span class="p">:</span> <span class="kc">nil</span><span class="p">,</span> <span class="nv">using</span><span class="p">:</span> <span class="p">{</span> <span class="p">})</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="12-不推荐直接初始化可能有害的类型">12. 不推荐直接初始化可能有害的类型</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 建议写法</span>
<span class="k">let</span> <span class="nv">foo</span> <span class="o">=</span> <span class="kt">UIDevice</span><span class="o">.</span><span class="n">current</span>
<span class="k">let</span> <span class="nv">foo</span> <span class="o">=</span> <span class="kt">Bundle</span><span class="o">.</span><span class="n">main</span>
<span class="k">let</span> <span class="nv">foo</span> <span class="o">=</span> <span class="kt">Bundle</span><span class="p">(</span><span class="nv">path</span><span class="p">:</span> <span class="s">"bar"</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">foo</span> <span class="o">=</span> <span class="kt">Bundle</span><span class="p">(</span><span class="nv">identifier</span><span class="p">:</span> <span class="s">"bar"</span><span class="p">)</span>
<span class="c1">// 不建议写法</span>
<span class="k">let</span> <span class="nv">foo</span> <span class="o">=</span> <span class="kt">UIDevice</span><span class="p">()</span>
<span class="k">let</span> <span class="nv">foo</span> <span class="o">=</span> <span class="kt">Bundle</span><span class="p">()</span>
<span class="k">let</span> <span class="nv">foo</span> <span class="o">=</span> <span class="nf">bar</span><span class="p">(</span><span class="nv">bundle</span><span class="p">:</span> <span class="kt">Bundle</span><span class="p">(),</span> <span class="nv">device</span><span class="p">:</span> <span class="kt">UIDevice</span><span class="p">())</span>
</code></pre></div></div>
<h4 id="13-避免一起使用dynamic和inline__always">13. 避免一起使用<code class="language-plaintext highlighter-rouge">dynamic</code>和<code class="language-plaintext highlighter-rouge">@inline(__always)</code></h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="kd">class</span> <span class="kt">C</span> <span class="p">{</span>
<span class="kd">dynamic</span> <span class="kd">func</span> <span class="nf">f</span><span class="p">()</span> <span class="p">{}</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="kt">C</span> <span class="p">{</span>
<span class="kd">@inline</span><span class="p">(</span><span class="n">__always</span><span class="p">)</span> <span class="kd">func</span> <span class="nf">f</span><span class="p">()</span> <span class="p">{}</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="kt">C</span> <span class="p">{</span>
<span class="kd">@inline</span><span class="p">(</span><span class="n">never</span><span class="p">)</span> <span class="kd">dynamic</span> <span class="kd">func</span> <span class="nf">f</span><span class="p">()</span> <span class="p">{}</span>
<span class="p">}</span>
<span class="c1">// Wrong</span>
<span class="kd">class</span> <span class="kt">C</span> <span class="p">{</span>
<span class="kd">@inline</span><span class="p">(</span><span class="n">__always</span><span class="p">)</span> <span class="kd">dynamic</span> <span class="kd">func</span> <span class="nf">f</span><span class="p">()</span> <span class="p">{}</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="kt">C</span> <span class="p">{</span>
<span class="kd">@inline</span><span class="p">(</span><span class="n">__always</span><span class="p">)</span> <span class="kd">public</span> <span class="kd">dynamic</span> <span class="kd">func</span> <span class="nf">f</span><span class="p">()</span> <span class="p">{}</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="kt">C</span> <span class="p">{</span>
<span class="kd">@inline</span><span class="p">(</span><span class="n">__always</span><span class="p">)</span> <span class="kd">dynamic</span> <span class="kd">internal</span> <span class="kd">func</span> <span class="nf">f</span><span class="p">()</span> <span class="p">{}</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="kt">C</span> <span class="p">{</span>
<span class="kd">@inline</span><span class="p">(</span><span class="n">__always</span><span class="p">)</span>
<span class="kd">dynamic</span> <span class="kd">func</span> <span class="nf">f</span><span class="p">()</span> <span class="p">{}</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="kt">C</span> <span class="p">{</span>
<span class="kd">@inline</span><span class="p">(</span><span class="n">__always</span><span class="p">)</span>
<span class="kd">dynamic</span>
<span class="kd">func</span> <span class="nf">f</span><span class="p">()</span> <span class="p">{}</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="14-建议使用isempty而不是使用count--0比较">14. 建议使用<code class="language-plaintext highlighter-rouge">isEmpty</code>,而不是使用<code class="language-plaintext highlighter-rouge">count == 0</code>比较</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 建议写法</span>
<span class="k">if</span> <span class="n">number</span><span class="o">.</span><span class="n">isEmpty</span> <span class="p">{</span>
<span class="nf">print</span><span class="p">(</span><span class="s">"为空"</span><span class="p">)</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nf">print</span><span class="p">(</span><span class="s">"不为空"</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">// 不建议写法</span>
<span class="k">let</span> <span class="nv">number</span> <span class="o">=</span> <span class="s">"long"</span>
<span class="k">if</span> <span class="n">number</span><span class="o">.</span><span class="n">characters</span><span class="o">.</span><span class="n">count</span> <span class="o">==</span> <span class="mi">0</span> <span class="p">{</span>
<span class="nf">print</span><span class="p">(</span><span class="s">"为空"</span><span class="p">)</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nf">print</span><span class="p">(</span><span class="s">"不为空"</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="15-将枚举与关联类型进行匹配时如果不使用它们则可省略参数">15. 将枚举与关联类型进行匹配时,如果不使用它们,则可省略参数</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 建议写法</span>
<span class="k">switch</span> <span class="n">foo</span> <span class="p">{</span>
<span class="k">case</span> <span class="o">.</span><span class="nv">bar</span><span class="p">:</span> <span class="k">break</span>
<span class="p">}</span>
<span class="k">switch</span> <span class="n">foo</span> <span class="p">{</span>
<span class="k">case</span> <span class="o">.</span><span class="nf">bar</span><span class="p">(</span><span class="k">let</span> <span class="nv">x</span><span class="p">):</span> <span class="k">break</span>
<span class="p">}</span>
<span class="k">switch</span> <span class="n">foo</span> <span class="p">{</span>
<span class="k">case</span> <span class="kd">let</span> <span class="o">.</span><span class="nf">bar</span><span class="p">(</span><span class="n">x</span><span class="p">):</span> <span class="k">break</span>
<span class="p">}</span>
<span class="k">switch</span> <span class="p">(</span><span class="n">foo</span><span class="p">,</span> <span class="n">bar</span><span class="p">)</span> <span class="p">{</span>
<span class="k">case</span> <span class="p">(</span><span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">):</span> <span class="k">break</span>
<span class="p">}</span>
<span class="k">switch</span> <span class="n">foo</span> <span class="p">{</span>
<span class="k">case</span> <span class="s">"bar"</span><span class="o">.</span><span class="nf">uppercased</span><span class="p">():</span> <span class="k">break</span>
<span class="p">}</span>
<span class="c1">// 不建议写法</span>
<span class="k">switch</span> <span class="n">foo</span> <span class="p">{</span>
<span class="k">case</span> <span class="o">.</span><span class="nf">bar</span><span class="p">(</span><span class="n">_</span><span class="p">):</span> <span class="k">break</span>
<span class="p">}</span>
<span class="k">switch</span> <span class="n">foo</span> <span class="p">{</span>
<span class="k">case</span> <span class="o">.</span><span class="nf">bar</span><span class="p">():</span> <span class="k">break</span>
<span class="p">}</span>
<span class="k">switch</span> <span class="n">foo</span> <span class="p">{</span>
<span class="k">case</span> <span class="o">.</span><span class="nf">bar</span><span class="p">(</span><span class="n">_</span><span class="p">),</span> <span class="o">.</span><span class="nf">bar2</span><span class="p">(</span><span class="n">_</span><span class="p">):</span> <span class="k">break</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="16-闭包参数为空时建议使用---void而不是void---void">16. 闭包参数为空时建议使用<code class="language-plaintext highlighter-rouge">() -> Void</code>而不是<code class="language-plaintext highlighter-rouge">Void -> Void</code></h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="k">let</span> <span class="nv">abc</span><span class="p">:</span> <span class="p">()</span> <span class="o">-></span> <span class="kt">Void</span>
<span class="kd">func</span> <span class="nf">foo</span><span class="p">(</span><span class="nv">completion</span><span class="p">:</span> <span class="p">()</span> <span class="o">-></span> <span class="kt">Void</span><span class="p">)</span> <span class="p">{</span>
<span class="p">}</span>
<span class="c1">// Wrong</span>
<span class="k">let</span> <span class="nv">bcd</span><span class="p">:</span> <span class="kt">Void</span> <span class="o">-></span> <span class="kt">Void</span>
<span class="kd">func</span> <span class="nf">foo</span><span class="p">(</span><span class="nv">completion</span><span class="p">:</span> <span class="kt">Void</span> <span class="o">-></span> <span class="kt">Void</span><span class="p">)</span> <span class="p">{</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="17-使用尾随闭包时避免使用空的圆括号">17. 使用尾随闭包时,避免使用空的圆括号</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="nv">$0</span> <span class="o">+</span> <span class="mi">1</span> <span class="p">}</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span><span class="o">.</span><span class="nf">map</span><span class="p">({</span> <span class="nv">$0</span> <span class="o">+</span> <span class="mi">1</span> <span class="p">})</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span><span class="o">.</span><span class="nf">reduce</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="p">{</span> <span class="nv">$0</span> <span class="o">+</span> <span class="nv">$1</span> <span class="p">}</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="n">number</span> <span class="k">in</span>
<span class="n">number</span> <span class="o">+</span> <span class="mi">1</span>
<span class="p">}</span>
<span class="k">let</span> <span class="nv">isEmpty</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span><span class="o">.</span><span class="nf">isEmpty</span><span class="p">()</span>
<span class="kt">UIView</span><span class="o">.</span><span class="nf">animateWithDuration</span><span class="p">(</span><span class="mf">0.3</span><span class="p">,</span> <span class="nv">animations</span><span class="p">:</span> <span class="p">{</span>
<span class="k">self</span><span class="o">.</span><span class="n">disableInteractionRightView</span><span class="o">.</span><span class="n">alpha</span> <span class="o">=</span> <span class="mi">0</span>
<span class="p">},</span> <span class="nv">completion</span><span class="p">:</span> <span class="p">{</span> <span class="n">_</span> <span class="nf">in</span>
<span class="p">()</span>
<span class="p">})</span>
<span class="c1">// Wrong</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span><span class="o">.</span><span class="nf">map</span><span class="p">()</span> <span class="p">{</span> <span class="nv">$0</span> <span class="o">+</span> <span class="mi">1</span> <span class="p">}</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span><span class="o">.</span><span class="nf">map</span><span class="p">(</span> <span class="p">)</span> <span class="p">{</span> <span class="nv">$0</span> <span class="o">+</span> <span class="mi">1</span> <span class="p">}</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span><span class="o">.</span><span class="nf">map</span><span class="p">()</span> <span class="p">{</span> <span class="n">number</span> <span class="k">in</span>
<span class="n">number</span> <span class="o">+</span> <span class="mi">1</span>
<span class="p">}</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span><span class="o">.</span><span class="nf">map</span><span class="p">(</span> <span class="p">)</span> <span class="p">{</span> <span class="n">number</span> <span class="k">in</span>
<span class="n">number</span> <span class="o">+</span> <span class="mi">1</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="18-在声明类属性和方法时注意访问控制aclprivatefileprivateinternalpublicopen-必要时明确指定修饰关键字">18. 在声明类属性和方法时注意访问控制(ACL),<code class="language-plaintext highlighter-rouge">private</code>,<code class="language-plaintext highlighter-rouge">fileprivate</code>,<code class="language-plaintext highlighter-rouge">internal</code>,<code class="language-plaintext highlighter-rouge">public</code>,<code class="language-plaintext highlighter-rouge">open</code> 必要时明确指定修饰关键字</h4>
<h4 id="19-尽量避免显式调用init">19. 尽量避免显式调用<code class="language-plaintext highlighter-rouge">.init()</code></h4>
<h4 id="20-避免使用fallthrough">20. 避免使用<code class="language-plaintext highlighter-rouge">fallthrough</code></h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="k">switch</span> <span class="n">foo</span> <span class="p">{</span>
<span class="k">case</span> <span class="o">.</span><span class="n">bar</span><span class="p">,</span> <span class="o">.</span><span class="n">bar2</span><span class="p">,</span> <span class="o">.</span><span class="nv">bar3</span><span class="p">:</span>
<span class="nf">something</span><span class="p">()</span>
<span class="p">}</span>
<span class="c1">// Wrong</span>
<span class="k">switch</span> <span class="n">foo</span> <span class="p">{</span>
<span class="k">case</span> <span class="o">.</span><span class="nv">bar</span><span class="p">:</span>
<span class="k">fallthrough</span>
<span class="k">case</span> <span class="o">.</span><span class="nv">bar2</span><span class="p">:</span>
<span class="nf">something</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="21-每个文件大于700行时应考虑重构文件行数禁止大于1200行">21. 每个文件大于700行时应考虑重构。文件行数禁止大于1200行。</h4>
<h4 id="22-for循环中如果只有单个if条件建议使用where表达式">22. <code class="language-plaintext highlighter-rouge">for</code>循环中如果只有单个<code class="language-plaintext highlighter-rouge">if</code>条件建议使用<code class="language-plaintext highlighter-rouge">where</code>表达式</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 建议写法</span>
<span class="k">for</span> <span class="n">user</span> <span class="k">in</span> <span class="n">users</span> <span class="k">where</span> <span class="n">user</span><span class="o">.</span><span class="n">id</span> <span class="o">==</span> <span class="mi">1</span> <span class="p">{</span> <span class="p">}</span>
<span class="k">for</span> <span class="n">user</span> <span class="k">in</span> <span class="n">users</span> <span class="p">{</span>
<span class="k">if</span> <span class="k">let</span> <span class="nv">id</span> <span class="o">=</span> <span class="n">user</span><span class="o">.</span><span class="n">id</span> <span class="p">{</span> <span class="p">}</span>
<span class="p">}</span>
<span class="k">for</span> <span class="n">user</span> <span class="k">in</span> <span class="n">users</span> <span class="p">{</span>
<span class="k">if</span> <span class="k">var</span> <span class="nv">id</span> <span class="o">=</span> <span class="n">user</span><span class="o">.</span><span class="n">id</span> <span class="p">{</span> <span class="p">}</span>
<span class="p">}</span>
<span class="k">for</span> <span class="n">user</span> <span class="k">in</span> <span class="n">users</span> <span class="p">{</span>
<span class="k">if</span> <span class="n">user</span><span class="o">.</span><span class="n">id</span> <span class="o">==</span> <span class="mi">1</span> <span class="p">{</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="p">}</span>
<span class="p">}</span>
<span class="k">for</span> <span class="n">user</span> <span class="k">in</span> <span class="n">users</span> <span class="p">{</span>
<span class="k">if</span> <span class="n">user</span><span class="o">.</span><span class="n">id</span> <span class="o">==</span> <span class="mi">1</span> <span class="p">{</span> <span class="p">}</span>
<span class="nf">print</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">for</span> <span class="n">user</span> <span class="k">in</span> <span class="n">users</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">id</span> <span class="o">=</span> <span class="n">user</span><span class="o">.</span><span class="n">id</span>
<span class="k">if</span> <span class="n">id</span> <span class="o">==</span> <span class="mi">1</span> <span class="p">{</span> <span class="p">}</span>
<span class="p">}</span>
<span class="k">for</span> <span class="n">user</span> <span class="k">in</span> <span class="n">users</span> <span class="p">{</span>
<span class="k">if</span> <span class="n">user</span><span class="o">.</span><span class="n">id</span> <span class="o">==</span> <span class="mi">1</span> <span class="o">&&</span> <span class="n">user</span><span class="o">.</span><span class="n">age</span> <span class="o">></span> <span class="mi">18</span> <span class="p">{</span> <span class="p">}</span>
<span class="p">}</span>
<span class="c1">// 不建议写法</span>
<span class="k">for</span> <span class="n">user</span> <span class="k">in</span> <span class="n">users</span> <span class="p">{</span>
<span class="k">if</span> <span class="n">user</span><span class="o">.</span><span class="n">id</span> <span class="o">==</span> <span class="mi">1</span> <span class="p">{</span> <span class="k">return</span> <span class="kc">true</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="23-避免强制类型转换force-cast-as">23. 避免强制类型转换(force cast) <code class="language-plaintext highlighter-rouge">as!</code></h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="kt">NSNumber</span><span class="p">()</span> <span class="k">as?</span> <span class="kt">Int</span>
<span class="c1">// Wrong</span>
<span class="kt">NSNumber</span><span class="p">()</span> <span class="k">as!</span> <span class="kt">Int</span>
</code></pre></div></div>
<h4 id="24-对会抛出异常throw的方法避免使用try强解">24. 对会抛出异常(throw)的方法,避免使用<code class="language-plaintext highlighter-rouge">try!</code>强解</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="kd">func</span> <span class="nf">a</span><span class="p">()</span> <span class="k">throws</span> <span class="p">{};</span> <span class="k">do</span> <span class="p">{</span> <span class="k">try</span> <span class="nf">a</span><span class="p">()</span> <span class="p">}</span> <span class="k">catch</span> <span class="p">{}</span>
<span class="c1">// Wrong</span>
<span class="kd">func</span> <span class="nf">a</span><span class="p">()</span> <span class="k">throws</span> <span class="p">{};</span> <span class="k">try!</span> <span class="nf">a</span><span class="p">()</span>
</code></pre></div></div>
<h4 id="25-尽量避免使用强制解包">25. 尽量避免使用强制解包。</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 建议写法</span>
<span class="n">navigationController</span><span class="p">?</span><span class="o">.</span><span class="nf">pushViewController</span><span class="p">(</span><span class="n">myViewController</span><span class="p">,</span> <span class="nv">animated</span><span class="p">:</span> <span class="kc">true</span><span class="p">)</span>
<span class="c1">// 不建议写法</span>
<span class="n">navigationController</span><span class="o">!.</span><span class="nf">pushViewController</span><span class="p">(</span><span class="n">myViewController</span><span class="p">,</span> <span class="nv">animated</span><span class="p">:</span> <span class="kc">true</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">url</span> <span class="o">=</span> <span class="kt">NSURL</span><span class="p">(</span><span class="nv">string</span><span class="p">:</span> <span class="s">"http://www.baidu.com"</span><span class="p">)</span><span class="o">!</span>
<span class="nf">print</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
<span class="k">return</span> <span class="n">cell</span><span class="o">!</span>
</code></pre></div></div>
<h4 id="26-函数体长度限制大于60行应当重构单个函数体长度禁止大于100行">26. 函数体长度限制。大于60行应当重构。单个函数体长度禁止大于100行。</h4>
<h4 id="27-函数参数数量应该尽量少一般不超过5个禁止大于8个">27. 函数参数数量应该尽量少。一般不超过5个。禁止大于8个。</h4>
<h4 id="28-范型类型名称只能包含字母数字以大写字母开头长度介于120个字符之间">28. 范型类型名称只能包含字母数字,以大写字母开头,长度介于1~20个字符之间</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="kd">func</span> <span class="n">foo</span><span class="o"><</span><span class="kt">T</span><span class="o">></span><span class="p">()</span> <span class="p">{}</span>
<span class="kd">func</span> <span class="n">foo</span><span class="o"><</span><span class="kt">T</span><span class="o">></span><span class="p">()</span> <span class="o">-></span> <span class="kt">T</span> <span class="p">{}</span>
<span class="kd">func</span> <span class="n">foo</span><span class="o"><</span><span class="kt">T</span><span class="p">,</span> <span class="kt">U</span><span class="o">></span><span class="p">(</span><span class="nv">param</span><span class="p">:</span> <span class="kt">U</span><span class="p">)</span> <span class="o">-></span> <span class="kt">T</span> <span class="p">{}</span>
<span class="kd">func</span> <span class="n">foo</span><span class="o"><</span><span class="kt">T</span><span class="p">:</span> <span class="kt">Hashable</span><span class="p">,</span> <span class="kt">U</span><span class="p">:</span> <span class="kt">Rule</span><span class="o">></span><span class="p">(</span><span class="nv">param</span><span class="p">:</span> <span class="kt">U</span><span class="p">)</span> <span class="o">-></span> <span class="kt">T</span> <span class="p">{}</span>
<span class="c1">// Wrong</span>
<span class="kd">func</span> <span class="n">foo</span><span class="o"><</span><span class="kt">T_Foo</span><span class="o">></span><span class="p">()</span> <span class="p">{}</span>
<span class="kd">func</span> <span class="n">foo</span><span class="o"><</span><span class="kt">T</span><span class="p">,</span> <span class="kt">U_Foo</span><span class="o">></span><span class="p">(</span><span class="nv">param</span><span class="p">:</span> <span class="kt">U_Foo</span><span class="p">)</span> <span class="o">-></span> <span class="kt">T</span> <span class="p">{}</span>
<span class="kd">func</span> <span class="n">foo</span><span class="o"><</span><span class="kt">TTTTTTTTTTTTTTTTTTTTT</span><span class="o">></span><span class="p">()</span> <span class="p">{}</span>
<span class="kd">func</span> <span class="n">foo</span><span class="o"><</span><span class="n">type</span><span class="o">></span><span class="p">()</span> <span class="p">{}</span>
</code></pre></div></div>
<h4 id="29-变量标识符名称应该只包含字母数字字符并以小写字母开头或只应包含大写字母在上述例外情况下当变量名称被声明为静态且不可变时变量名称可能以大写字母开头变量名称不应该太长或太短">29. 变量标识符名称应该只包含字母数字字符,并以小写字母开头或只应包含大写字母。在上述例外情况下,当变量名称被声明为静态且不可变时,变量名称可能以大写字母开头。变量名称不应该太长或太短</h4>
<h4 id="30-只读属性不使用get关键字">30. 只读属性不使用get关键字</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="kd">class</span> <span class="kt">Foo</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">foo</span><span class="p">:</span> <span class="kt">Int</span> <span class="p">{</span>
<span class="k">get</span> <span class="p">{</span>
<span class="k">return</span> <span class="mi">3</span>
<span class="p">}</span>
<span class="k">set</span> <span class="p">{</span>
<span class="n">_abc</span> <span class="o">=</span> <span class="n">newValue</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="kt">Foo</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">foo</span><span class="p">:</span> <span class="kt">Int</span> <span class="p">{</span>
<span class="k">return</span> <span class="mi">20</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="kt">Foo</span> <span class="p">{</span>
<span class="kd">static</span> <span class="k">var</span> <span class="nv">foo</span><span class="p">:</span> <span class="kt">Int</span> <span class="p">{</span>
<span class="k">return</span> <span class="mi">20</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// Wrong</span>
<span class="kd">class</span> <span class="kt">Foo</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">foo</span><span class="p">:</span> <span class="kt">Int</span> <span class="p">{</span>
<span class="k">get</span> <span class="p">{</span>
<span class="k">return</span> <span class="mi">20</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="kt">Foo</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">foo</span><span class="p">:</span> <span class="kt">Int</span> <span class="p">{</span>
<span class="k">get</span><span class="p">{</span>
<span class="k">return</span> <span class="mi">20</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="31-尽可能避免使用隐式解析可选类型">31. 尽可能避免使用隐式解析可选类型</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="kd">@IBOutlet</span> <span class="k">var</span> <span class="nv">label</span><span class="p">:</span> <span class="kt">UILabel</span><span class="o">!</span>
<span class="k">let</span> <span class="nv">int</span><span class="p">:</span> <span class="kt">Int</span><span class="p">?</span> <span class="o">=</span> <span class="mi">42</span>
<span class="k">let</span> <span class="nv">int</span><span class="p">:</span> <span class="kt">Int</span><span class="p">?</span> <span class="o">=</span> <span class="kc">nil</span>
<span class="c1">// Wrong</span>
<span class="k">let</span> <span class="nv">label</span><span class="p">:</span> <span class="kt">UILabel</span><span class="o">!</span>
<span class="k">let</span> <span class="nv">int</span><span class="p">:</span> <span class="kt">Int</span><span class="o">!</span> <span class="o">=</span> <span class="mi">42</span>
<span class="k">let</span> <span class="nv">int</span><span class="p">:</span> <span class="kt">Int</span><span class="o">!</span> <span class="o">=</span> <span class="kc">nil</span>
<span class="k">var</span> <span class="nv">ints</span><span class="p">:</span> <span class="p">[</span><span class="kt">Int</span><span class="o">!</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="mi">42</span><span class="p">,</span> <span class="kc">nil</span><span class="p">,</span> <span class="mi">42</span><span class="p">]</span>
<span class="kd">func</span> <span class="nf">foo</span><span class="p">(</span><span class="nv">int</span><span class="p">:</span> <span class="kt">Int</span><span class="o">!</span><span class="p">)</span> <span class="p">{}</span>
<span class="k">let</span> <span class="nv">int</span><span class="p">:</span> <span class="kt">ImplicitlyUnwrappedOptional</span><span class="o"><</span><span class="kt">Int</span><span class="o">></span>
</code></pre></div></div>
<h4 id="32-初始化集合set时推荐使用setisdisjoint-不建议setintersection">32. 初始化集合Set时,推荐使用Set.isDisjoint(), 不建议:Set.intersection</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 建议写法</span>
<span class="n">_</span> <span class="o">=</span> <span class="kt">Set</span><span class="p">(</span><span class="n">syntaxKinds</span><span class="p">)</span><span class="o">.</span><span class="nf">isDisjoint</span><span class="p">(</span><span class="nv">with</span><span class="p">:</span> <span class="n">commentAndStringKindsSet</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">isObjc</span> <span class="o">=</span> <span class="o">!</span><span class="n">objcAttributes</span><span class="o">.</span><span class="nf">isDisjoint</span><span class="p">(</span><span class="nv">with</span><span class="p">:</span> <span class="n">dictionary</span><span class="o">.</span><span class="n">enclosedSwiftAttributes</span><span class="p">)</span>
<span class="n">_</span> <span class="o">=</span> <span class="kt">Set</span><span class="p">(</span><span class="n">syntaxKinds</span><span class="p">)</span><span class="o">.</span><span class="nf">intersection</span><span class="p">(</span><span class="n">commentAndStringKindsSet</span><span class="p">)</span>
<span class="n">_</span> <span class="o">=</span> <span class="o">!</span><span class="n">objcAttributes</span><span class="o">.</span><span class="nf">intersection</span><span class="p">(</span><span class="n">dictionary</span><span class="o">.</span><span class="n">enclosedSwiftAttributes</span><span class="p">)</span>
<span class="c1">// 不建议写法</span>
<span class="n">_</span> <span class="o">=</span> <span class="kt">Set</span><span class="p">(</span><span class="n">syntaxKinds</span><span class="p">)</span><span class="o">.</span><span class="nf">intersection</span><span class="p">(</span><span class="n">commentAndStringKindsSet</span><span class="p">)</span><span class="o">.</span><span class="n">isEmpty</span>
<span class="k">let</span> <span class="nv">isObjc</span> <span class="o">=</span> <span class="o">!</span><span class="n">objcAttributes</span><span class="o">.</span><span class="nf">intersection</span><span class="p">(</span><span class="n">dictionary</span><span class="o">.</span><span class="n">enclosedSwiftAttributes</span><span class="p">)</span><span class="o">.</span><span class="n">isEmpty</span>
</code></pre></div></div>
<h4 id="33-元组tuple成员个数不要太多一般不超过3个">33. 元组(tuple)成员个数不要太多,一般不超过3个</h4>
<h4 id="34-文件开头不应有空格或空行">34. 文件开头不应有空格或空行</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">/// Correct</span>
<span class="c1">//</span>
<span class="c1">// ViewController.swift</span>
<span class="c1">/// Wrong</span>
<span class="c1">//......................这里有个空格</span>
<span class="c1">// ViewController.swift</span>
<span class="c1">/// Wrong</span>
<span class="o">.........................</span><span class="n">这里是个空行</span>
<span class="c1">//</span>
<span class="c1">// ViewController.swift</span>
</code></pre></div></div>
<h4 id="35-使用结构体扩展属性和方法而不是传统函数">35. 使用结构体扩展属性和方法而不是传统函数</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="n">rect</span><span class="o">.</span><span class="n">width</span>
<span class="n">rect</span><span class="o">.</span><span class="n">height</span>
<span class="n">rect</span><span class="o">.</span><span class="n">isNull</span>
<span class="n">rect</span><span class="o">.</span><span class="n">isEmpty</span>
<span class="n">rect</span><span class="o">.</span><span class="nf">insetBy</span><span class="p">(</span><span class="nv">dx</span><span class="p">:</span> <span class="mf">5.0</span><span class="p">,</span> <span class="nv">dy</span><span class="p">:</span> <span class="o">-</span><span class="mf">7.0</span><span class="p">)</span>
<span class="c1">// Wrong</span>
<span class="kt">CGRectGetWidth</span><span class="p">(</span><span class="n">rect</span><span class="p">)</span>
<span class="kt">CGRectGetHeight</span><span class="p">(</span><span class="n">rect</span><span class="p">)</span>
<span class="kt">CGRectIsNull</span><span class="p">(</span><span class="n">rect</span><span class="p">)</span>
<span class="kt">CGRectIsEmpty</span><span class="p">(</span><span class="n">rect</span><span class="p">)</span>
<span class="kt">CGRectInset</span><span class="p">(</span><span class="n">rect</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span>
</code></pre></div></div>
<h4 id="36-使用结构体范围的常量而不是传统的全局常量">36. 使用结构体范围的常量而不是传统的全局常量</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="kt">CGRect</span><span class="o">.</span><span class="n">infinite</span>
<span class="kt">CGPoint</span><span class="o">.</span><span class="n">zero</span>
<span class="kt">CGRect</span><span class="o">.</span><span class="n">zero</span>
<span class="kt">CGRect</span><span class="o">.</span><span class="n">null</span>
<span class="kt">CGFloat</span><span class="o">.</span><span class="n">pi</span>
<span class="c1">// Wrong</span>
<span class="kt">CGRectInfinite</span>
<span class="kt">CGPointZero</span>
<span class="kt">CGRectZero</span>
<span class="kt">CGRectNull</span>
<span class="kt">CGFloat</span><span class="p">(</span><span class="kt">M_PI</span><span class="p">)</span>
</code></pre></div></div>
<h4 id="37-使用swift构造函数而不是传统便利函数">37. 使用Swift构造函数而不是传统便利函数</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="kt">CGPoint</span><span class="p">(</span><span class="nv">x</span><span class="p">:</span> <span class="mi">10</span><span class="p">,</span> <span class="nv">y</span><span class="p">:</span> <span class="mi">10</span><span class="p">)</span>
<span class="kt">CGPoint</span><span class="p">(</span><span class="nv">x</span><span class="p">:</span> <span class="n">xValue</span><span class="p">,</span> <span class="nv">y</span><span class="p">:</span> <span class="n">yValue</span><span class="p">)</span>
<span class="kt">CGSize</span><span class="p">(</span><span class="nv">width</span><span class="p">:</span> <span class="mi">10</span><span class="p">,</span> <span class="nv">height</span><span class="p">:</span> <span class="mi">10</span><span class="p">)</span>
<span class="c1">// Wrong</span>
<span class="kt">CGPointMake</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span>
<span class="kt">CGPointMake</span><span class="p">(</span><span class="n">xVal</span><span class="p">,</span> <span class="n">yVal</span><span class="p">)</span>
<span class="kt">CGSizeMake</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span>
</code></pre></div></div>
<h4 id="38-使用结构体扩展属性和方法而不是传统ns函数">38. 使用结构体扩展属性和方法而不是传统NS函数</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="n">rect</span><span class="o">.</span><span class="n">width</span>
<span class="n">rect</span><span class="o">.</span><span class="n">height</span>
<span class="n">rect</span><span class="o">.</span><span class="n">isNull</span>
<span class="n">rect</span><span class="o">.</span><span class="n">isEmpty</span>
<span class="n">rect</span><span class="o">.</span><span class="nf">insetBy</span><span class="p">(</span><span class="nv">dx</span><span class="p">:</span> <span class="mf">5.0</span><span class="p">,</span> <span class="nv">dy</span><span class="p">:</span> <span class="o">-</span><span class="mf">7.0</span><span class="p">)</span>
<span class="c1">// Wrong</span>
<span class="kt">NSWidth</span><span class="p">(</span><span class="n">rect</span><span class="p">)</span>
<span class="kt">NSHeight</span><span class="p">(</span><span class="n">rect</span><span class="p">)</span>
<span class="kt">NSIsEmptyRect</span><span class="p">(</span><span class="n">rect</span><span class="p">)</span>
<span class="kt">NSIntersectsRect</span><span class="p">(</span><span class="n">rect1</span><span class="p">,</span> <span class="n">rect2</span><span class="p">)</span>
</code></pre></div></div>
<h4 id="39-单行代码长度不能超过120个字符">39. 单行代码长度不能超过120个字符</h4>
<h4 id="40-数组array和字典dictionary字面量结束处应该与它开始处有相同的缩进">40. 数组(Array)和字典(Dictionary)字面量结束处应该与它开始处有相同的缩进</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span>
<span class="mi">2</span>
<span class="p">]</span>
<span class="p">[</span>
<span class="mi">1</span><span class="p">,</span>
<span class="mi">2</span>
<span class="p">]</span>
<span class="p">[</span>
<span class="mi">1</span><span class="p">,</span>
<span class="mi">2</span><span class="p">]</span>
<span class="k">let</span> <span class="nv">x</span> <span class="o">=</span> <span class="p">[</span>
<span class="mi">1</span><span class="p">,</span>
<span class="mi">2</span>
<span class="p">]</span>
<span class="p">[</span><span class="nv">key</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="nv">key2</span><span class="p">:</span> <span class="mi">3</span><span class="p">]</span>
<span class="p">[</span><span class="nv">key</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
<span class="nv">key2</span><span class="p">:</span> <span class="mi">2</span>
<span class="p">]</span>
<span class="p">[</span>
<span class="nv">key</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
<span class="nv">key2</span><span class="p">:</span> <span class="mi">20</span>
<span class="p">]</span>
<span class="c1">// Wrong</span>
<span class="k">let</span> <span class="nv">x</span> <span class="o">=</span> <span class="p">[</span>
<span class="mi">1</span><span class="p">,</span>
<span class="mi">2</span>
<span class="p">]</span>
<span class="k">let</span> <span class="nv">x</span> <span class="o">=</span> <span class="p">[</span>
<span class="mi">1</span><span class="p">,</span>
<span class="mi">2</span>
<span class="p">]</span>
<span class="k">let</span> <span class="nv">x</span> <span class="o">=</span> <span class="p">[</span>
<span class="nv">key</span><span class="p">:</span> <span class="n">value</span>
<span class="p">]</span>
</code></pre></div></div>
<h4 id="41-mark注释应当采用有效格式-mark或-mark--">41. MARK注释应当采用有效格式<code class="language-plaintext highlighter-rouge">// MARK:...</code>或<code class="language-plaintext highlighter-rouge">// MARK: -...</code></h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">/// Correct</span>
<span class="c1">// MARK: good</span>
<span class="c1">// MARK: - good</span>
<span class="c1">// MARK: -</span>
<span class="c1">// BOOKMARK</span>
<span class="c1">/// Wrong</span>
<span class="c1">//MARK: bad</span>
<span class="c1">// MARK:bad</span>
<span class="c1">//MARK:bad</span>
<span class="c1">// MARK: bad</span>
<span class="c1">// MARK: bad</span>
<span class="c1">// MARK: -bad</span>
<span class="c1">// MARK:- bad</span>
<span class="c1">// MARK:-bad</span>
<span class="c1">//MARK: - bad</span>
</code></pre></div></div>
<h4 id="42-当参数闭包多于一个的时候不使用尾随闭包语法">42. 当参数闭包多于一个的时候不使用尾随闭包语法</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="n">foo</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="nv">$0</span> <span class="o">+</span> <span class="mi">1</span> <span class="p">}</span>
<span class="n">foo</span><span class="o">.</span><span class="nf">reduce</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="p">{</span> <span class="nv">$0</span> <span class="o">+</span> <span class="nv">$1</span> <span class="p">}</span>
<span class="k">if</span> <span class="k">let</span> <span class="nv">foo</span> <span class="o">=</span> <span class="n">bar</span><span class="o">.</span><span class="nf">map</span><span class="p">({</span> <span class="nv">$0</span> <span class="o">+</span> <span class="mi">1</span> <span class="p">})</span> <span class="p">{</span>
<span class="p">}</span>
<span class="n">foo</span><span class="o">.</span><span class="nf">something</span><span class="p">(</span><span class="nv">param1</span><span class="p">:</span> <span class="p">{</span> <span class="nv">$0</span> <span class="p">},</span> <span class="nv">param2</span><span class="p">:</span> <span class="p">{</span> <span class="nv">$0</span> <span class="o">+</span> <span class="mi">1</span> <span class="p">})</span>
<span class="kt">UIView</span><span class="o">.</span><span class="nf">animate</span><span class="p">(</span><span class="nv">withDuration</span><span class="p">:</span> <span class="mf">1.0</span><span class="p">)</span> <span class="p">{</span>
<span class="n">someView</span><span class="o">.</span><span class="n">alpha</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="p">}</span>
<span class="c1">// Wrong</span>
<span class="n">foo</span><span class="o">.</span><span class="nf">something</span><span class="p">(</span><span class="nv">param1</span><span class="p">:</span> <span class="p">{</span> <span class="nv">$0</span> <span class="p">})</span> <span class="p">{</span> <span class="nv">$0</span> <span class="o">+</span> <span class="mi">1</span> <span class="p">}</span>
<span class="kt">UIView</span><span class="o">.</span><span class="nf">animate</span><span class="p">(</span><span class="nv">withDuration</span><span class="p">:</span> <span class="mf">1.0</span><span class="p">,</span> <span class="nv">animations</span><span class="p">:</span> <span class="p">{</span>
<span class="n">someView</span><span class="o">.</span><span class="n">alpha</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="p">})</span> <span class="p">{</span> <span class="n">_</span> <span class="k">in</span>
<span class="n">someView</span><span class="o">.</span><span class="nf">removeFromSuperview</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="43-一个对象作为观察者应该只在deinit中移除自己">43. 一个对象作为观察者应该只在<code class="language-plaintext highlighter-rouge">deinit</code>中移除自己</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="kd">class</span> <span class="kt">Foo</span> <span class="p">{</span>
<span class="kd">deinit</span> <span class="p">{</span>
<span class="kt">NotificationCenter</span><span class="o">.</span><span class="k">default</span><span class="o">.</span><span class="nf">removeObserver</span><span class="p">(</span><span class="k">self</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="kt">Foo</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">bar</span><span class="p">()</span> <span class="p">{</span>
<span class="kt">NotificationCenter</span><span class="o">.</span><span class="k">default</span><span class="o">.</span><span class="nf">removeObserver</span><span class="p">(</span><span class="n">otherObject</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// Wrong</span>
<span class="kd">class</span> <span class="kt">Foo</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">bar</span><span class="p">()</span> <span class="p">{</span>
<span class="kt">NotificationCenter</span><span class="o">.</span><span class="k">default</span><span class="o">.</span><span class="nf">removeObserver</span><span class="p">(</span><span class="k">self</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="44-左大括号前边应有一个单独空格并与声明语句位于同一行">44. 左大括号前边应有一个单独空格,并与声明语句位于同一行</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct </span>
<span class="kd">func</span> <span class="nf">abc</span><span class="p">()</span> <span class="p">{</span>
<span class="p">}</span>
<span class="p">[]</span><span class="o">.</span><span class="nf">map</span><span class="p">()</span> <span class="p">{</span> <span class="nv">$0</span> <span class="p">}</span>
<span class="p">[]</span><span class="o">.</span><span class="nf">map</span><span class="p">({</span> <span class="p">})</span>
<span class="k">if</span> <span class="k">let</span> <span class="nv">a</span> <span class="o">=</span> <span class="n">b</span> <span class="p">{</span> <span class="p">}</span>
<span class="k">while</span> <span class="n">a</span> <span class="o">==</span> <span class="n">b</span> <span class="p">{</span> <span class="p">}</span>
<span class="k">guard</span> <span class="k">let</span> <span class="nv">a</span> <span class="o">=</span> <span class="n">b</span> <span class="k">else</span> <span class="p">{</span> <span class="p">}</span>
<span class="c1">// Wrong</span>
<span class="kd">func</span> <span class="nf">abc</span><span class="p">(){</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">abc</span><span class="p">()</span>
<span class="p">{</span> <span class="p">}</span>
<span class="p">[]</span><span class="o">.</span><span class="nf">map</span><span class="p">(){</span> <span class="nv">$0</span> <span class="p">}</span>
<span class="p">[]</span><span class="o">.</span><span class="nf">map</span><span class="p">(</span> <span class="p">{</span> <span class="p">}</span> <span class="p">)</span>
<span class="k">if</span> <span class="k">let</span> <span class="nv">a</span> <span class="o">=</span> <span class="n">b</span><span class="p">{</span> <span class="p">}</span>
<span class="k">while</span> <span class="n">a</span> <span class="o">==</span> <span class="n">b</span><span class="p">{</span> <span class="p">}</span>
<span class="k">guard</span> <span class="k">let</span> <span class="nv">a</span> <span class="o">=</span> <span class="n">b</span> <span class="k">else</span><span class="p">{</span> <span class="p">}</span>
</code></pre></div></div>
<h4 id="45-运算符左右应当各有一个空格">45. 运算符左右应当各有一个空格</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="k">let</span> <span class="nv">foo</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">+</span> <span class="mi">2</span>
<span class="k">let</span> <span class="nv">foo</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">></span> <span class="mi">2</span>
<span class="k">let</span> <span class="nv">foo</span> <span class="o">=</span> <span class="o">!</span><span class="kc">false</span>
<span class="c1">// Wrong</span>
<span class="k">let</span> <span class="nv">foo</span> <span class="o">=</span> <span class="mi">1</span><span class="o">+</span><span class="mi">2</span>
<span class="k">let</span> <span class="nv">foo</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">+</span> <span class="mi">2</span>
<span class="k">let</span> <span class="nv">foo</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">+</span> <span class="mi">2</span>
<span class="k">let</span> <span class="nv">foo</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">+</span> <span class="mi">2</span>
<span class="k">let</span> <span class="nv">foo</span><span class="o">=</span><span class="mi">1</span><span class="o">+</span><span class="mi">2</span>
<span class="k">let</span> <span class="nv">foo</span><span class="o">=</span><span class="mi">1</span> <span class="o">+</span> <span class="mi">2</span>
<span class="k">let</span> <span class="nv">foo</span><span class="o">=</span><span class="n">bar</span>
<span class="k">let</span> <span class="nv">foo</span> <span class="o">=</span> <span class="n">bar</span> <span class="p">??</span> <span class="mi">0</span>
<span class="k">let</span> <span class="nv">foo</span> <span class="o">=</span> <span class="n">bar</span><span class="p">??</span><span class="mi">0</span>
</code></pre></div></div>
<h4 id="46-在协议中声明属性时应该设置访问器的顺序为get-set">46. 在协议中声明属性时,应该设置访问器的顺序为<code class="language-plaintext highlighter-rouge">get set</code></h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="kd">protocol</span> <span class="kt">Foo</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">bar</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span> <span class="k">get</span> <span class="k">set</span> <span class="p">}</span>
<span class="p">}</span>
<span class="kd">protocol</span> <span class="kt">Foo</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">bar</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span> <span class="k">get</span> <span class="p">}</span>
<span class="p">}</span>
<span class="kd">protocol</span> <span class="kt">Foo</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">bar</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span> <span class="k">set</span> <span class="p">}</span>
<span class="p">}</span>
<span class="c1">// Wrong</span>
<span class="kd">protocol</span> <span class="kt">Foo</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">bar</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span> <span class="k">set</span> <span class="k">get</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="47-在弃置函数结果时优先使用_--foo而不是let-_--foo">47. 在弃置函数结果时,优先使用<code class="language-plaintext highlighter-rouge">_ = foo()</code>而不是<code class="language-plaintext highlighter-rouge">let _ = foo()</code></h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="n">_</span> <span class="o">=</span> <span class="nf">foo</span><span class="p">()</span>
<span class="k">if</span> <span class="k">let</span> <span class="nv">_</span> <span class="o">=</span> <span class="nf">foo</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
<span class="k">guard</span> <span class="k">let</span> <span class="nv">_</span> <span class="o">=</span> <span class="nf">foo</span><span class="p">()</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>
<span class="c1">// Wrong</span>
<span class="k">let</span> <span class="nv">_</span> <span class="o">=</span> <span class="nf">foo</span><span class="p">()</span>
<span class="k">if</span> <span class="n">_</span> <span class="o">=</span> <span class="nf">foo</span><span class="p">()</span> <span class="p">{</span> <span class="k">let</span> <span class="nv">_</span> <span class="o">=</span> <span class="nf">bar</span><span class="p">()</span> <span class="p">}</span>
</code></pre></div></div>
<h4 id="48-用nil初始化可选变量是冗余的">48. 用nil初始化可选变量是冗余的</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="k">var</span> <span class="nv">myVar</span><span class="p">:</span> <span class="kt">Int</span><span class="p">?</span>
<span class="k">let</span> <span class="nv">myVar</span><span class="p">:</span> <span class="kt">Int</span><span class="p">?</span> <span class="o">=</span> <span class="kc">nil</span>
<span class="k">var</span> <span class="nv">myVar</span><span class="p">:</span> <span class="kt">Int</span><span class="p">?</span> <span class="o">=</span> <span class="mi">0</span>
<span class="c1">// Wrong</span>
<span class="k">var</span> <span class="nv">myVar</span><span class="p">:</span> <span class="kt">Int</span><span class="p">?</span> <span class="o">=</span> <span class="kc">nil</span>
</code></pre></div></div>
<h4 id="49-当字符串枚举值与枚举名称相等时可以省略">49. 当字符串枚举值与枚举名称相等时,可以省略。</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="kd">enum</span> <span class="kt">Numbers</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">one</span>
<span class="k">case</span> <span class="n">two</span>
<span class="p">}</span>
<span class="kd">enum</span> <span class="kt">Numbers</span><span class="p">:</span> <span class="kt">Int</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">one</span> <span class="o">=</span> <span class="mi">1</span>
<span class="k">case</span> <span class="n">two</span> <span class="o">=</span> <span class="mi">2</span>
<span class="p">}</span>
<span class="kd">enum</span> <span class="kt">Numbers</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">one</span> <span class="o">=</span> <span class="s">"ONE"</span>
<span class="k">case</span> <span class="n">two</span> <span class="o">=</span> <span class="s">"TWO"</span>
<span class="p">}</span>
<span class="kd">enum</span> <span class="kt">Numbers</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">one</span> <span class="o">=</span> <span class="s">"ONE"</span>
<span class="k">case</span> <span class="n">two</span> <span class="o">=</span> <span class="s">"two"</span>
<span class="p">}</span>
<span class="kd">enum</span> <span class="kt">Numbers</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">one</span><span class="p">,</span> <span class="n">two</span>
<span class="p">}</span>
<span class="c1">// Wrong</span>
<span class="kd">enum</span> <span class="kt">Numbers</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">one</span> <span class="o">=</span> <span class="s">"one"</span>
<span class="k">case</span> <span class="n">two</span> <span class="o">=</span> <span class="s">"two"</span>
<span class="p">}</span>
<span class="kd">enum</span> <span class="kt">Numbers</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">one</span> <span class="o">=</span> <span class="s">"one"</span><span class="p">,</span> <span class="n">two</span> <span class="o">=</span> <span class="s">"two"</span>
<span class="p">}</span>
<span class="kd">enum</span> <span class="kt">Numbers</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">one</span><span class="p">,</span> <span class="n">two</span> <span class="o">=</span> <span class="s">"two"</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="50-函数声明返回void是冗余的">50. 函数声明返回Void是冗余的</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="kd">func</span> <span class="nf">foo</span><span class="p">()</span> <span class="p">{}</span>
<span class="kd">func</span> <span class="nf">foo</span><span class="p">()</span> <span class="o">-></span> <span class="kt">Int</span> <span class="p">{}</span>
<span class="c1">// Wrong</span>
<span class="kd">func</span> <span class="nf">foo</span><span class="p">()</span> <span class="o">-></span> <span class="kt">Void</span> <span class="p">{}</span>
<span class="kd">protocol</span> <span class="kt">Foo</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">foo</span><span class="p">()</span> <span class="o">-></span> <span class="kt">Void</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">foo</span><span class="p">()</span> <span class="o">-></span> <span class="p">()</span> <span class="p">{}</span>
</code></pre></div></div>
<h4 id="51-函数返回箭头和返回类型间应该用一个空格分隔或者在独立一行">51. 函数返回箭头和返回类型间应该用一个空格分隔,或者在独立一行</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="kd">func</span> <span class="nf">abc</span><span class="p">()</span> <span class="o">-></span> <span class="kt">Int</span> <span class="p">{}</span>
<span class="kd">func</span> <span class="nf">abc</span><span class="p">()</span> <span class="o">-></span> <span class="p">[</span><span class="kt">Int</span><span class="p">]</span> <span class="p">{}</span>
<span class="kd">func</span> <span class="nf">abc</span><span class="p">()</span> <span class="o">-></span>
<span class="kt">Int</span> <span class="p">{}</span>
<span class="kd">func</span> <span class="nf">abc</span><span class="p">()</span>
<span class="o">-></span> <span class="kt">Int</span> <span class="p">{}</span>
<span class="c1">// Wrong</span>
<span class="kd">func</span> <span class="nf">abc</span><span class="p">()</span><span class="o">-></span><span class="kt">Int</span> <span class="p">{}</span>
<span class="kd">func</span> <span class="nf">abc</span><span class="p">()</span><span class="o">-></span><span class="p">[</span><span class="kt">Int</span><span class="p">]</span> <span class="p">{}</span>
<span class="kd">func</span> <span class="nf">abc</span><span class="p">()</span><span class="o">-></span><span class="p">(</span><span class="kt">Int</span><span class="p">,</span> <span class="kt">Int</span><span class="p">)</span> <span class="p">{}</span>
<span class="kd">func</span> <span class="nf">abc</span><span class="p">()</span><span class="o">-></span> <span class="kt">Int</span> <span class="p">{}</span>
<span class="kd">func</span> <span class="nf">abc</span><span class="p">()</span> <span class="o">-></span><span class="kt">Int</span> <span class="p">{}</span>
<span class="kd">func</span> <span class="nf">abc</span><span class="p">()</span> <span class="o">-></span> <span class="kt">Int</span> <span class="p">{}</span>
</code></pre></div></div>
<h4 id="52-在执行运算操作和赋值时优先使用简写操作符------">52. 在执行运算操作和赋值时,优先使用简写操作符(+ =, - =,* =,/ =)</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="n">foo</span> <span class="o">-=</span> <span class="mi">1</span>
<span class="n">foo</span> <span class="o">-=</span> <span class="n">variable</span>
<span class="n">foo</span> <span class="o">-=</span> <span class="n">bar</span><span class="o">.</span><span class="nf">method</span><span class="p">()</span>
<span class="c1">// Wrong</span>
<span class="n">foo</span> <span class="o">=</span> <span class="n">foo</span> <span class="o">-</span> <span class="mi">1</span>
<span class="n">foo</span> <span class="o">=</span> <span class="n">foo</span> <span class="o">-</span> <span class="n">aVariable</span>
<span class="n">foo</span> <span class="o">=</span> <span class="n">foo</span> <span class="o">-</span> <span class="n">bar</span><span class="o">.</span><span class="nf">method</span><span class="p">()</span>
</code></pre></div></div>
<h4 id="53-else和catch应该在前一个声明的同一行上并以一个空格分隔">53. <code class="language-plaintext highlighter-rouge">else</code>和<code class="language-plaintext highlighter-rouge">catch</code>应该在前一个声明的同一行上,并以一个空格分隔</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">{</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="p">}</span> <span class="k">catch</span> <span class="p">{</span>
<span class="c1">// Wrong</span>
<span class="p">}</span><span class="k">else</span> <span class="k">if</span> <span class="p">{</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="p">}</span>
<span class="k">catch</span> <span class="p">{</span>
<span class="p">}</span>
<span class="k">catch</span> <span class="p">{</span>
</code></pre></div></div>
<h4 id="54-case语句应该与封闭的switch语句垂直对齐">54. <code class="language-plaintext highlighter-rouge">case</code>语句应该与封闭的<code class="language-plaintext highlighter-rouge">switch</code>语句垂直对齐</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="k">switch</span> <span class="n">someBool</span> <span class="p">{</span>
<span class="k">case</span> <span class="nv">true</span><span class="p">:</span> <span class="c1">// case 1</span>
<span class="nf">print</span><span class="p">(</span><span class="err">'</span><span class="n">red</span><span class="err">'</span><span class="p">)</span>
<span class="k">case</span> <span class="nv">false</span><span class="p">:</span>
<span class="cm">/*
case 2
*/</span>
<span class="k">if</span> <span class="k">case</span> <span class="kd">let</span> <span class="o">.</span><span class="nf">someEnum</span><span class="p">(</span><span class="n">val</span><span class="p">)</span> <span class="o">=</span> <span class="nf">someFunc</span><span class="p">()</span> <span class="p">{</span>
<span class="nf">print</span><span class="p">(</span><span class="err">'</span><span class="n">blue</span><span class="err">'</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">enum</span> <span class="kt">SomeEnum</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">innocent</span>
<span class="p">}</span>
<span class="k">if</span> <span class="n">aBool</span> <span class="p">{</span>
<span class="k">switch</span> <span class="n">someBool</span> <span class="p">{</span>
<span class="k">case</span> <span class="nv">true</span><span class="p">:</span>
<span class="nf">print</span><span class="p">(</span><span class="err">'</span><span class="n">red</span><span class="err">'</span><span class="p">)</span>
<span class="k">case</span> <span class="nv">false</span><span class="p">:</span>
<span class="nf">print</span><span class="p">(</span><span class="err">'</span><span class="n">blue</span><span class="err">'</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">switch</span> <span class="n">someInt</span> <span class="p">{</span>
<span class="c1">// comments ignored</span>
<span class="k">case</span> <span class="mi">0</span><span class="p">:</span>
<span class="c1">// zero case</span>
<span class="nf">print</span><span class="p">(</span><span class="err">'</span><span class="kt">Zero</span><span class="err">'</span><span class="p">)</span>
<span class="k">case</span> <span class="mi">1</span><span class="p">:</span>
<span class="nf">print</span><span class="p">(</span><span class="err">'</span><span class="kt">One</span><span class="err">'</span><span class="p">)</span>
<span class="k">default</span><span class="p">:</span>
<span class="nf">print</span><span class="p">(</span><span class="err">'</span><span class="kt">Some</span> <span class="n">other</span> <span class="n">number</span><span class="err">'</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">// Wrong</span>
<span class="k">switch</span> <span class="n">someBool</span> <span class="p">{</span>
<span class="k">case</span> <span class="nv">true</span><span class="p">:</span>
<span class="nf">print</span><span class="p">(</span><span class="err">'</span><span class="n">red</span><span class="err">'</span><span class="p">)</span>
<span class="k">case</span> <span class="nv">false</span><span class="p">:</span>
<span class="nf">print</span><span class="p">(</span><span class="err">'</span><span class="n">blue</span><span class="err">'</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">if</span> <span class="n">aBool</span> <span class="p">{</span>
<span class="k">switch</span> <span class="n">someBool</span> <span class="p">{</span>
<span class="k">case</span> <span class="nv">true</span><span class="p">:</span>
<span class="nf">print</span><span class="p">(</span><span class="err">'</span><span class="n">red</span><span class="err">'</span><span class="p">)</span>
<span class="k">case</span> <span class="nv">false</span><span class="p">:</span>
<span class="nf">print</span><span class="p">(</span><span class="err">'</span><span class="n">blue</span><span class="err">'</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">switch</span> <span class="n">someInt</span> <span class="p">{</span>
<span class="k">case</span> <span class="mi">0</span><span class="p">:</span>
<span class="nf">print</span><span class="p">(</span><span class="err">'</span><span class="kt">Zero</span><span class="err">'</span><span class="p">)</span>
<span class="k">case</span> <span class="mi">1</span><span class="p">:</span>
<span class="nf">print</span><span class="p">(</span><span class="err">'</span><span class="kt">One</span><span class="err">'</span><span class="p">)</span>
<span class="k">default</span><span class="p">:</span>
<span class="nf">print</span><span class="p">(</span><span class="err">'</span><span class="kt">Some</span> <span class="n">other</span> <span class="n">number</span><span class="err">'</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="55-应该使用速记语法糖即int而不是array">55. 应该使用速记语法糖,即[Int]而不是Array</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="k">let</span> <span class="nv">x</span><span class="p">:</span> <span class="p">[</span><span class="kt">Int</span><span class="p">]</span>
<span class="k">let</span> <span class="nv">x</span><span class="p">:</span> <span class="p">[</span><span class="kt">Int</span><span class="p">:</span> <span class="kt">String</span><span class="p">]</span>
<span class="c1">// Wrong</span>
<span class="k">let</span> <span class="nv">x</span><span class="p">:</span> <span class="kt">Array</span><span class="o"><</span><span class="kt">String</span><span class="o">></span>
<span class="k">let</span> <span class="nv">x</span><span class="p">:</span> <span class="kt">Dictionary</span><span class="o"><</span><span class="kt">Int</span><span class="p">,</span> <span class="kt">String</span><span class="o">></span>
</code></pre></div></div>
<h4 id="56-禁止在数组和字典中出现尾随逗号">56. 禁止在数组和字典中出现尾随逗号</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="k">let</span> <span class="nv">foo</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span>
<span class="k">let</span> <span class="nv">foo</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">let</span> <span class="nv">foo</span> <span class="o">=</span> <span class="p">[:]</span>
<span class="k">let</span> <span class="nv">foo</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">:</span> <span class="mi">3</span><span class="p">]</span>
<span class="c1">// Wrong</span>
<span class="k">let</span> <span class="nv">foo</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,]</span>
<span class="k">let</span> <span class="nv">foo</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="p">]</span>
<span class="k">let</span> <span class="nv">foo</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span> <span class="p">,]</span>
<span class="k">let</span> <span class="nv">foo</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span> <span class="p">]</span>
</code></pre></div></div>
<h4 id="57-每个文件应该以一个并且只有一个空行结束">57. 每个文件应该以一个并且只有一个空行结束</h4>
<h4 id="58-代码行不能有尾随分号">58. 代码行不能有尾随分号</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="k">let</span> <span class="nv">a</span> <span class="o">=</span> <span class="mi">0</span>
<span class="c1">// Wrong</span>
<span class="k">let</span> <span class="nv">a</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">let</span> <span class="nv">a</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">let</span> <span class="nv">b</span> <span class="o">=</span> <span class="mi">1</span>
<span class="k">let</span> <span class="nv">a</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;;</span>
<span class="k">let</span> <span class="nv">a</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="p">;;</span>
<span class="k">let</span> <span class="nv">a</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="p">;</span> <span class="p">;</span>
</code></pre></div></div>
<h4 id="59-行末不能有空格">59. 行末不能有空格</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="k">let</span> <span class="nv">name</span><span class="p">:</span> <span class="kt">String</span>
<span class="c1">//</span>
<span class="k">let</span> <span class="nv">name</span><span class="p">:</span> <span class="kt">String</span> <span class="c1">//</span>
<span class="c1">// Wrong</span>
<span class="k">let</span> <span class="nv">name</span><span class="p">:</span> <span class="kt">String</span> <span class="err">←</span><span class="n">这里有空格</span>
</code></pre></div></div>
<h4 id="60-单个类型定义长度不能超过500行如单个类代码行数不能超过500行">60. 单个类型定义长度不能超过500行,如单个类代码行数不能超过500行</h4>
<h4 id="70-类型名称只能包含字母数字字符以大写字母开头长度介于3到40个字符之间">70. 类型名称只能包含字母数字字符,以大写字母开头,长度介于3到40个字符之间</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="kd">class</span> <span class="kt">MyType</span> <span class="p">{}</span>
<span class="kd">class</span> <span class="kt">AAAAAAAAAAAAAAAAAAAAAAAAAAAAA</span> <span class="p">{}</span>
<span class="kd">struct</span> <span class="kt">MyType</span> <span class="p">{}</span>
<span class="c1">// Wrong</span>
<span class="kd">class</span> <span class="n">myType</span> <span class="p">{}</span>
<span class="kd">class</span> <span class="n">_MyType</span> <span class="p">{}</span>
<span class="kd">private</span> <span class="kd">class</span> <span class="kt">MyType_</span> <span class="p">{}</span>
<span class="kd">class</span> <span class="kt">My</span> <span class="p">{}</span>
<span class="kd">class</span> <span class="kt">AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</span> <span class="p">{}</span>
<span class="kd">struct</span> <span class="n">myType</span> <span class="p">{}</span>
</code></pre></div></div>
<h4 id="71-避免使用不需要的break语句">71. 避免使用不需要的<code class="language-plaintext highlighter-rouge">break</code>语句</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="k">switch</span> <span class="n">foo</span> <span class="p">{</span>
<span class="k">case</span> <span class="o">.</span><span class="nv">bar</span><span class="p">:</span>
<span class="k">break</span>
<span class="p">}</span>
<span class="k">switch</span> <span class="n">foo</span> <span class="p">{</span>
<span class="k">default</span><span class="p">:</span>
<span class="k">break</span>
<span class="p">}</span>
<span class="k">switch</span> <span class="n">foo</span> <span class="p">{</span>
<span class="k">case</span> <span class="o">.</span><span class="nv">bar</span><span class="p">:</span>
<span class="k">for</span> <span class="n">i</span> <span class="k">in</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span> <span class="p">{</span> <span class="k">break</span> <span class="p">}</span>
<span class="p">}</span>
<span class="k">switch</span> <span class="n">foo</span> <span class="p">{</span>
<span class="k">case</span> <span class="o">.</span><span class="nv">bar</span><span class="p">:</span>
<span class="k">if</span> <span class="kc">true</span> <span class="p">{</span> <span class="k">break</span> <span class="p">}</span>
<span class="p">}</span>
<span class="k">switch</span> <span class="n">foo</span> <span class="p">{</span>
<span class="k">case</span> <span class="o">.</span><span class="nv">bar</span><span class="p">:</span>
<span class="nf">something</span><span class="p">()</span>
<span class="p">}</span>
<span class="c1">// Wrong</span>
<span class="k">switch</span> <span class="n">foo</span> <span class="p">{</span>
<span class="k">case</span> <span class="o">.</span><span class="nv">bar</span><span class="p">:</span>
<span class="nf">something</span><span class="p">()</span>
<span class="err">↓</span><span class="k">break</span>
<span class="p">}</span>
<span class="k">switch</span> <span class="n">foo</span> <span class="p">{</span>
<span class="k">case</span> <span class="o">.</span><span class="nv">bar</span><span class="p">:</span>
<span class="nf">something</span><span class="p">()</span>
<span class="err">↓</span><span class="k">break</span> <span class="c1">// comment</span>
<span class="p">}</span>
<span class="k">switch</span> <span class="n">foo</span> <span class="p">{</span>
<span class="k">default</span><span class="p">:</span>
<span class="nf">something</span><span class="p">()</span>
<span class="err">↓</span><span class="k">break</span>
<span class="p">}</span>
<span class="k">switch</span> <span class="n">foo</span> <span class="p">{</span>
<span class="k">case</span> <span class="o">.</span><span class="n">foo</span><span class="p">,</span> <span class="o">.</span><span class="n">foo2</span> <span class="k">where</span> <span class="nv">condition</span><span class="p">:</span>
<span class="nf">something</span><span class="p">()</span>
<span class="err">↓</span><span class="k">break</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="72-闭包中未使用的参数应该用_替换">72. 闭包中未使用的参数应该用<code class="language-plaintext highlighter-rouge">_</code>替换</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="nv">$0</span> <span class="o">+</span> <span class="mi">1</span> <span class="p">}</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="n">number</span> <span class="k">in</span>
<span class="n">number</span> <span class="o">+</span> <span class="mi">1</span>
<span class="p">}</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="n">_</span> <span class="k">in</span>
<span class="mi">3</span>
<span class="p">}</span>
<span class="c1">// Wrong</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="n">number</span> <span class="k">in</span>
<span class="k">return</span> <span class="mi">3</span>
<span class="p">}</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="n">number</span> <span class="k">in</span>
<span class="k">return</span> <span class="n">numberWithSuffix</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="73-当index或item未使用时enumerated可以删除">73. 当index或item未使用时,<code class="language-plaintext highlighter-rouge">.enumerated()</code>可以删除</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 推荐写法</span>
<span class="k">for</span> <span class="p">(</span><span class="n">idx</span><span class="p">,</span> <span class="n">foo</span><span class="p">)</span> <span class="k">in</span> <span class="n">bar</span><span class="o">.</span><span class="nf">enumerated</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
<span class="k">for</span> <span class="p">(</span><span class="n">_</span><span class="p">,</span> <span class="n">foo</span><span class="p">)</span> <span class="k">in</span> <span class="n">bar</span><span class="o">.</span><span class="nf">enumerated</span><span class="p">()</span><span class="o">.</span><span class="nf">something</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
<span class="k">for</span> <span class="p">(</span><span class="n">_</span><span class="p">,</span> <span class="n">foo</span><span class="p">)</span> <span class="k">in</span> <span class="n">bar</span><span class="o">.</span><span class="nf">something</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
<span class="k">for</span> <span class="n">foo</span> <span class="k">in</span> <span class="n">bar</span><span class="o">.</span><span class="nf">enumerated</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
<span class="k">for</span> <span class="n">foo</span> <span class="k">in</span> <span class="n">bar</span> <span class="p">{</span> <span class="p">}</span>
<span class="k">for</span> <span class="p">(</span><span class="n">idx</span><span class="p">,</span> <span class="n">_</span><span class="p">)</span> <span class="k">in</span> <span class="n">bar</span><span class="o">.</span><span class="nf">enumerated</span><span class="p">()</span><span class="o">.</span><span class="nf">something</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
<span class="k">for</span> <span class="p">(</span><span class="n">idx</span><span class="p">,</span> <span class="n">_</span><span class="p">)</span> <span class="k">in</span> <span class="n">bar</span><span class="o">.</span><span class="nf">something</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
<span class="k">for</span> <span class="n">idx</span> <span class="k">in</span> <span class="n">bar</span><span class="o">.</span><span class="n">indices</span> <span class="p">{</span> <span class="p">}</span>
<span class="c1">// 不推荐写法</span>
<span class="k">for</span> <span class="p">(</span><span class="n">_</span><span class="p">,</span> <span class="n">foo</span><span class="p">)</span> <span class="k">in</span> <span class="n">bar</span><span class="o">.</span><span class="nf">enumerated</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
<span class="k">for</span> <span class="p">(</span><span class="n">_</span><span class="p">,</span> <span class="n">foo</span><span class="p">)</span> <span class="k">in</span> <span class="n">abc</span><span class="o">.</span><span class="n">bar</span><span class="o">.</span><span class="nf">enumerated</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
<span class="k">for</span> <span class="p">(</span><span class="n">_</span><span class="p">,</span> <span class="n">foo</span><span class="p">)</span> <span class="k">in</span> <span class="n">abc</span><span class="o">.</span><span class="nf">something</span><span class="p">()</span><span class="o">.</span><span class="nf">enumerated</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
<span class="k">for</span> <span class="p">(</span><span class="n">idx</span><span class="p">,</span> <span class="n">_</span><span class="p">)</span> <span class="k">in</span> <span class="n">bar</span><span class="o">.</span><span class="nf">enumerated</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</code></pre></div></div>
<h4 id="74-可选绑定值不使用时用-nil优于let-_-">74. 可选绑定值不使用时,用<code class="language-plaintext highlighter-rouge">!= nil</code>优于<code class="language-plaintext highlighter-rouge">let _ =</code></h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 推荐写法</span>
<span class="k">if</span> <span class="k">let</span> <span class="nv">bar</span> <span class="o">=</span> <span class="kt">Foo</span><span class="o">.</span><span class="n">optionalValue</span> <span class="p">{</span>
<span class="p">}</span>
<span class="k">if</span> <span class="k">let</span> <span class="p">(</span><span class="nv">_</span><span class="p">,</span> <span class="nv">second</span><span class="p">)</span> <span class="o">=</span> <span class="nf">getOptionalTuple</span><span class="p">()</span> <span class="p">{</span>
<span class="p">}</span>
<span class="k">if</span> <span class="k">let</span> <span class="p">(</span><span class="nv">_</span><span class="p">,</span> <span class="nv">asd</span><span class="p">,</span> <span class="nv">_</span><span class="p">)</span> <span class="o">=</span> <span class="nf">getOptionalTuple</span><span class="p">(),</span> <span class="k">let</span> <span class="nv">bar</span> <span class="o">=</span> <span class="kt">Foo</span><span class="o">.</span><span class="n">optionalValue</span> <span class="p">{</span>
<span class="p">}</span>
<span class="k">if</span> <span class="nf">foo</span><span class="p">()</span> <span class="p">{</span> <span class="k">let</span> <span class="nv">_</span> <span class="o">=</span> <span class="nf">bar</span><span class="p">()</span> <span class="p">}</span>
<span class="k">if</span> <span class="nf">foo</span><span class="p">()</span> <span class="p">{</span> <span class="n">_</span> <span class="o">=</span> <span class="nf">bar</span><span class="p">()</span> <span class="p">}</span>
<span class="k">if</span> <span class="k">case</span> <span class="o">.</span><span class="nf">some</span><span class="p">(</span><span class="n">_</span><span class="p">)</span> <span class="o">=</span> <span class="k">self</span> <span class="p">{}</span>
<span class="k">if</span> <span class="k">let</span> <span class="nv">point</span> <span class="o">=</span> <span class="n">state</span><span class="o">.</span><span class="nf">find</span><span class="p">({</span> <span class="n">_</span> <span class="k">in</span> <span class="kc">true</span> <span class="p">})</span> <span class="p">{}</span>
<span class="c1">// 不推荐写法</span>
<span class="k">if</span> <span class="kd">let</span> <span class="err">↓</span><span class="n">_</span> <span class="o">=</span> <span class="kt">Foo</span><span class="o">.</span><span class="n">optionalValue</span> <span class="p">{</span>
<span class="p">}</span>
<span class="k">if</span> <span class="k">let</span> <span class="nv">a</span> <span class="o">=</span> <span class="kt">Foo</span><span class="o">.</span><span class="n">optionalValue</span><span class="p">,</span> <span class="kd">let</span> <span class="err">↓</span><span class="n">_</span> <span class="o">=</span> <span class="kt">Foo</span><span class="o">.</span><span class="n">optionalValue2</span> <span class="p">{</span>
<span class="p">}</span>
<span class="k">guard</span> <span class="k">let</span> <span class="nv">a</span> <span class="o">=</span> <span class="kt">Foo</span><span class="o">.</span><span class="n">optionalValue</span><span class="p">,</span> <span class="kd">let</span> <span class="err">↓</span><span class="n">_</span> <span class="o">=</span> <span class="kt">Foo</span><span class="o">.</span><span class="n">optionalValue2</span> <span class="p">{</span>
<span class="p">}</span>
<span class="k">if</span> <span class="k">let</span> <span class="p">(</span><span class="nv">first</span><span class="p">,</span> <span class="nv">second</span><span class="p">)</span> <span class="o">=</span> <span class="nf">getOptionalTuple</span><span class="p">(),</span> <span class="kd">let</span> <span class="err">↓</span><span class="n">_</span> <span class="o">=</span> <span class="kt">Foo</span><span class="o">.</span><span class="n">optionalValue</span> <span class="p">{</span>
<span class="p">}</span>
<span class="k">if</span> <span class="k">let</span> <span class="p">(</span><span class="nv">first</span><span class="p">,</span> <span class="nv">_</span><span class="p">)</span> <span class="o">=</span> <span class="nf">getOptionalTuple</span><span class="p">(),</span> <span class="kd">let</span> <span class="err">↓</span><span class="n">_</span> <span class="o">=</span> <span class="kt">Foo</span><span class="o">.</span><span class="n">optionalValue</span> <span class="p">{</span>
<span class="p">}</span>
<span class="k">if</span> <span class="k">let</span> <span class="p">(</span><span class="nv">_</span><span class="p">,</span> <span class="nv">second</span><span class="p">)</span> <span class="o">=</span> <span class="nf">getOptionalTuple</span><span class="p">(),</span> <span class="kd">let</span> <span class="err">↓</span><span class="n">_</span> <span class="o">=</span> <span class="kt">Foo</span><span class="o">.</span><span class="n">optionalValue</span> <span class="p">{</span>
<span class="p">}</span>
<span class="k">if</span> <span class="kd">let</span> <span class="err">↓</span><span class="p">(</span><span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">)</span> <span class="o">=</span> <span class="nf">getOptionalTuple</span><span class="p">(),</span> <span class="k">let</span> <span class="nv">bar</span> <span class="o">=</span> <span class="kt">Foo</span><span class="o">.</span><span class="n">optionalValue</span> <span class="p">{</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">foo</span><span class="p">()</span> <span class="p">{</span>
<span class="k">if</span> <span class="kd">let</span> <span class="err">↓</span><span class="n">_</span> <span class="o">=</span> <span class="n">bar</span> <span class="p">{</span>
<span class="p">}</span>
<span class="k">if</span> <span class="k">case</span> <span class="o">.</span><span class="nf">some</span><span class="p">(</span><span class="kd">let</span> <span class="err">↓</span><span class="n">_</span><span class="p">)</span> <span class="o">=</span> <span class="k">self</span> <span class="p">{}</span>
</code></pre></div></div>
<h4 id="75-ibinspectable只应用于变量其类型是显式的并且是受支持的类型">75. @IBInspectable只应用于变量,其类型是显式的,并且是受支持的类型</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="kd">class</span> <span class="kt">Foo</span> <span class="p">{</span>
<span class="kd">@IBInspectable</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">x</span><span class="p">:</span> <span class="kt">Int</span>
<span class="p">}</span>
<span class="c1">// Wrong</span>
<span class="kd">class</span> <span class="kt">Foo</span> <span class="p">{</span>
<span class="kd">@IBInspectable</span> <span class="kd">private</span> <span class="k">let</span> <span class="nv">count</span><span class="p">:</span> <span class="kt">Int</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="76-如果函数参数在声明中位于多行中则应垂直对齐">76. 如果函数参数在声明中位于多行中,则应垂直对齐</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="kd">func</span> <span class="nf">validateFunction</span><span class="p">(</span><span class="n">_</span> <span class="nv">file</span><span class="p">:</span> <span class="kt">File</span><span class="p">,</span> <span class="nv">kind</span><span class="p">:</span> <span class="kt">SwiftDeclarationKind</span><span class="p">,</span>
<span class="nv">dictionary</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">SourceKitRepresentable</span><span class="p">])</span> <span class="p">{</span> <span class="p">}</span>
<span class="kd">func</span> <span class="nf">foo</span><span class="p">(</span><span class="nv">data</span><span class="p">:</span> <span class="p">(</span><span class="nv">size</span><span class="p">:</span> <span class="kt">CGSize</span><span class="p">,</span>
<span class="nv">identifier</span><span class="p">:</span> <span class="kt">String</span><span class="p">))</span> <span class="p">{}</span>
<span class="kd">func</span> <span class="nf">validateFunction</span><span class="p">(</span>
<span class="n">_</span> <span class="nv">file</span><span class="p">:</span> <span class="kt">File</span><span class="p">,</span> <span class="nv">kind</span><span class="p">:</span> <span class="kt">SwiftDeclarationKind</span><span class="p">,</span>
<span class="nv">dictionary</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">SourceKitRepresentable</span><span class="p">])</span> <span class="o">-></span> <span class="p">[</span><span class="kt">StyleViolation</span><span class="p">]</span>
<span class="c1">// Wrong</span>
<span class="kd">func</span> <span class="nf">validateFunction</span><span class="p">(</span><span class="n">_</span> <span class="nv">file</span><span class="p">:</span> <span class="kt">File</span><span class="p">,</span> <span class="nv">kind</span><span class="p">:</span> <span class="kt">SwiftDeclarationKind</span><span class="p">,</span>
<span class="err">↓</span><span class="nv">dictionary</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">SourceKitRepresentable</span><span class="p">])</span> <span class="p">{</span> <span class="p">}</span>
<span class="kd">func</span> <span class="nf">validateFunction</span><span class="p">(</span><span class="n">_</span> <span class="nv">file</span><span class="p">:</span> <span class="kt">File</span><span class="p">,</span> <span class="nv">kind</span><span class="p">:</span> <span class="kt">SwiftDeclarationKind</span><span class="p">,</span>
<span class="err">↓</span><span class="nv">dictionary</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">SourceKitRepresentable</span><span class="p">])</span> <span class="p">{</span> <span class="p">}</span>
<span class="kd">func</span> <span class="nf">validateFunction</span><span class="p">(</span><span class="n">_</span> <span class="nv">file</span><span class="p">:</span> <span class="kt">File</span><span class="p">,</span>
<span class="err">↓</span><span class="nv">kind</span><span class="p">:</span> <span class="kt">SwiftDeclarationKind</span><span class="p">,</span>
<span class="err">↓</span><span class="nv">dictionary</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">SourceKitRepresentable</span><span class="p">])</span> <span class="p">{</span> <span class="p">}</span>
</code></pre></div></div>
<h4 id="77-调用函数时如果参数位于多行则应垂直对齐">77. 调用函数时如果参数位于多行,则应垂直对齐</h4>
<h4 id="78-垂直空白分隔仅限单个空行">78. 垂直空白分隔仅限单个空行</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="k">let</span> <span class="nv">abc</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">let</span> <span class="nv">cba</span> <span class="o">=</span> <span class="mi">0</span>
<span class="cm">/* bcs
*/</span>
<span class="c1">// bca </span>
<span class="c1">// Wrong</span>
<span class="k">let</span> <span class="nv">aaaa</span> <span class="o">=</span> <span class="mi">0</span>
<span class="err">↑</span> <span class="mi">2</span><span class="n">个空行</span>
<span class="kd">struct</span> <span class="kt">AAAA</span> <span class="p">{}</span>
<span class="err">↑</span> <span class="mi">3</span><span class="n">个空行</span>
</code></pre></div></div>
<h4 id="79-使用--void而不是--">79. 使用<code class="language-plaintext highlighter-rouge">-> Void</code>而不是<code class="language-plaintext highlighter-rouge">-> ()</code></h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="k">let</span> <span class="nv">abc</span><span class="p">:</span> <span class="p">()</span> <span class="o">-></span> <span class="kt">Void</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">let</span> <span class="nv">abc</span><span class="p">:</span> <span class="p">()</span> <span class="o">-></span> <span class="p">(</span><span class="kt">VoidVoid</span><span class="p">)</span> <span class="o">=</span> <span class="p">{}</span>
<span class="kd">func</span> <span class="nf">foo</span><span class="p">(</span><span class="nv">completion</span><span class="p">:</span> <span class="p">()</span> <span class="o">-></span> <span class="kt">Void</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">foo</span><span class="p">:</span> <span class="p">(</span><span class="kt">ConfigurationTests</span><span class="p">)</span> <span class="o">-></span> <span class="p">()</span> <span class="k">throws</span> <span class="o">-></span> <span class="kt">Void</span><span class="p">)</span>
<span class="c1">// Wrong</span>
<span class="k">let</span> <span class="nv">abc</span><span class="p">:</span> <span class="p">()</span> <span class="o">-></span> <span class="err">↓</span><span class="p">()</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">let</span> <span class="nv">abc</span><span class="p">:</span> <span class="p">()</span> <span class="o">-></span> <span class="err">↓</span><span class="p">(</span><span class="kt">Void</span><span class="p">)</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">let</span> <span class="nv">abc</span><span class="p">:</span> <span class="p">()</span> <span class="o">-></span> <span class="err">↓</span><span class="p">(</span> <span class="kt">Void</span> <span class="p">)</span> <span class="o">=</span> <span class="p">{}</span>
<span class="kd">func</span> <span class="nf">foo</span><span class="p">(</span><span class="nv">completion</span><span class="p">:</span> <span class="p">()</span> <span class="o">-></span> <span class="err">↓</span><span class="p">())</span>
<span class="kd">func</span> <span class="nf">foo</span><span class="p">(</span><span class="nv">completion</span><span class="p">:</span> <span class="p">()</span> <span class="o">-></span> <span class="err">↓</span><span class="p">(</span> <span class="p">))</span>
<span class="kd">func</span> <span class="nf">foo</span><span class="p">(</span><span class="nv">completion</span><span class="p">:</span> <span class="p">()</span> <span class="o">-></span> <span class="err">↓</span><span class="p">(</span><span class="kt">Void</span><span class="p">))</span>
<span class="k">let</span> <span class="nv">foo</span><span class="p">:</span> <span class="p">(</span><span class="kt">ConfigurationTests</span><span class="p">)</span> <span class="o">-></span> <span class="p">()</span> <span class="k">throws</span> <span class="o">-></span> <span class="err">↓</span><span class="p">())</span>
</code></pre></div></div>
<h4 id="80-delegate应该为weak来避免循环引用">80. Delegate应该为weak来避免循环引用</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="kd">class</span> <span class="kt">Foo</span> <span class="p">{</span>
<span class="k">weak</span> <span class="k">var</span> <span class="nv">delegate</span><span class="p">:</span> <span class="kt">SomeProtocol</span><span class="p">?</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="kt">Foo</span> <span class="p">{</span>
<span class="k">weak</span> <span class="k">var</span> <span class="nv">someDelegate</span><span class="p">:</span> <span class="kt">SomeDelegateProtocol</span><span class="p">?</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="kt">Foo</span> <span class="p">{</span>
<span class="k">weak</span> <span class="k">var</span> <span class="nv">delegateScroll</span><span class="p">:</span> <span class="kt">ScrollDelegate</span><span class="p">?</span>
<span class="p">}</span>
<span class="c1">// Wrong</span>
<span class="kd">class</span> <span class="kt">Foo</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">delegate</span><span class="p">:</span> <span class="kt">SomeProtocol</span><span class="p">?</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="kt">Foo</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">scrollDelegate</span><span class="p">:</span> <span class="kt">ScrollDelegate</span><span class="p">?</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="81-使用可选绑定而不是强制解包">81. 使用可选绑定而不是强制解包</h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="k">if</span> <span class="k">let</span> <span class="nv">constantName</span> <span class="o">=</span> <span class="n">someOptional</span> <span class="p">{</span> <span class="p">}</span>
<span class="c1">// Wrong</span>
<span class="k">let</span> <span class="nv">constants</span> <span class="o">=</span> <span class="n">someOptional</span><span class="o">!</span>
</code></pre></div></div>
<h4 id="82-使用可选绑定语法if-let-value-来判空而不是if-value--nil">82. 使用可选绑定语法<code class="language-plaintext highlighter-rouge">if let value =</code>来判空而不是<code class="language-plaintext highlighter-rouge">if value != nil</code></h4>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Correct</span>
<span class="k">if</span> <span class="k">let</span> <span class="nv">value</span> <span class="o">=</span> <span class="n">result</span> <span class="p">{</span>
<span class="nf">print</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">// Wrong</span>
<span class="k">if</span> <span class="n">value</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
<span class="nf">print</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>Hisoka根据SwiftLint的规则自己撸了一份Swift编码规范。整个顺序都是按照SwiftLint的顺序,基本上属于搬运。面向协议编程(Protocol Oriented Programming)的网络层代码简述2018-03-01T00:00:00+00:002018-03-01T00:00:00+00:00https://hisoka0917.github.io/swift/2018/03/01/pop-network-layer<p>在WWDC2015上苹果介绍了面向协议编程(Protocol Oriented Programming)这一思想在Swift上的应用。这给了我们一种新的思路。接下来我们尝试用POP的思想来构建一个网络层。</p>
<p>首先我们定义一个Request协议</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">enum</span> <span class="kt">HTTPMethod</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span>
<span class="k">case</span> <span class="kt">GET</span>
<span class="k">case</span> <span class="kt">POST</span>
<span class="p">}</span>
<span class="kd">protocol</span> <span class="kt">Request</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">path</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span> <span class="k">get</span> <span class="p">}</span>
<span class="k">var</span> <span class="nv">method</span><span class="p">:</span> <span class="kt">HTTPMethod</span> <span class="p">{</span> <span class="k">get</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>这是一个最原始的Request协议。显然这个协议还缺少很多东西。比如说还需要参数和返回数据的处理。那么在这里我们使用Swift4的Codable协议来定义Request所需的参数以及返回数据的解析。完善一下上面的协议。</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">protocol</span> <span class="kt">Parsable</span> <span class="p">{</span>
<span class="kd">associatedtype</span> <span class="kt">Result</span><span class="p">:</span> <span class="kt">Decodable</span>
<span class="kd">static</span> <span class="kd">func</span> <span class="nf">parse</span><span class="p">(</span><span class="nv">data</span><span class="p">:</span> <span class="kt">Data</span><span class="p">)</span> <span class="k">throws</span> <span class="o">-></span> <span class="kt">Result</span><span class="p">?</span>
<span class="p">}</span>
<span class="kd">protocol</span> <span class="kt">Request</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">path</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span> <span class="k">get</span> <span class="p">}</span>
<span class="k">var</span> <span class="nv">method</span><span class="p">:</span> <span class="kt">HTTPMethod</span> <span class="p">{</span> <span class="k">get</span> <span class="p">}</span>
<span class="k">var</span> <span class="nv">query</span><span class="p">:</span> <span class="kt">Query</span><span class="p">?</span> <span class="p">{</span> <span class="k">get</span> <span class="k">set</span> <span class="p">}</span>
<span class="kd">associatedtype</span> <span class="kt">Response</span><span class="p">:</span> <span class="kt">Parsable</span>
<span class="kd">associatedtype</span> <span class="kt">Query</span><span class="p">:</span> <span class="kt">Encodable</span>
<span class="p">}</span>
</code></pre></div></div>
<p>这里我们使用了<code class="language-plaintext highlighter-rouge">associatedtype</code>来让协议实现者自己来定义类型。</p>
<p>接下来我们需要一个发送Request的对象,于是定义一个Client。</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">enum</span> <span class="kt">ClientError</span><span class="p">:</span> <span class="kt">Error</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">requestFailed</span>
<span class="k">case</span> <span class="n">jsonConversionFailure</span>
<span class="k">case</span> <span class="n">invalidData</span>
<span class="k">case</span> <span class="n">responseUnsuccessful</span>
<span class="k">case</span> <span class="n">jsonParsingFailure</span>
<span class="p">}</span>
<span class="kd">protocol</span> <span class="kt">Client</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">host</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span> <span class="k">get</span> <span class="p">}</span>
<span class="kd">func</span> <span class="n">fetch</span><span class="o"><</span><span class="kt">R</span><span class="o">></span><span class="p">(</span><span class="n">_</span> <span class="nv">req</span><span class="p">:</span> <span class="kt">R</span><span class="p">,</span> <span class="nv">handler</span><span class="p">:</span> <span class="kd">@escaping</span> <span class="p">(</span><span class="kt">R</span><span class="o">.</span><span class="kt">Response</span><span class="o">.</span><span class="kt">Result</span><span class="p">?,</span> <span class="kt">ClientError</span><span class="p">?)</span> <span class="o">-></span> <span class="kt">Void</span><span class="p">)</span> <span class="k">where</span> <span class="kt">R</span><span class="p">:</span> <span class="kt">Request</span>
<span class="p">}</span>
</code></pre></div></div>
<p>在Client协议里面定义了一个<code class="language-plaintext highlighter-rouge">host</code>变量和一个<code class="language-plaintext highlighter-rouge">fetch</code>方法,这个<code class="language-plaintext highlighter-rouge">fetch</code>方法接收一个<code class="language-plaintext highlighter-rouge">Request</code>类型的参数,完成之后异步回调<code class="language-plaintext highlighter-rouge">handler</code>。</p>
<p>然后来实现Client协议。</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">URLSessionClient</span><span class="p">:</span> <span class="kt">Client</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">host</span> <span class="o">=</span> <span class="s">"http://gank.io/"</span>
<span class="kd">func</span> <span class="n">fetch</span><span class="o"><</span><span class="kt">R</span><span class="o">></span><span class="p">(</span><span class="n">_</span> <span class="nv">req</span><span class="p">:</span> <span class="kt">R</span><span class="p">,</span> <span class="nv">handler</span><span class="p">:</span> <span class="kd">@escaping</span> <span class="p">(</span><span class="kt">R</span><span class="o">.</span><span class="kt">Response</span><span class="o">.</span><span class="kt">Result</span><span class="p">?,</span> <span class="kt">ClientError</span><span class="p">?)</span> <span class="o">-></span> <span class="kt">Void</span><span class="p">)</span> <span class="k">where</span> <span class="kt">R</span><span class="p">:</span> <span class="kt">Request</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">url</span> <span class="o">=</span> <span class="kt">URL</span><span class="p">(</span><span class="nv">string</span><span class="p">:</span> <span class="k">self</span><span class="o">.</span><span class="n">host</span><span class="o">.</span><span class="nf">appending</span><span class="p">(</span><span class="n">req</span><span class="o">.</span><span class="n">path</span><span class="p">))</span><span class="o">!</span>
<span class="k">var</span> <span class="nv">request</span> <span class="o">=</span> <span class="kt">URLRequest</span><span class="p">(</span><span class="nv">url</span><span class="p">:</span> <span class="n">url</span><span class="p">)</span>
<span class="n">request</span><span class="o">.</span><span class="n">httpMethod</span> <span class="o">=</span> <span class="n">req</span><span class="o">.</span><span class="n">method</span><span class="o">.</span><span class="n">rawValue</span>
<span class="k">let</span> <span class="nv">task</span> <span class="o">=</span> <span class="kt">URLSession</span><span class="o">.</span><span class="n">shared</span><span class="o">.</span><span class="nf">dataTask</span><span class="p">(</span><span class="nv">with</span><span class="p">:</span> <span class="n">request</span><span class="p">)</span> <span class="p">{</span> <span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">response</span><span class="p">,</span> <span class="n">_</span><span class="p">)</span> <span class="k">in</span>
<span class="k">guard</span> <span class="k">let</span> <span class="nv">httpResponse</span> <span class="o">=</span> <span class="n">response</span> <span class="k">as?</span> <span class="kt">HTTPURLResponse</span> <span class="k">else</span> <span class="p">{</span>
<span class="nf">handler</span><span class="p">(</span><span class="kc">nil</span><span class="p">,</span> <span class="o">.</span><span class="n">requestFailed</span><span class="p">)</span>
<span class="k">return</span>
<span class="p">}</span>
<span class="k">if</span> <span class="n">httpResponse</span><span class="o">.</span><span class="n">statusCode</span> <span class="o">==</span> <span class="mi">200</span> <span class="p">{</span>
<span class="kt">DispatchQueue</span><span class="o">.</span><span class="n">main</span><span class="o">.</span><span class="n">async</span> <span class="p">{</span>
<span class="k">if</span> <span class="k">let</span> <span class="nv">data</span> <span class="o">=</span> <span class="n">data</span> <span class="p">{</span>
<span class="k">do</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">res</span> <span class="o">=</span> <span class="k">try</span> <span class="kt">R</span><span class="o">.</span><span class="kt">Response</span><span class="o">.</span><span class="nf">parse</span><span class="p">(</span><span class="nv">data</span><span class="p">:</span> <span class="n">data</span><span class="p">)</span>
<span class="nf">handler</span><span class="p">(</span><span class="n">res</span><span class="p">,</span> <span class="kc">nil</span><span class="p">)</span>
<span class="p">}</span> <span class="k">catch</span> <span class="p">{</span>
<span class="nf">handler</span><span class="p">(</span><span class="kc">nil</span><span class="p">,</span> <span class="o">.</span><span class="n">jsonConversionFailure</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nf">handler</span><span class="p">(</span><span class="kc">nil</span><span class="p">,</span> <span class="o">.</span><span class="n">invalidData</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nf">handler</span><span class="p">(</span><span class="kc">nil</span><span class="p">,</span> <span class="o">.</span><span class="n">responseUnsuccessful</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">task</span><span class="o">.</span><span class="nf">resume</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>这里使用原生的URLSession来实现一个Client。当然这里可以用任何的网络框架。那么这里的一个关键步骤就是使用<code class="language-plaintext highlighter-rouge">Request</code>协议里的<code class="language-plaintext highlighter-rouge">Response</code>属性的<code class="language-plaintext highlighter-rouge">parse()</code>方法来解析网络返回的数据。</p>
<p>实现了Client以后我们需要实现Request和Response。</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">ArticleModel</span><span class="p">:</span> <span class="kt">Codable</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">_id</span><span class="p">:</span> <span class="kt">String</span>
<span class="k">var</span> <span class="nv">createdAt</span><span class="p">:</span> <span class="kt">String</span>
<span class="k">var</span> <span class="nv">desc</span><span class="p">:</span> <span class="kt">String</span>
<span class="k">var</span> <span class="nv">publishedAt</span><span class="p">:</span> <span class="kt">String</span>
<span class="k">var</span> <span class="nv">images</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">]?</span>
<span class="k">var</span> <span class="nv">source</span><span class="p">:</span> <span class="kt">String</span>
<span class="k">var</span> <span class="nv">type</span><span class="p">:</span> <span class="kt">String</span>
<span class="k">var</span> <span class="nv">url</span><span class="p">:</span> <span class="kt">String</span>
<span class="k">var</span> <span class="nv">used</span><span class="p">:</span> <span class="kt">Bool</span>
<span class="k">var</span> <span class="nv">who</span><span class="p">:</span> <span class="kt">String</span><span class="p">?</span>
<span class="p">}</span>
<span class="kd">struct</span> <span class="kt">ArticleResponseModel</span><span class="p">:</span> <span class="kt">Codable</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">error</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">?</span>
<span class="k">var</span> <span class="nv">results</span><span class="p">:</span> <span class="p">[</span><span class="kt">ArticleModel</span><span class="p">]?</span>
<span class="p">}</span>
<span class="kd">struct</span> <span class="kt">ArticleResponse</span><span class="p">:</span> <span class="kt">Parsable</span> <span class="p">{</span>
<span class="kd">typealias</span> <span class="kt">Result</span> <span class="o">=</span> <span class="kt">ArticleResponseModel</span>
<span class="kd">static</span> <span class="kd">func</span> <span class="nf">parse</span><span class="p">(</span><span class="nv">data</span><span class="p">:</span> <span class="kt">Data</span><span class="p">)</span> <span class="k">throws</span> <span class="o">-></span> <span class="kt">ArticleResponseModel</span><span class="p">?</span> <span class="p">{</span>
<span class="k">do</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">responseJson</span> <span class="o">=</span> <span class="k">try</span> <span class="kt">JSONDecoder</span><span class="p">()</span><span class="o">.</span><span class="nf">decode</span><span class="p">(</span><span class="kt">Result</span><span class="o">.</span><span class="k">self</span><span class="p">,</span> <span class="nv">from</span><span class="p">:</span> <span class="n">data</span><span class="p">)</span>
<span class="k">return</span> <span class="n">responseJson</span>
<span class="p">}</span> <span class="k">catch</span> <span class="p">{</span>
<span class="k">throw</span> <span class="kt">ClientError</span><span class="o">.</span><span class="n">jsonParsingFailure</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">struct</span> <span class="kt">ArticleRequest</span><span class="p">:</span> <span class="kt">Request</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">path</span><span class="p">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="s">"api/data/iOS/20/2"</span>
<span class="k">var</span> <span class="nv">method</span><span class="p">:</span> <span class="kt">HTTPMethod</span> <span class="o">=</span> <span class="o">.</span><span class="kt">GET</span>
<span class="k">var</span> <span class="nv">query</span><span class="p">:</span> <span class="kt">Query</span><span class="p">?</span>
<span class="kd">typealias</span> <span class="kt">Response</span> <span class="o">=</span> <span class="kt">ArticleResponse</span>
<span class="kd">typealias</span> <span class="kt">Query</span> <span class="o">=</span> <span class="kt">String</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">ArticleResponse</code>遵守<code class="language-plaintext highlighter-rouge">Parsable</code>协议,定义了<code class="language-plaintext highlighter-rouge">Result</code>的类型为<code class="language-plaintext highlighter-rouge">ArticleResponseModel</code>。并且实现了<code class="language-plaintext highlighter-rouge">parse()</code>方法,这里使用<code class="language-plaintext highlighter-rouge">JSONDecoder</code>来进行数据解析。</p>
<p>在<code class="language-plaintext highlighter-rouge">ArticleRequest</code>里面定义Response的类型为<code class="language-plaintext highlighter-rouge">ArticleResponse</code>。</p>
<p>接下来我们只需要构造Request示例并传递给Client就行了。很多时候我们喜欢用一个<code class="language-plaintext highlighter-rouge">RequestManager</code>来干这个事情。</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">enum</span> <span class="kt">FetchResult</span><span class="o"><</span><span class="kt">T</span><span class="p">,</span> <span class="kt">U</span><span class="o">></span> <span class="k">where</span> <span class="kt">U</span><span class="p">:</span> <span class="kt">Error</span> <span class="p">{</span>
<span class="k">case</span> <span class="nf">success</span><span class="p">(</span><span class="kt">T</span><span class="p">)</span>
<span class="k">case</span> <span class="nf">failure</span><span class="p">(</span><span class="kt">U</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="kt">RequestManager</span> <span class="p">{</span>
<span class="kd">static</span> <span class="kd">func</span> <span class="n">fetch</span><span class="o"><</span><span class="kt">R</span><span class="o">></span><span class="p">(</span><span class="n">_</span> <span class="nv">request</span><span class="p">:</span> <span class="kt">R</span><span class="p">,</span>
<span class="nv">handler</span><span class="p">:</span> <span class="kd">@escaping</span> <span class="p">(</span><span class="kt">FetchResult</span><span class="o"><</span><span class="kt">R</span><span class="o">.</span><span class="kt">Response</span><span class="o">.</span><span class="kt">Result</span><span class="p">,</span> <span class="kt">ClientError</span><span class="o">></span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span><span class="p">)</span> <span class="k">where</span> <span class="kt">R</span><span class="p">:</span> <span class="kt">Request</span> <span class="p">{</span>
<span class="kt">URLSessionClient</span><span class="p">()</span><span class="o">.</span><span class="nf">fetch</span><span class="p">(</span><span class="n">request</span><span class="p">)</span> <span class="p">{</span> <span class="p">(</span><span class="n">result</span><span class="p">,</span> <span class="n">error</span><span class="p">)</span> <span class="k">in</span>
<span class="k">if</span> <span class="k">let</span> <span class="nv">result</span> <span class="o">=</span> <span class="n">result</span> <span class="p">{</span>
<span class="nf">handler</span><span class="p">(</span><span class="kt">FetchResult</span><span class="o">.</span><span class="nf">success</span><span class="p">(</span><span class="n">result</span><span class="p">))</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nf">handler</span><span class="p">(</span><span class="kt">FetchResult</span><span class="o">.</span><span class="nf">failure</span><span class="p">(</span><span class="o">.</span><span class="n">jsonParsingFailure</span><span class="p">))</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>到这里为止整个网络层的基本框架已经完成。在遵循了面向协议编程的思想之后现在的这个网络层弹性非常好。如果不喜欢用URLSession,可以整个替换掉,使用Alamofire或者别的你喜欢的网络框架,只需要遵守Client协议就行。Response的parse方法的实现也可以替换掉。Request的Query部分都是对象化操作,很方便。</p>
<p>查看完整的<a href="https://github.com/hisoka0917/POPNetworkExample">示例代码</a></p>Hisoka在WWDC2015上苹果介绍了面向协议编程(Protocol Oriented Programming)这一思想在Swift上的应用。这给了我们一种新的思路。接下来我们尝试用POP的思想来构建一个网络层。shadowsocks-libv使用ss-manager实现多用户使用2018-01-19T00:00:00+00:002018-01-19T00:00:00+00:00https://hisoka0917.github.io/shadowsocks/2018/01/19/shadowsocks-multiuser-with-ssmanager<p>shadowsocks目前只有libv版本还在持续更新并支持AEAD加密算法。但是libv版本本身不支持多用户使用。不过ss-manager可以实现这一目的。以下使用CentOS 7作为例子来实现多用户使用。</p>
<ol>
<li>
<p>安装shadowsocks-libv,这一步的教程很多,就不重复了。</p>
</li>
<li>
<p>编辑配置文件</p>
</li>
</ol>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$vim</span> /etc/shadowsocks/config.json
<span class="o">{</span>
<span class="s2">"server"</span>:[<span class="s2">"::0"</span>,<span class="s2">"0.0.0.0"</span><span class="o">]</span>,
<span class="s2">"port_password"</span>: <span class="o">{</span>
<span class="s2">"8848"</span>: <span class="s2">"password1"</span>,
<span class="s2">"8838"</span>: <span class="s2">"password2"</span>
<span class="o">}</span>,
<span class="s2">"timeout"</span>:60,
<span class="s2">"method"</span>:<span class="s2">"chacha20-ietf-poly1305"</span>,
<span class="s2">"fast_open"</span>:true
<span class="o">}</span>
</code></pre></div></div>
<p>配置文件写法和以前的多端口写法相同。</p>
<ol>
<li>创建系统服务</li>
</ol>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$vim</span> /etc/systemd/system/ssmanager.service
<span class="o">[</span>Unit]
<span class="nv">Description</span><span class="o">=</span>Shadowsocks Manager Server
<span class="o">[</span>Service]
<span class="nv">User</span><span class="o">=</span>root
<span class="nv">Group</span><span class="o">=</span>root
<span class="nv">Type</span><span class="o">=</span>simple
<span class="nv">ExecStart</span><span class="o">=</span>/usr/local/bin/ss-manager <span class="nt">--manager-address</span> /var/run/shadowsocks-manager.sock <span class="nt">-c</span> /etc/shadowsocks/config.json <span class="nt">-a</span> shadowsocks start
<span class="nv">ExecStop</span><span class="o">=</span>/usr/local/bin/ss-manager <span class="nt">--manager-address</span> /var/run/shadowsocks-manager.sock <span class="nt">-c</span> /etc/shadowsocks/config.json <span class="nt">-a</span> shadowsocks stop
<span class="o">[</span>Install]
<span class="nv">WantedBy</span><span class="o">=</span>multi-user.target
</code></pre></div></div>
<p>保存文件。</p>
<ol>
<li>设置开机启动</li>
</ol>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemctl <span class="nb">enable </span>ssmanager
systemctl start ssmanager
</code></pre></div></div>
<p>配置好防火墙就可以使用了,而且之前配置的shadowsocks服务可以停用了。</p>Hisokashadowsocks目前只有libv版本还在持续更新并支持AEAD加密算法。但是libv版本本身不支持多用户使用。不过ss-manager可以实现这一目的。以下使用CentOS 7作为例子来实现多用户使用。npm在升级之后报错2017-12-29T03:06:00+00:002017-12-29T03:06:00+00:00https://hisoka0917.github.io/nodejs/2017/12/29/npm-error-after-upgrade<p>今天在执行npm命令的时候报错,如下所示:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ npm -v
module.js:557
throw err;
^
Error: Cannot find module '../lib/utils/unsupported.js'
at Function.Module._resolveFilename (module.js:555:15)
at Function.Module._load (module.js:482:25)
at Module.require (module.js:604:17)
at require (internal/module.js:11:18)
at /usr/local/lib/node_modules/npm/bin/npm-cli.js:19:21
at Object.<anonymous> (/usr/local/lib/node_modules/npm/bin/npm-cli.js:92:3)
at Module._compile (module.js:660:30)
at Object.Module._extensions..js (module.js:671:10)
at Module.load (module.js:573:32)
at tryModuleLoad (module.js:513:12)
</code></pre></div></div>
<p>搜了一下发现是因为之前手贱执行了一下<code class="language-plaintext highlighter-rouge">brew upgrade</code>,结果就这样了。</p>
<p>解决方法是<code class="language-plaintext highlighter-rouge">rm -rf /usr/local/lib/node_modules/</code> 然后重新安装node&npm。</p>Hisoka今天在执行npm命令的时候报错,如下所示:OpenLDAP用户和组的类型2017-12-26T08:50:00+00:002017-12-26T08:50:00+00:00https://hisoka0917.github.io/linux/2017/12/26/openldap-user-group<p>前几篇文章讲了OpenLDAP的一些安装配置。在使用OpenLDAP的过程当中也是踩了一些坑,主要是LDAP用户的类型。</p>
<p>我们使用OpenVPN的时候使用OpenLDAP来进行鉴权,根据前文的配置,基本是可以使用的。不过我们希望把那些可以使用vpn的用户加到一个组里,这样方便管理。于是我们建了一个<code class="language-plaintext highlighter-rouge">cn=vpn</code>的组,然后编辑<code class="language-plaintext highlighter-rouge">memberUid</code>把某几个用户加进来。然后编辑<code class="language-plaintext highlighter-rouge">ldap.conf</code></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><Authorization>
# Base DN
BaseDN "ou=People,dc=example,dc=com"
# User Search Filter
SearchFilter "(uid=%u)"
# Require Group Membership
RequireGroup true
# Add non-group members to a PF table (disabled)
#PFTable ips_vpn_users
<Group>
BaseDN "ou=Group,dc=example,dc=com"
SearchFilter "(cn=vpn)"
MemberAttribute memberUid
# Add group members to a PF table (disabled)
#PFTable ips_vpn_eng
</Group>
</Authorization>
</code></pre></div></div>
<p>完成配置后重启服务器,尝试使用已加到这个组里的用户登录,结果返回结果验证失败。</p>
<p>通过几次尝试以及使用不同的ldap客户端终于找到了原因。我们之前创建用户和组都是使用的<code class="language-plaintext highlighter-rouge">phpldapadmin</code>,并且创建的时候选择了<code class="language-plaintext highlighter-rouge">Generic: User Account</code>,创建组的时候选择了<code class="language-plaintext highlighter-rouge">Generic: Posix Group</code>。这样创建出来的用户的<code class="language-plaintext highlighter-rouge">objectClass</code>值是<code class="language-plaintext highlighter-rouge">top, posixAccount, inetOrgPerson</code>,创建的组的<code class="language-plaintext highlighter-rouge">objectClass</code>是<code class="language-plaintext highlighter-rouge">top, posixGroup</code>,组里面关联用户的key是<code class="language-plaintext highlighter-rouge">memberUid</code>。</p>
<p>使用别的ldap客户端创建的用户<code class="language-plaintext highlighter-rouge">objectClass</code>是<code class="language-plaintext highlighter-rouge">top, person, organizationalPerson, inetOrgPerson</code>,组的<code class="language-plaintext highlighter-rouge">objectClass</code>是<code class="language-plaintext highlighter-rouge">top, groupOfNames</code>,以及组里面关联用户的key是<code class="language-plaintext highlighter-rouge">member</code>。当我们使用别的客户端新建了<code class="language-plaintext highlighter-rouge">groupOfNames</code>类型的组,并且把原来的用户加到<code class="language-plaintext highlighter-rouge">member</code>属性下。在ldap.conf文件中也把组相关的配置改成<code class="language-plaintext highlighter-rouge">MemberAttribute member</code>。再次尝试连接成功。</p>
<p>这里面可能是和<code class="language-plaintext highlighter-rouge">openvpn-auth-ldap</code>这个插件有关,在现在这种情况下只能这种类型的组才能完成验证。在phpldapadmin里面这种组叫做<code class="language-plaintext highlighter-rouge">Samba: Group Mapping</code>。</p>
<p>由于之前一直是使用phpldapadmin所以没在意这种类型为题。这提醒我要好好组织一下ldap的目录结构以及每个节点的类型。objectClass的类型在和Windows AD做关联的时候也许也是很重要的。</p>Hisoka前几篇文章讲了OpenLDAP的一些安装配置。在使用OpenLDAP的过程当中也是踩了一些坑,主要是LDAP用户的类型。