-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathatom.xml
891 lines (823 loc) · 303 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>移动开发</title>
<subtitle>Stay hungry. Stay foolish.</subtitle>
<link href="/atom.xml" rel="self"/>
<link href="http://yoursite.com/"/>
<updated>2016-03-06T13:47:10.000Z</updated>
<id>http://yoursite.com/</id>
<author>
<name>移动开发</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>iOS技术周刊第2期</title>
<link href="http://yoursite.com/2016/03/06/ios-technology-magazine-2/"/>
<id>http://yoursite.com/2016/03/06/ios-technology-magazine-2/</id>
<published>2016-03-05T16:00:00.000Z</published>
<updated>2016-03-06T13:47:10.000Z</updated>
<content type="html"><p>本周重点在性能优化和Hybrid方面,包含<code>AsyncDisplayKit</code>的实践,RN相关等,另外App开发人员也应该了解一些API设计,本期重点介绍API建模工具RAML,希望大家能够有所收获。</p>
<a id="more"></a>
<h2 id="沪江原创"><a href="#沪江原创" class="headerlink" title="沪江原创"></a>沪江原创</h2><ul>
<li><a href="http://hujiangtechnology.github.io/2016/03/05/asyncdisplaykit-practice/" target="_blank" rel="external">深度实践 AsyncDisplayKit</a></li>
<li><a href="http://hujiangtechnology.github.io/2016/03/06/using-raml-build-api-doc/#Examples" target="_blank" rel="external">用RAML来构建API文档</a></li>
</ul>
<h2 id="Hybrid"><a href="#Hybrid" class="headerlink" title="Hybrid"></a>Hybrid</h2><ul>
<li><a href="http://www.cocoachina.com/ios/20150811/12985.html" target="_blank" rel="external">UIWebView与JS的深度交互</a>,禁用获取的HTML文本中自带的 &lt; img &gt; 标签自动加载,并把下载图片的操作放在native端来处理,提高响应速度并且节省用户流量,同时也能在本地对图片各种操作,一个不错的交互实践。</li>
<li><a href="https://github.com/bang590/JSPatch/wiki/JSPatch-%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86%E8%AF%A6%E8%A7%A3" target="_blank" rel="external">JSPatch 实现原理详解</a></li>
<li><a href="https://github.com/ele828/react-native-guide#%E5%B7%A5%E5%85%B7" target="_blank" rel="external">React-Native各类学习资源</a>,资料很全面,包含开源app,以及一些工具和组件。</li>
</ul>
<h2 id="性能优化"><a href="#性能优化" class="headerlink" title="性能优化"></a>性能优化</h2><ul>
<li><a href="http://blog.ibireme.com/2015/11/12/smooth_user_interfaces_for_ios/#more-41893" target="_blank" rel="external">iOS 保持界面流畅的技巧</a>,很详细的分析了界面卡顿的原因,以及对应的解决方法的研究方向(<em>异步绘制</em>,<em>预排版</em>,<em>预渲染</em>,<em>全局并发控制</em>)和实践,相当不错哦,值得你拥有。</li>
<li><a href="http://mp.weixin.qq.com/s?__biz=MjM5NTIyNTUyMQ==&amp;mid=447105405&amp;idx=1&amp;sn=054dc54289a98e8a39f2b9386f4f620e&amp;scene=23&amp;srcid=0108RhyzhXk9wUwQvnW3cmZT#rd" target="_blank" rel="external">内存恶鬼drawRect - 谈画图功能的内存优化</a></li>
</ul>
<h2 id="网络"><a href="#网络" class="headerlink" title="网络"></a>网络</h2><ul>
<li><a href="http://www.jianshu.com/p/cc756016243b" target="_blank" rel="external">iOS的TCP/IP协议族剖析&amp;&amp;Socket</a>,主要介绍了TCP/IP的工作原理以及Socket的原理和实现细节。</li>
</ul>
<h2 id="绘图"><a href="#绘图" class="headerlink" title="绘图"></a>绘图</h2><ul>
<li><a href="http://ciechanowski.me/" target="_blank" rel="external">神奇的Transform</a>,看看我们可以通过transform来做点什么,简直太好玩了,不服来看😏😏</li>
</ul>
<h2 id="工具"><a href="#工具" class="headerlink" title="工具"></a>工具</h2><ul>
<li><a href="https://github.com/apple/swift/tree/master/benchmark" target="_blank" rel="external">Swift基准测试套件</a>,包含基准测试驱动以及显示性能的一些度量,协助开发者创建更快更高效的代码。</li>
<li><a href="http://blog.cnbluebox.com/blog/2015/03/05/chisel/" target="_blank" rel="external">Chisel-LLDB命令插件,让调试更Easy</a></li>
<li><a href="http://camsong.github.io/redux-in-chinese/index.html" target="_blank" rel="external">Redux可预测化的状态管理</a></li>
</ul>
<h2 id="后花园"><a href="#后花园" class="headerlink" title="后花园"></a>后花园</h2><ul>
<li><a href="http://mp.weixin.qq.com/s?__biz=MjM5MDE0Mjc4MA==&amp;mid=402973995&amp;idx=1&amp;sn=87625ad8a3e3d5b16baa0890d6eedfe8&amp;scene=23&amp;srcid=03050zf9BxGthj6HoRiTCmb3#rd" target="_blank" rel="external">程序员VS武林高手:技术为外功,思维乃内力</a>,真气雄浑,滚滚不可测,吐气如剑,拈花摘叶,老司机今天带你重新认识当代武林高手-程序员。</li>
<li>大妈喊你还债了,<a href="http://mp.weixin.qq.com/s?__biz=MjM5MDE0Mjc4MA==&amp;mid=402964742&amp;idx=1&amp;sn=60a657a4eeca714bee80e861406c6443&amp;scene=23&amp;srcid=0305FCtWZQFUXz2Xx4PePzCx#rd" target="_blank" rel="external">年前挖的坑都填了吗?技术债务偿还计划</a>😄😄</li>
<li><a href="http://www.jianshu.com/p/ff4a34a5a136" target="_blank" rel="external">你不可错过的50部电影清单!(附评分、推荐语)</a></li>
<li><a href="https://mp.weixin.qq.com/s?__biz=MzAwMDU1MTE1OQ==&amp;mid=404242829&amp;idx=1&amp;sn=aacddf1c2c828281e6202eff8cd374f5&amp;scene=1&amp;srcid=0302Q1dT8nUezoEZLZzistUl&amp;key=710a5d99946419d96e1c7481a2d8dae6400769cbc8bc9015cca38f9694d8c1ae46c0ff571ec714de57c149dd6aef6796&amp;ascene=0&amp;uin=Mzg1OTg2NQ%3D%3D" target="_blank" rel="external">并发之痛 Thread,Goroutine,Actor</a></li>
</ul>
</content>
<summary type="html">
<p>本周重点在性能优化和Hybrid方面,包含<code>AsyncDisplayKit</code>的实践,RN相关等,另外App开发人员也应该了解一些API设计,本期重点介绍API建模工具RAML,希望大家能够有所收获。</p>
</summary>
<category term="iOS开发" scheme="http://yoursite.com/categories/iOS%E5%BC%80%E5%8F%91/"/>
<category term="iOS技术周刊" scheme="http://yoursite.com/tags/iOS%E6%8A%80%E6%9C%AF%E5%91%A8%E5%88%8A/"/>
</entry>
<entry>
<title>用RAML构建API文档</title>
<link href="http://yoursite.com/2016/03/06/using-raml-build-api-doc/"/>
<id>http://yoursite.com/2016/03/06/using-raml-build-api-doc/</id>
<published>2016-03-05T16:00:00.000Z</published>
<updated>2016-03-06T13:47:17.000Z</updated>
<content type="html"><p>App开发人员应当了解的一些API知识,熟悉编写API文档的一些工具和方法。</p>
<a id="more"></a>
<h2 id="RESTful-API"><a href="#RESTful-API" class="headerlink" title="RESTful API"></a>RESTful API</h2><p>全称:Representational State Transfer(表现层状态转化)</p>
<p>其实我们常见的GET, POST等的API都属于这类,RESTful有以下几个关键概念:</p>
<ul>
<li>一种设计风格而不是标准,只是提供了一组设计原则和约束条件</li>
<li>资源,用一个URI(统一资源定位符)指向它,每种资源对应一个特定的URI</li>
<li>Method<ul>
<li>GET:用来获取资源</li>
<li>POST用来新建资源(也可以用于更新资源)</li>
<li>PUT用来更新资源</li>
<li>DELETE用来删除资源</li>
</ul>
</li>
</ul>
<h2 id="RAML是啥?"><a href="#RAML是啥?" class="headerlink" title="RAML是啥?"></a>RAML是啥?</h2><p><a href="http://raml.org/" target="_blank" rel="external">RAML</a>是一个API建模语言,完全遵守RESTful风格,有严格的语法来约束,采用<a href="http://www.yaml.org/" target="_blank" rel="external">yaml</a>描述,可以通过一系列工具转化为HTML,Wiki等文档样式。</p>
<h2 id="开始写一个API"><a href="#开始写一个API" class="headerlink" title="开始写一个API"></a>开始写一个API</h2><h3 id="先装个工具"><a href="#先装个工具" class="headerlink" title="先装个工具"></a>先装个工具</h3><p>采用Atom插件 <a href="http://apiworkbench.com/" target="_blank" rel="external">API Workbench</a> 打造一个可视化工具,自动化提示,语法检测等功能,一应俱全,非常适合编写RAML。</p>
<p><img src="api-workbench.png" alt="API Workbench主要功能"></p>
<h3 id="熟读基本语法"><a href="#熟读基本语法" class="headerlink" title="熟读基本语法"></a>熟读基本语法</h3><p><a href="https://github.com/raml-org/raml-spec/blob/master/raml-0.8.md" target="_blank" rel="external">点击查看</a></p>
<h3 id="创建API工程"><a href="#创建API工程" class="headerlink" title="创建API工程"></a>创建API工程</h3><p>本文例子采用v0.8版本,目前1.0版本配套的工具还不太完善,等后续更新。</p>
<ul>
<li>API 头部</li>
</ul>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#%RAML 0.8</span></span><br><span class="line"><span class="attr">title:</span> A6</span><br><span class="line"><span class="attr">version:</span> v2</span><br><span class="line"><span class="attr">baseUri:</span> http://&#123;apiDomain&#125;.hujiang.com/&#123;version&#125;</span><br><span class="line"><span class="attr">baseUriParameters:</span></span><br><span class="line"><span class="attr"> apiDomain:</span></span><br><span class="line"><span class="attr"> description:</span> <span class="string">|</span><br><span class="line"> The sub-domain at which the API is accessible.</span><br><span class="line"></span><span class="attr"> enum:</span> [ <span class="string">"test"</span> , <span class="string">"yz.test"</span>, <span class="string">"qa.test"</span> ]</span><br><span class="line"><span class="attr">mediaType:</span> application/json</span><br><span class="line"><span class="attr">protocols:</span> [ HTTP, HTTPS ]</span><br><span class="line"><span class="attr">documentation:</span></span><br><span class="line"><span class="attr"> - title:</span> 类型定义</span><br><span class="line"><span class="attr"> content:</span> !include documentation/type-definition.md</span><br><span class="line"><span class="attr">resourceTypes:</span></span><br><span class="line"><span class="attr"> - base:</span> !include resourceTypes/base.raml</span><br><span class="line"><span class="attr"> - collection:</span> !include resourceTypes/collection.raml</span><br><span class="line"><span class="attr">schemas:</span></span><br><span class="line"><span class="attr"> - test:</span> !include schemas/test-schema.json</span><br></pre></td></tr></table></figure>
<ul>
<li>API 实体</li>
</ul>
<p>有几个关键词: <code>type</code>, <code>uriParameters</code>, <code>require</code>, <code>queryParameters</code>, <code>displayName</code>, <code>description</code>,具体可参看文档 </p>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">/test/&#123;uriParam1&#125;/&#123;uriParam2&#125;:</span><br><span class="line"><span class="attr">type:</span> collection</span><br><span class="line"><span class="attr"> displayName:</span> 获取xxxxx</span><br><span class="line"><span class="attr"> uriParameters:</span></span><br><span class="line"><span class="attr"> screenWidth:</span></span><br><span class="line"><span class="attr"> required:</span> <span class="literal">true</span></span><br><span class="line"><span class="attr"> displayName:</span> uriParam1</span><br><span class="line"><span class="attr"> description:</span> uriParam1说明</span><br><span class="line"><span class="attr"> type:</span> integer</span><br><span class="line"><span class="attr"> screenHeight:</span></span><br><span class="line"><span class="attr"> required:</span> <span class="literal">true</span></span><br><span class="line"><span class="attr"> displayName:</span> uriParam2</span><br><span class="line"><span class="attr"> description:</span> uriParam2说明</span><br><span class="line"><span class="attr"> type:</span> integer</span><br><span class="line"><span class="attr"> get:</span></span><br><span class="line"><span class="attr"> description:</span> 获取xx内容</span><br><span class="line"><span class="attr"> queryParameters:</span></span><br><span class="line"><span class="attr"> param1:</span></span><br><span class="line"><span class="attr"> required:</span> <span class="literal">true</span></span><br><span class="line"><span class="attr"> displayName:</span> param1</span><br><span class="line"><span class="attr"> description:</span> param1说明</span><br><span class="line"><span class="attr"> type:</span> string</span><br><span class="line"><span class="attr"> responses:</span></span><br><span class="line"> <span class="number">200</span>:</span><br><span class="line"><span class="attr"> body:</span></span><br><span class="line"> application/json:</span><br><span class="line"><span class="attr"> schema:</span> test</span><br><span class="line"><span class="attr"> example:</span> !include examples/test-example.json</span><br></pre></td></tr></table></figure>
<h3 id="ResourceTypes"><a href="#ResourceTypes" class="headerlink" title="ResourceTypes"></a>ResourceTypes</h3><p>资源类型统一描述,常见的统一定义HTTP Header,示例如下:</p>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">get?:</span><br><span class="line"><span class="attr"> headers:</span></span><br><span class="line"><span class="attr"> Hujiang-Agent:</span></span><br><span class="line"><span class="attr"> description:</span> <span class="string">|</span><br><span class="line"> 统一的沪江Agent定义</span><br><span class="line"></span><span class="attr"> required:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure>
<h3 id="Documentation"><a href="#Documentation" class="headerlink" title="Documentation"></a>Documentation</h3><p>API的一些说明性文档,例如某些特定值的定义和说明,注意事项等</p>
<h3 id="Schemas"><a href="#Schemas" class="headerlink" title="Schemas"></a>Schemas</h3><p>定义返回结构中的数据定义,示例:</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line"> <span class="attr">"required"</span> : <span class="literal">true</span> ,</span><br><span class="line"> <span class="attr">"$schema"</span> : <span class="string">"http://json-schema.org/draft-03/schema"</span> ,</span><br><span class="line"> <span class="attr">"type"</span> : <span class="string">"array"</span> ,</span><br><span class="line"> <span class="attr">"items"</span> : &#123;</span><br><span class="line"> <span class="attr">"type"</span> : <span class="string">"object"</span>,</span><br><span class="line"> <span class="attr">"properties"</span> : &#123;</span><br><span class="line"> <span class="attr">"adType"</span> : &#123;</span><br><span class="line"> <span class="attr">"type"</span> : <span class="string">"integer"</span> ,</span><br><span class="line"> <span class="attr">"description"</span>: <span class="string">"参看广告类型定义"</span>,</span><br><span class="line"> <span class="attr">"required"</span> : <span class="literal">true</span></span><br><span class="line"> &#125;,</span><br><span class="line"> <span class="attr">"adId"</span> : &#123;</span><br><span class="line"> <span class="attr">"type"</span> : <span class="string">"integer"</span> ,</span><br><span class="line"> <span class="attr">"description"</span> : <span class="string">"广告位ID"</span>,</span><br><span class="line"> <span class="attr">"required"</span> : <span class="literal">false</span></span><br><span class="line"> &#125;,</span><br><span class="line"> <span class="attr">"adItemList"</span> : &#123;</span><br><span class="line"> <span class="attr">"type"</span> : <span class="string">"array"</span> ,</span><br><span class="line"> <span class="attr">"description"</span> : <span class="string">"广告条列表,最大不超过6个"</span>,</span><br><span class="line"> <span class="attr">"required"</span> : <span class="literal">true</span> ,</span><br><span class="line"> <span class="attr">"items"</span> : &#123;</span><br><span class="line"> <span class="attr">"type"</span> : <span class="string">"object"</span> ,</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>另外在API头部可以统一include,方便后续API实体中直接调用。</p>
<h3 id="Examples"><a href="#Examples" class="headerlink" title="Examples"></a>Examples</h3><p>根据上述的Schemas,提供一个API返回示例,便于用户更好地了解API,示例如下:</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line"> &#123;</span><br><span class="line"> <span class="attr">"adType"</span> : <span class="number">31</span>,</span><br><span class="line"> <span class="attr">"adId"</span> : <span class="number">10001</span>,</span><br><span class="line"> <span class="attr">"adItemList"</span> : [</span><br><span class="line"> &#123;</span><br><span class="line"> <span class="attr">"adItemId"</span> : <span class="number">1021021</span>,</span><br><span class="line"> <span class="attr">"title"</span> : <span class="string">"广告条标题"</span>,</span><br><span class="line"> <span class="attr">"summary"</span> : <span class="string">"广告条描述"</span>,</span><br><span class="line"> <span class="attr">"actionType"</span> : <span class="number">1</span>,</span><br><span class="line"> <span class="attr">"actionUrl"</span> : <span class="string">"http://www.hujiang.com"</span>,</span><br><span class="line"> <span class="attr">"startTime"</span> : <span class="string">"2016-2-29T17:54:46.203+08:00"</span>,</span><br><span class="line"> <span class="attr">"endTime"</span> : <span class="string">"2016-3-29T17:54:46.203+08:00"</span>,</span><br><span class="line"> <span class="attr">"imageUrl"</span> : <span class="string">"http://i2.w.hjfile.cn/news/201602/201602247003009941.jpg"</span>,</span><br><span class="line"> <span class="attr">"orderIndex"</span> : <span class="number">2</span>,</span><br><span class="line"> <span class="attr">"fillColor"</span> : <span class="number">1023123123123</span>,</span><br><span class="line"> <span class="attr">"scheme"</span> : <span class="string">"hujiang://"</span>,</span><br><span class="line"> <span class="attr">"appIdentifier"</span> : <span class="string">"com.hujiang.normandy"</span></span><br><span class="line"> &#125;</span><br><span class="line"> ]</span><br><span class="line"> &#125;,</span><br><span class="line"> ... ...</span><br><span class="line"> </span><br><span class="line"> &#125;</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<h3 id="生成HTML或wiki"><a href="#生成HTML或wiki" class="headerlink" title="生成HTML或wiki"></a>生成HTML或wiki</h3><p>基本差不多了,就要以找一些工具转成用户可以直观看到的文档,可以使用如下工具:</p>
<p>raml2html: <a href="https://github.com/raml2html/raml2html" target="_blank" rel="external">https://github.com/raml2html/raml2html</a><br>raml2wiki: <a href="https://github.com/jhitchcock/raml2wiki" target="_blank" rel="external">https://github.com/jhitchcock/raml2wiki</a></p>
<h2 id="它还可以做什么"><a href="#它还可以做什么" class="headerlink" title="它还可以做什么"></a>它还可以做什么</h2><p>既然是建模语言,所有的API定义,返回值都非常方便结构化,利用这些数据我们可以自制mock server,将API整个流程打通,非常期待大家的参与。</p>
<h2 id="RAML-vs-Swagger-vs-API-Blueprint"><a href="#RAML-vs-Swagger-vs-API-Blueprint" class="headerlink" title="RAML vs. Swagger vs. API Blueprint"></a>RAML vs. Swagger vs. API Blueprint</h2><p><a href="http://www.mikestowe.com/2014/07/raml-vs-swagger-vs-api-blueprint.php" target="_blank" rel="external">mikestowe</a>上有专门针对这三类做了比较,最后综合得分RAML排首位,当然大家也要结合当下团队实力,选择合适的工具</p>
<h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul>
<li><a href="http://raml.org/" target="_blank" rel="external">RAML 官网</a></li>
<li><a href="https://atom.io/" target="_blank" rel="external">atom 官网</a></li>
<li><a href="https://bourgeois.me/rest/" target="_blank" rel="external">REST best practices</a></li>
<li><a href="https://codeplanet.io/principles-good-restful-api-design/" target="_blank" rel="external">Principles of good RESTful API Design</a></li>
<li><a href="http://www.yaml.org/" target="_blank" rel="external">YAML官网</a></li>
<li><a href="http://www.mikestowe.com/2014/07/raml-vs-swagger-vs-api-blueprint.php" target="_blank" rel="external">RAML vs. Swagger vs. API Blueprint</a></li>
</ul>
</content>
<summary type="html">
<p>App开发人员应当了解的一些API知识,熟悉编写API文档的一些工具和方法。</p>
</summary>
<category term="API" scheme="http://yoursite.com/categories/API/"/>
<category term="基础" scheme="http://yoursite.com/tags/%E5%9F%BA%E7%A1%80/"/>
</entry>
<entry>
<title>深度实践 AsyncDisplayKit</title>
<link href="http://yoursite.com/2016/03/05/asyncdisplaykit-practice/"/>
<id>http://yoursite.com/2016/03/05/asyncdisplaykit-practice/</id>
<published>2016-03-04T16:00:00.000Z</published>
<updated>2016-03-06T11:18:40.000Z</updated>
<content type="html"><p>追求极致的用户体验,从来都是我们锲而不舍的追求,对于iOS用户而言,这更是容不得一点马虎。随着时间的推移,现如今,谁还能忍受得了一个页面打开后,半天没有结果😭,出来之后滑动卡顿,点击个按钮半天木有反应啊,有木有?还好,还有<code>Facebook</code>,这位互联网IT界的大佬,为我们带来了福音,它就是我们今天要讲的<code>AsyncDisplayKit</code>。</p>
<a id="more"></a>
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p><code>AsyncDisplayKit</code> 可以给我们带来很棒的用户体验,主要是通过优化以下3点:</p>
<ul>
<li>图像解码</li>
<li>页面布局</li>
<li>页面元素渲染</li>
</ul>
<p>通过把以上几项操作放在后台线程,从而避免了阻塞用户主线程。所以,通过这个库,如果使用得当,即使是复杂的页面布局我们仍可以获得丝滑般的无限接近60FPS的页面滚动体验,这一点,通过常规的<code>UIKit</code>优化一般是达不到这个效果的😏。</p>
<p>在本文中,我们通过一个开始的初级项目(主要是使用了<code>UICollectionView</code>,有一些滚动体验不佳),通过使用<code>AsyncDisplayKit</code>来优化提升它的性能。跟着我走,你将会学会如何在你自己的项目中使用 <code>AsyncDisplayKit</code> 。</p>
<p>&gt;<br>注意: 在开始之前, 你最好对 Swift, Core Animation 和 Core Graphics这些知识已经有所了解.</p>
<h2 id="开始了"><a href="#开始了" class="headerlink" title="开始了"></a>开始了</h2><p>开始之前,如果有时间的话,你最好看一下<a href="https://code.facebook.com/posts/721586784561674/introducing-AsyncDisplayKit-for-smooth-and-responsive-apps-on-ios/" target="_blank" rel="external">AsyncDisplayKit的介绍</a>,这样你会对 <code>AsyncDisplayKit</code>的主要功能有一个初步的认识。项目源码<em>(EMAIL ME:[email protected])</em>,先跑起来看看(需要Xcode 6.3 和 the iOS 8.3 SDK 以上环境)。为了能比较明显的看到使用这个库的差异,最好在老一点的设备上运行,如果是模拟器,看到的性能提升不明显,你懂的😏。运行起来,看起来是这样的:<br><img src="http://cdn4.raywenderlich.com/wp-content/uploads/2014/10/IMG_0002.png" alt=""><br>像你现在看到的,这个app的主页面是一个众多动物卡片组成的一个<code>UICollectionView</code>,每一个卡片包含了一张图片,一条文字描述,以及一个由主图片经过模糊处理的背景。<br></p>
<p> 滚动一下这个页面,注意它的帧率,(我使用的是iPad 3,大概只有20FPS),失帧很严重啊,所以你的直观触觉就是界面很不流畅,卡顿实实在在的。好吧,在这篇文章结束的时候,我们的目标就是要把它搞到(或者无限接近)60FPS。<br></p>
<p> &gt;<br> 注意: 这个项目里你看到的所有图片都是放在本地地资源文件里面的,没有一个是从网络下载来的。</p>
<h2 id="测试响应能力"><a href="#测试响应能力" class="headerlink" title="测试响应能力"></a>测试响应能力</h2><p>在使用<code>AsyncDisplayKit</code>优化你的项目之前,先通过<code>Instruments</code>来检测一下你的应用的响应能力,这一点很重要,可以确保你知道优化后<code>AsyncDisplayKit</code>给你带来了什么变化。<br><br>最重要的是,影响性能的因素中,无非就是<code>CPU</code>,<code>GPU</code>这两块,所以优化前,你应该首先弄清楚你的性能瓶颈在哪里,究竟是受制于<code>CPU</code>还是<code>GPU</code>,是哪一个降低了你应用的FPS。搞清楚这个之后,你可以看到<code>AsyncDisplayKit</code>是如何利用它的特性来帮你优化的。<br><br>如果你有时间的话,你可以使用<code>Instruments</code>来监测一下我们一开始提供的那个项目的性能瓶颈,你会发现它是受制于<code>CPU</code>的。</p>
<h2 id="准备切换到AsyncDisplayKit"><a href="#准备切换到AsyncDisplayKit" class="headerlink" title="准备切换到AsyncDisplayKit"></a>准备切换到AsyncDisplayKit</h2><p>在已存在的项目上使用<code>AsyncDisplayKit</code>很简单,就是把<code>view hierarchies</code>或者/和 <code>layer trees</code>替换为<code>display node hierarchies</code>。 <code>Display nodes</code>是<code>AsyncDisplayKit</code>中很重要的一个概念,它是基于views之上并且线程安全的,这意味着我们平常习惯于在主线程中做的那些views有关的部分工作现在可以脱离主线程了,是不是很惊奇?没错,这就是最大的魅力所在,所以你就可以把有限的资源去处理更重要的事情了,比如<code>touch event</code>或者<code>scroll view</code>的滚动。所以接下来第一步,就是去掉<code>view hierarchy</code>。<br></p>
<h3 id="Removing-the-View-Hierarchy"><a href="#Removing-the-View-Hierarchy" class="headerlink" title="Removing the View Hierarchy"></a>Removing the View Hierarchy</h3><p>打开 <code>RainforestCardCell.swift</code>,在 <code>awakeFromNib()</code> 删除所有的 <code>addSubview(...)</code> 调用, 像这样:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">override</span> <span class="function"><span class="keyword">func</span> <span class="title">awakeFromNib</span><span class="params">()</span></span> &#123;</span><br><span class="line"> <span class="keyword">super</span>.awakeFromNib()</span><br><span class="line"> contentView.layer.borderColor =</span><br><span class="line"> <span class="type">UIColor</span>(hue: <span class="number">0</span>, saturation: <span class="number">0</span>, brightness: <span class="number">0.85</span>, alpha: <span class="number">0.2</span>).<span class="type">CGColor</span></span><br><span class="line"> contentView.layer.borderWidth = <span class="number">1</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>替换<code>layoutSubviews()</code> 为下面的:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">override</span> <span class="function"><span class="keyword">func</span> <span class="title">layoutSubviews</span><span class="params">()</span></span> &#123;</span><br><span class="line"> <span class="keyword">super</span>.layoutSubviews()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>替换<code>configureCellDisplayWithCardInfo(cardInfo:)</code> 为下面的:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">configureCellDisplayWithCardInfo</span><span class="params">(cardInfo: RainforestCardInfo)</span></span> &#123;</span><br><span class="line"> <span class="comment">//MARK: Image Size Section</span></span><br><span class="line"> <span class="keyword">let</span> image = <span class="type">UIImage</span>(named: cardInfo.imageName)!</span><br><span class="line"> featureImageSizeOptional = image.size</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>删除<code>RainforestCardCell</code>中所有的<code>view</code>属性,剩下来的像这样:<br></p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">RainforestCardCell</span>: <span class="title">UICollectionViewCell</span> </span>&#123;</span><br><span class="line"> <span class="keyword">var</span> featureImageSizeOptional: <span class="type">CGSize</span>?</span><br><span class="line"> ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>然后保存并运行,结果像这样:<br><img src="http://www.raywenderlich.com/wp-content/uploads/2014/10/IMG_0001.jpg" alt=""><br>现在都是一些空的cells,所以你滚动起来相当顺滑,我们的目标就是当这些cell填上内容之后,仍然保持这样的触感。在你每做一步之后,你可以使用<code>Instruments’s Core Animation template</code>来观察app的帧率有什么变化。<br></p>
<h3 id="Adding-a-Placeholder"><a href="#Adding-a-Placeholder" class="headerlink" title="Adding a Placeholder"></a>Adding a Placeholder</h3><p>在<code>RainforestCardCell.swift</code>中添加一个属性<code>placeholderLayer</code>:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">RainforestCardCell</span>: <span class="title">UICollectionViewCell</span> </span>&#123;</span><br><span class="line"> <span class="keyword">var</span> featureImageSizeOptional: <span class="type">CGSize</span>?</span><br><span class="line"> <span class="keyword">var</span> placeholderLayer: <span class="type">CALayer</span>!</span><br><span class="line"> ...</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>
<p>使用placeholder是因为我们的cell的内容展示的时候是异步的,为了不让用户看到空的cell。这就像我们从网络下载图片的时候的做法一样,当图片下载完成之前先设置一个placeholder。</p>
<p>在<code>awakeFromNib()</code>中,配置<code>placeholderLayer</code>,然后该方法如下:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">override</span> <span class="function"><span class="keyword">func</span> <span class="title">awakeFromNib</span><span class="params">()</span></span> &#123;</span><br><span class="line"> <span class="keyword">super</span>.awakeFromNib()</span><br><span class="line"> placeholderLayer = <span class="type">CALayer</span>()</span><br><span class="line"> placeholderLayer.contents = <span class="type">UIImage</span>(named:<span class="string">"cardPlaceholder"</span>)!.<span class="type">CGImage</span></span><br><span class="line"> placeholderLayer.contentsGravity = kCAGravityCenter</span><br><span class="line"> placeholderLayer.contentsScale = <span class="type">UIScreen</span>.mainScreen().scale</span><br><span class="line"> placeholderLayer.backgroundColor = <span class="type">UIColor</span>(hue: <span class="number">0</span>, saturation: <span class="number">0</span>, brightness: <span class="number">0.85</span>, alpha: <span class="number">1</span>).<span class="type">CGColor</span></span><br><span class="line"> contentView.layer.addSublayer(placeholderLayer)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>在 <code>layoutSubviews()</code>, 加载<code>placeholderLayer</code>,修改后的方法如下:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">override</span> <span class="function"><span class="keyword">func</span> <span class="title">layoutSubviews</span><span class="params">()</span></span> &#123;</span><br><span class="line"> <span class="keyword">super</span>.layoutSubviews()</span><br><span class="line"> placeholderLayer?.frame = bounds</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>编译运行,看起来是这样:<br><img src="http://www.raywenderlich.com/wp-content/uploads/2014/10/IMG_0003.png" alt=""></p>
<p>普通的<code>CALayers</code>单独使用,没有与view关联的时候,当你改变frame的时候它们会有一个隐式的动画,所以你应该会看到当那个layer加载出来的时候有一个缩放的动画,为了修改这个问题,我们重写<code>layoutSubviews</code>方法如下:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">override</span> <span class="function"><span class="keyword">func</span> <span class="title">layoutSubviews</span><span class="params">()</span></span> &#123;</span><br><span class="line"> <span class="keyword">super</span>.layoutSubviews()</span><br><span class="line"> <span class="type">CATransaction</span>.begin()</span><br><span class="line"> <span class="type">CATransaction</span>.setValue(kCFBooleanTrue,</span><br><span class="line"> forKey:kCATransactionDisableActions)</span><br><span class="line"> placeholderLayer?.frame = bounds</span><br><span class="line"> <span class="type">CATransaction</span>.commit()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>重新编译运行,你会发现刚才的问题解决了。</p>
<h2 id="Your-First-Node"><a href="#Your-First-Node" class="headerlink" title="Your First Node"></a>Your First Node</h2><p>我们要重构这个app的第一步就是添加一个node,在这一部分,我们将要处理以下几个任务:<br></p>
<ul>
<li>创建,布局,添加 node 到 cell 中</li>
<li>重用 cell 以及其中的 node 和 layer</li>
<li>对 image node 做 blur 处理 </li>
</ul>
<p>接下来,在 <code>Layers-Bridging-Header.h</code>中导入<code>AsyncDisplayKit</code>:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">#<span class="keyword">import</span> &lt;AsyncDisplayKit/AsyncDisplayKit.h&gt;</span><br></pre></td></tr></table></figure>
<h3 id="项目结构梳理"><a href="#项目结构梳理" class="headerlink" title="项目结构梳理"></a>项目结构梳理</h3><ul>
<li>View Controller : <code>RainforestViewController</code> 实际上不做什么事情,只是简单的获得数据源并实现<code>UICollectionView</code>的代理。</li>
<li>Data Source : view controller 生成并重用cell,通过调用<code>configureCellDisplayWithCardInfo(cardInfo:)</code>配置cell。</li>
<li>Cell : 在 <code>configureCellDisplayWithCardInfo(cardInfo:)</code>方法中,cell生成node并添加到cell上面,然后布局nodes。</li>
</ul>
<p>这意味着每一次重用cell的时候,都会重复这些动作 。<br>如果你是用views代替nodes,那这样绝对不是最好的做法。但是现在用的是nodes,因为nodes的生成,布局以及填充,这些步骤都是可以放在异步线程做的,所以目前还不是问题,当然我们后续还会优化。唯一的问题是这样做的话,你不能很方便的取消这些异步操作或者是在重用cell的时候删除nodes。</p>
<p>&gt;</p>
<blockquote>
<p>Note : 在实际的开发中,你可以选择使用ASRangeController来缓存nodes,这样你就不需要在每次重用cell的时候去重新生成nodes。</p>
</blockquote>
<h3 id="Adding-the-Background-Image-Node"><a href="#Adding-the-Background-Image-Node" class="headerlink" title="Adding the Background Image Node"></a>Adding the Background Image Node</h3><p>打开 <code>RainforestCardCell.swift</code> 然后替换 <code>configureCellDisplayWithCardInfo(cardInfo:)</code> 为如下:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">configureCellDisplayWithCardInfo</span><span class="params">(cardInfo: RainforestCardInfo)</span></span> &#123;</span><br><span class="line"> <span class="comment">//MARK: Image Size Section</span></span><br><span class="line"> <span class="keyword">let</span> image = <span class="type">UIImage</span>(named: cardInfo.imageName)!</span><br><span class="line"> featureImageSizeOptional = image.size</span><br><span class="line"> <span class="comment">//MARK: Node Creation Section</span></span><br><span class="line"> <span class="keyword">let</span> backgroundImageNode = <span class="type">ASImageNode</span>()</span><br><span class="line"> backgroundImageNode.image = image</span><br><span class="line"> backgroundImageNode.contentMode = .<span class="type">ScaleAspectFill</span></span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>
<p><code>ASImageNode</code>是<code>AsyncDisplayKit</code>中用来做展示用的众多nodes中的一种,等价于<code>UIKit</code>中的<code>UIImageView</code>,只是<code>ASImageNode</code>默认情况下的图片解码操作是异步的。</p>
<p>在<code>configureCellDisplayWithCardInfo(cardInfo:)</code>的末尾添加下面一行代码:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">backgroundImageNode.layerBacked = <span class="literal">true</span></span><br></pre></td></tr></table></figure>
<p>Nodes有两种模式,一般情况下当需要处理某些事件的时候比如<code>touch event</code>,我们采用<code>view-backed</code>模式,反之如果只是纯粹的展示则采用<code>layer-backed</code>模式,<code>layer-backed</code>模式相对而言是轻量级的,会有更好一点的性能。由于我们这个项目中不需要处理事件,所以<code>backgroundImageNode</code>采用<code>layer-backed</code>模式。</p>
<p>在<code>configureCellDisplayWithCardInfo(cardInfo:)</code>的末尾继续添加下面一行代码:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//MARK: Node Layout Section</span></span><br><span class="line">backgroundImageNode.frame = <span class="type">FrameCalculator</span>.frameForContainer(featureImageSize: image.size)</span><br></pre></td></tr></table></figure>
<p><code>FrameCalculator</code>是个辅助类,封装了cell的布局处理,返回每一个node的frame。如果你要适配多个设备尺寸,这里你要谨慎处理,你可以看到这里没有使用约束,因为<code>AsyncDisplayKit</code>目前版本并不支持约束,希望后续支持吧。</p>
<p>在<code>configureCellDisplayWithCardInfo(cardInfo:)</code>的末尾继续添加下面一行代码:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//MARK: Node Layer and Wrap Up Section</span></span><br><span class="line"><span class="keyword">self</span>.contentView.layer.addSublayer(backgroundImageNode.layer)</span><br></pre></td></tr></table></figure>
<p>上面已经提到了,<code>AsyncDisplayKit</code>会为<code>backgroundImageNode</code>创建一个layer,但是你仍然需要把这个node添加到<code>layer tree</code>中,它才可以在屏幕上显示。另外由于node的绘制是异步的,所以在绘制完成之前它是不会显示的,尽管你已经把它添加到<code>layer tree</code>中了,这一点需要注意。当node的图片绘制完成之后,node的layer的content就会被更新,这个时候cell的 <code>content view</code>的layer会有两个sublayer.</p>
<p>你应该会注意到cell每次被取出重用的时候,<code>configureCellDisplayWithCardInfo(cardInfo:)</code>都会被调用,所以每次该方法调用的时候,我们都会添加一个新的layer到cell的contentview的layer上面,不过别担心,我们马上就会解决这个问题。</p>
<p>在<code>RainforestCardCell.swift</code>中新添加一个变量<code>backgroundImageNode</code>像这样:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">RainforestCardCell</span>: <span class="title">UICollectionViewCell</span> </span>&#123;</span><br><span class="line"> <span class="keyword">var</span> featureImageSizeOptional: <span class="type">CGSize</span>?</span><br><span class="line"> <span class="keyword">var</span> placeholderLayer: <span class="type">CALayer</span>!</span><br><span class="line"> <span class="keyword">var</span> backgroundImageNode: <span class="type">ASImageNode</span>? <span class="comment">///&lt; ADD THIS LINE</span></span><br><span class="line"> ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>添加这个属性我们可以持有这个node,是因为在<code>ARC</code>环境下,某些时候它会被释放,这样就不会显示在屏幕上了。node是持有它的layer的引用的,但是它的layer并不持有node,所以我们需要持有这个node。</p>
<p>在<code>configureCellDisplayWithCardInfo(cardInfo:)</code>的末尾继续添加下面一行代码:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">self</span>.backgroundImageNode = backgroundImageNode</span><br></pre></td></tr></table></figure>
<p>好了,目前为止,<code>configureCellDisplayWithCardInfo(cardInfo:)</code>是这样子的:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">configureCellDisplayWithCardInfo</span><span class="params">(cardInfo: RainforestCardInfo)</span></span> &#123;</span><br><span class="line"> <span class="comment">//MARK: Image Size Section</span></span><br><span class="line"> <span class="keyword">let</span> image = <span class="type">UIImage</span>(named: cardInfo.imageName)!</span><br><span class="line"> featureImageSizeOptional = image.size</span><br><span class="line"> <span class="comment">//MARK: Node Creation Section</span></span><br><span class="line"> <span class="keyword">let</span> backgroundImageNode = <span class="type">ASImageNode</span>()</span><br><span class="line"> backgroundImageNode.image = image</span><br><span class="line"> backgroundImageNode.contentMode = .<span class="type">ScaleAspectFill</span></span><br><span class="line"> backgroundImageNode.layerBacked = <span class="literal">true</span></span><br><span class="line"> <span class="comment">//MARK: Node Layout Section</span></span><br><span class="line"> backgroundImageNode.frame = <span class="type">FrameCalculator</span>.frameForContainer(featureImageSize: image.size)</span><br><span class="line"> <span class="comment">//MARK: Node Layer and Wrap Up Section</span></span><br><span class="line"> <span class="keyword">self</span>.contentView.layer.addSublayer(backgroundImageNode.layer)</span><br><span class="line"> <span class="keyword">self</span>.backgroundImageNode = backgroundImageNode</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>编译运行,你可以观察到<code>backgroundImageNode</code>的图片的异步呈现,感觉一下效果吧。<br><img src="http://www.raywenderlich.com/wp-content/uploads/2014/10/IMG_0006.png" alt=""></p>
<p>如果你运行在一个老一点的设备上,你会发现那些cell上的图片像爆米花一样一个个跳出来了,这并不是我们的理想结果,这个问题我们会放在最后解决。</p>
<p>正如上面我已经提到的,每次cell重用的时候,都会有一个新的layer被加上去,你可以快速滚动页面,然后打个断点在cell里面,会发现很有多layer在上面,接下来我们就来处理这个问题。</p>
<h3 id="Handling-Cell-Reuse"><a href="#Handling-Cell-Reuse" class="headerlink" title="Handling Cell Reuse"></a>Handling Cell Reuse</h3><p>首先继续在<code>RainforestCardCell.swift</code>, 添加个<code>contentLayer</code>,像这样:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">RainforestCardCell</span>: <span class="title">UICollectionViewCell</span> </span>&#123; </span><br><span class="line"> <span class="keyword">var</span> featureImageSizeOptional: <span class="type">CGSize</span>?</span><br><span class="line"> <span class="keyword">var</span> placeholderLayer: <span class="type">CALayer</span>!</span><br><span class="line"> <span class="keyword">var</span> backgroundImageNode: <span class="type">ASImageNode</span>?</span><br><span class="line"> <span class="keyword">var</span> contentLayer: <span class="type">CALayer</span>? <span class="comment">///&lt; ADD THIS LINE</span></span><br><span class="line"> ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>在<code>configureCellDisplayWithCardInfo(cardInfo:)</code>的末尾继续添加下面一行代码:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">self</span>.contentLayer = backgroundImageNode.layer</span><br></pre></td></tr></table></figure>
<p>然后替换 <code>prepareForReuse()</code>方法如下:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">override</span> <span class="function"><span class="keyword">func</span> <span class="title">prepareForReuse</span><span class="params">()</span></span> &#123;</span><br><span class="line"> <span class="keyword">super</span>.prepareForReuse()</span><br><span class="line"> backgroundImageNode?.preventOrCancelDisplay = <span class="literal">true</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>由于<code>AsyncDisplayKit</code>可以异步绘制,nodes可以让你阻止任何正在进行的绘制,当你需要取消或者停止绘制的时候只需要设置它的<code>preventOrCancelDisplay</code>为<code>true</code>即可,这样你就可以在cell重用之前停止之前的所有绘制,是不是很赞?</p>
<p>接下来,在<code>prepareForReuse()</code>中再添加几行代码,如下:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">contentLayer?.removeFromSuperlayer()</span><br><span class="line">contentLayer = <span class="literal">nil</span></span><br><span class="line">backgroundImageNode = <span class="literal">nil</span></span><br></pre></td></tr></table></figure>
<p>这几行代码简单易懂,就是在cell重用之前,把相应的layer和node都删除,并确保被释放掉,这样就解决了上面提到的layer堆积的问题。<br>编译运行,这一次就不会有layer堆积了,并且在cell滚出屏幕可视范围后,取消不必要的绘制。</p>
<h3 id="Blurring-the-Image"><a href="#Blurring-the-Image" class="headerlink" title="Blurring the Image"></a>Blurring the Image</h3><p>为了添加blur效果,需要我们在imagenode的展示过程中加入一些步骤。在<code>RainforestCardCell.swift</code>文件的<code>configureCellDisplayWithCardInfo(cardInfo:)</code>方法中做点修改,在<code>backgroundImageNode.layerBacked = true</code>后面添加如下代码:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">backgroundImageNode.imageModificationBlock = &#123; input <span class="keyword">in</span></span><br><span class="line"> <span class="keyword">if</span> input == <span class="literal">nil</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> input</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">let</span> blurredImage = input.applyBlurWithRadius(</span><br><span class="line"> <span class="number">30</span>,</span><br><span class="line"> tintColor: <span class="type">UIColor</span>(white: <span class="number">0.5</span>, alpha: <span class="number">0.3</span>),</span><br><span class="line"> saturationDeltaFactor: <span class="number">1.8</span>,</span><br><span class="line"> maskImage: <span class="literal">nil</span>, </span><br><span class="line"> didCancel:&#123; <span class="keyword">return</span> <span class="literal">false</span> &#125;) &#123;</span><br><span class="line"> <span class="keyword">return</span> blurredImage</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> image</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><code>ASImageNode</code>的<code>imageModificationBlock</code>给了我们一个机会在展示原始图片之前可以做一些其他的操作,比如滤镜操作,模糊处理等。</p>
<p>上面的代码,使用<code>imageModificationBlock</code>,给cell的背景图加上了模糊效果。最关键的一点是imagenode把绘制动作和这个闭包操作放在了子线程,这样就使主线程运行顺畅,这个闭包把原始图片作为参数然后返回处理后的图片。</p>
<p>这个模糊处理是使用了系统的方法,<code>UIImage</code> 的 <code>blurring category</code>,它主要是使用<code>Accelerate framework</code> 基于<code>CPU</code>来做的模糊操作。由于这个模糊处理会消耗内存同时也比较耗时,所以就支持了取消机制,<code>didCancel</code>闭包会被多次调用来监测是否应该取消模糊操作。目前为止,上面的代码只是简单返回了<code>false</code>,稍后我们就会来实际修改<code>didCancel</code>。</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Note</span> : 还记得一开始的时候你滑动页面的时候是什么感觉么?那个模糊处理严重的阻塞了主线程,通过<span class="type">AsyncDisplayKit</span>把这个操作放入子线程,现在大大提升了collection view滚动时候的体验。这简直就是一个天上,一个地下啊,有木有?</span><br></pre></td></tr></table></figure>
<p>运行看看现在的效果吧:<br><img src="http://www.raywenderlich.com/wp-content/uploads/2014/10/IMG_0009.png" alt=""></p>
<p>现在你可以看到<code>collection view</code>滚动起来是有多么的顺滑。</p>
<p>当<code>collection view</code>从重用队列取出一个cell重用的时候就开始了一个模糊处理操作,所以当你快速滚动的时候,<code>collection view</code>就会重用每一个cell很多次,理所当然的就启动了很多模糊处理的操作。这个当然不合理了😏😏,所以我们的目标应该是当一个cell开始重用的时候,去取消之前的模糊处理操作。</p>
<p>在前面我们已经可以在<code>prepareForReuse()</code>中取消node的绘制,所以一旦我们有机会在合适的时候取消模糊操作的话,那就毫不犹豫的去取消吧。</p>
<h3 id="Canceling-the-Blur"><a href="#Canceling-the-Blur" class="headerlink" title="Canceling the Blur"></a>Canceling the Blur</h3><p>为了取消那些正在进行的blur操作,我们需要重新实现blur方法中的<code>didCancel</code>闭包。添加如下代码到<code>imageModificationBlock</code>中:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">backgroundImageNode.imageModificationBlock = &#123; [<span class="keyword">weak</span> backgroundImageNode] input <span class="keyword">in</span></span><br><span class="line"> <span class="keyword">if</span> input == <span class="literal">nil</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> input</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="comment">// ADD FROM HERE...</span></span><br><span class="line"> <span class="keyword">let</span> didCancelBlur: () -&gt; <span class="type">Bool</span> = &#123;</span><br><span class="line"> <span class="keyword">var</span> isCancelled = <span class="literal">true</span></span><br><span class="line"> <span class="comment">// 1</span></span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">let</span> strongBackgroundImageNode = backgroundImageNode &#123;</span><br><span class="line"> <span class="comment">// 2</span></span><br><span class="line"> <span class="keyword">let</span> isCancelledClosure = &#123;</span><br><span class="line"> isCancelled = strongBackgroundImageNode.preventOrCancelDisplay</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="comment">// 3</span></span><br><span class="line"> <span class="keyword">if</span> <span class="type">NSThread</span>.isMainThread() &#123;</span><br><span class="line"> isCancelledClosure()</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> dispatch_sync(dispatch_get_main_queue(), isCancelledClosure)</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> isCancelled</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="comment">// ...TO HERE</span></span><br><span class="line"> ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>很明显,我们这里需要使用一个<code>weak reference</code>来避免<code>closure</code>和<code>backgroundImageNode</code>的循环引用。我们就使用<code>backgroundImageNode</code>来决定是否需要取消模糊处理。</p>
<p>上面的代码完成了如下几个功能:</p>
<ul>
<li>首先获得<code>backgroundImageNode</code>的一个强引用,为我们接下来的操作做准备。如果当这个闭包运行的时候<code>backgroundImageNode</code>不存在了,那么<code>isCancelled</code>就会为<code>true</code>,blur操作就会被取消,那我们就别提做什么blur操作了。</li>
<li>你会有疑问,在这个地方为什么取消blur的这个闭包中的代码会要求放在主线程来操作,这是因为一旦node创建了它的<code>layer</code>或者<code>view</code>之后,你就只能在主线程来访问node的属性了(这一点很重要)。由于我们需要使用node的<code>preventOrCancelDisplay</code>这个属性,而此时<code>backgroundImageNode</code>的layer已经创建过了,所以我们必须把这个监测放在主线程中。</li>
<li>由于我们需要确保<code>isCancelledClosure</code>会在主线程来被调用,所以如果是在主线程就直接访问<code>preventOrCancelDisplay</code>,否则的话就使用<code>dispatch_sync</code>来在主线程访问。你又有疑问么😏,这里又为什么使用<code>dispatch_sync</code>来同步执行,是因为我们必须在<code>didCancelBlur</code>闭包返回之前给一个明确的结果,即返回一个确切的<code>isCancelled</code>值。</li>
</ul>
<p>最后,在调用<code>applyBlurWithRadius(...)</code>方法的地方,把刚才我们定义好的闭包作为值传给<code>didCancel</code>这个参数,所以代码看起来像下面这样:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> <span class="keyword">let</span> blurredImage = input.applyBlurWithRadius(</span><br><span class="line"> <span class="number">30</span>,</span><br><span class="line"> tintColor: <span class="type">UIColor</span>(white: <span class="number">0.5</span>, alpha: <span class="number">0.3</span>),</span><br><span class="line"> saturationDeltaFactor: <span class="number">1.8</span>,</span><br><span class="line"> maskImage: <span class="literal">nil</span>,</span><br><span class="line"> didCancel: didCancelBlur) &#123;</span><br><span class="line"> ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>编译运行,你会发现有很大不同,现在当cell滚出屏幕后,它相应的blur操作也会被取消,这样我们会节省很多内存开销,同时减少了不必要的CPU时间片占用。你将会看到巨大的性能提升,尤其是在配置低一点的设备上。</p>
<p>当然了,也不可能把所有的操作都搬到子线程,我们的卡片还需要其他的数据展示,在接下来的文章中,我们还将学习以下几点:</p>
<ul>
<li>创建一个node容器来绘制其他内容并添加到一个单一的<code>CALayer</code>上面;</li>
<li>自定义<code>ASDisplayNode</code>;</li>
<li>在子线程来创建node层级并布局sub nodes</li>
</ul>
<p>所以接下来,敬请期待吧😊😊😊</p>
</content>
<summary type="html">
<p>追求极致的用户体验,从来都是我们锲而不舍的追求,对于iOS用户而言,这更是容不得一点马虎。随着时间的推移,现如今,谁还能忍受得了一个页面打开后,半天没有结果😭,出来之后滑动卡顿,点击个按钮半天木有反应啊,有木有?还好,还有<code>Facebook</code>,这位互联网IT界的大佬,为我们带来了福音,它就是我们今天要讲的<code>AsyncDisplayKit</code>。</p>
</summary>
<category term="iOS开发" scheme="http://yoursite.com/categories/iOS%E5%BC%80%E5%8F%91/"/>
<category term="页面布局" scheme="http://yoursite.com/tags/%E9%A1%B5%E9%9D%A2%E5%B8%83%E5%B1%80/"/>
</entry>
<entry>
<title>探索Today Extension的奥秘</title>
<link href="http://yoursite.com/2016/03/01/swift-today-extension-practice/"/>
<id>http://yoursite.com/2016/03/01/swift-today-extension-practice/</id>
<published>2016-02-29T16:00:00.000Z</published>
<updated>2016-03-05T11:59:27.000Z</updated>
<content type="html"><p>本文重点介绍Today Extension的机制,并通过一个Demo讲解整个实现过程。</p>
<a id="more"></a>
<h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><ul>
<li>extension并不是一个独立的app,它有一个包含在 app bundle 中的独立 bundle,extension 的bundle 后缀名是 .appex。其生命周期也和普通 app 不同。</li>
<li>extension不能单独存在,必须有一个包含它的 Containing app。</li>
<li>extension 需要用户手动激活 比如 Today 中的 widget 需要在 Today 中激活和关闭</li>
</ul>
<h2 id="Today-extension-的特点"><a href="#Today-extension-的特点" class="headerlink" title="Today extension 的特点"></a>Today extension 的特点</h2><ul>
<li>可以在锁屏情况下 快速查看</li>
<li>及时 简单直观 快速</li>
<li>简单交互 不可以输入 打开 Container APP</li>
</ul>
<h2 id="生命周期"><a href="#生命周期" class="headerlink" title="生命周期"></a>生命周期</h2><p><img src="app_extensions_lifecycle.png" alt="生命周期"></p>
<hr>
<p>可以看到 Today 的生命周期分为3步 </p>
<h3 id="开始"><a href="#开始" class="headerlink" title="开始"></a>开始</h3><p>在用户打开Today 点击extension时,系统就会加载extension应用,这是生命周期的开始。</p>
<h3 id="执行任务"><a href="#执行任务" class="headerlink" title="执行任务"></a>执行任务</h3><p>extension启动后,开始执行它的代码。</p>
<h3 id="终止"><a href="#终止" class="headerlink" title="终止"></a>终止</h3><p>在用户取消任务,或者任务执行结束,系统会将其杀掉。</p>
<h2 id="通讯"><a href="#通讯" class="headerlink" title="通讯"></a>通讯</h2><p><img src="detailed_communication.png" alt="通讯"></p>
<ul>
<li>extension 是属于Today的插件 它和Today的通讯是直接的</li>
<li>通过openURL的方式打开Container APP 当然也可打开其它的应用</li>
<li>通过一个公共的数据区与Container来交换数据</li>
</ul>
<h2 id="数据共享"><a href="#数据共享" class="headerlink" title="数据共享"></a>数据共享</h2><p><img src="app_extensions_container_restrictions.png" alt="数据共享"><br>公共的数据区 extension 和 Container 都可以读写<br>其实是一个配置了相同 groupId 的 NSUserDefault</p>
<h2 id="Today-的尺寸"><a href="#Today-的尺寸" class="headerlink" title="Today 的尺寸"></a>Today 的尺寸</h2><h3 id="iOS不同设备-Today-的宽度"><a href="#iOS不同设备-Today-的宽度" class="headerlink" title="iOS不同设备 Today 的宽度"></a>iOS不同设备 Today 的宽度</h3><p>iphone4s /5/5s/6/6s 只有竖屏一种情况 </p>
<table>
<thead>
<tr>
<th style="text-align:center">设备</th>
<th style="text-align:center">横屏左</th>
<th style="text-align:center">横屏右</th>
<th style="text-align:center">竖屏左</th>
<th style="text-align:center">竖屏右</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">iphone6/6s plus iOS 8</td>
<td style="text-align:center">666</td>
<td style="text-align:center">666</td>
<td style="text-align:center">414</td>
<td style="text-align:center">414</td>
</tr>
<tr>
<td style="text-align:center">iphone6/6s plus iOS 9</td>
<td style="text-align:center">736</td>
<td style="text-align:center">736</td>
<td style="text-align:center">414</td>
<td style="text-align:center">414</td>
</tr>
<tr>
<td style="text-align:center">iPad</td>
<td style="text-align:center">471</td>
<td style="text-align:center">328</td>
<td style="text-align:center">512</td>
<td style="text-align:center">512</td>
</tr>
<tr>
<td style="text-align:center">iPad Pro</td>
<td style="text-align:center">629</td>
<td style="text-align:center">437</td>
<td style="text-align:center">471</td>
<td style="text-align:center">328</td>
</tr>
</tbody>
</table>
<p>可以看到 不同的设备 extension 的宽度是很不一样的<br>iPhone Plus 可以横屏<br>iPad 在横屏时候 分为左边和右边<br>iPad Pro 情况就更复杂,竖屏的时候相当于 iPad 的横屏 横屏的时候 宽度也很大<br>所以在设计 Extension 时候 最好用 autoLayout</p>
<h2 id="实践"><a href="#实践" class="headerlink" title="实践"></a>实践</h2><p>下面就花10分钟来 实践写一个最简单的 extension 具体可以看 Demo.<br>在 Demo 里我弄了一个非常简单的 App 就只是一个UITextField 和一个button<br>点击 OK 按钮, 会把UITextField的文本保存到公共的 Userdefault 里面. </p>
<p><img src="APPMain.png" alt=""></p>
<hr>
<p>当然还开启了一个 scheme 用来让 extension 可以通过 openUrl 来打开<br>另外 有一个 shareData文件<br>代码如下:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">ShareData</span> </span>&#123;</span><br><span class="line"> <span class="keyword">static</span> <span class="keyword">let</span> key = <span class="string">"com.hujiang.key"</span></span><br><span class="line"> <span class="keyword">static</span> <span class="keyword">let</span> <span class="type">GroupName</span> = <span class="string">"group.open.com"</span></span><br><span class="line"> <span class="keyword">static</span> <span class="keyword">let</span> scheme = <span class="string">"testExtension-appextention://"</span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">static</span> <span class="function"><span class="keyword">func</span> <span class="title">getShareData</span><span class="params">()</span></span> -&gt; <span class="type">String</span>? &#123;</span><br><span class="line"> <span class="keyword">let</span> userDefault = <span class="type">NSUserDefaults</span>(suiteName: <span class="type">GroupName</span>)</span><br><span class="line"> <span class="keyword">return</span> userDefault?.objectForKey(key) <span class="keyword">as</span>? <span class="type">String</span></span><br><span class="line"> &#125;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">static</span> <span class="function"><span class="keyword">func</span> <span class="title">saveShareData</span><span class="params">(string: String)</span></span> &#123;</span><br><span class="line"> <span class="keyword">let</span> shareDefault = <span class="type">NSUserDefaults</span>(suiteName: <span class="type">GroupName</span>)</span><br><span class="line"> shareDefault?.setObject(string, forKey: key)</span><br><span class="line"> shareDefault?.synchronize()</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>shareData 只是用来存储公共数据 包括3个常量和2个方法:<br>saveShareData 用来把值存到指定 GroupId 的 NSUserDefaults 中<br>getShareData 从指定 GroupId 的 NSUserDefaults 中取值 </p>
<p>ok Container app 介绍完毕.下一步<br>添加一个 extension 的 Target 名称是 testEExtension</p>
<h2 id="配置证书"><a href="#配置证书" class="headerlink" title="配置证书"></a>配置证书</h2><p><img src="配置证书.png" alt="配置证书"><br>首先得要在 iTunes Connect 里配置 证书文件的 groupId 例如: group.com.hujiang.demo<br>注意,必须要以 group 开头</p>
<h2 id="开启-App-Group"><a href="#开启-App-Group" class="headerlink" title="开启 App Group"></a>开启 App Group</h2><p>在Xcode中 分别为container 和 extension 的Target Target —&gt; Capabilities<br>开启 app group 功能<br>开启好之后 工程里会有 两个entitlements 文件 </p>
<h2 id="修改info-Plist文件"><a href="#修改info-Plist文件" class="headerlink" title="修改info.Plist文件"></a>修改info.Plist文件</h2><p><img src="plist.png" alt="plist.png"></p>
<p>####先说明一下这个Plist中 NSExtension下的几个字段</p>
<ul>
<li>NSExtensionAttributes 表明是那一种类型的 extension</li>
<li>NSExtensionPrincipalClass 使用代码时 ViewControlle r的名称 </li>
<li>NSExtensionMainStoryboard 使用 storyboard storyboard 文件名,默认是MainInterface.</li>
</ul>
<p>如果不使用 storyboard 就一定要指定 NSExtensionPrincipalClass<br>删除 NSExtensionMainStoryboard,然后添加 NSExtensionPrincipalClass<br>使用你的新ViewController的名字 </p>
<hr>
<p>这里我们不使用 Storyboard 我们用自定义的 TodayViewController 作为主界面.<br>首先删除 NSExtensionMainStoryboard字段 ,添加 NSExtensionPrincipalClass 字段,设置 NSExtensionPrincipalClass 的 value 为 TodayViewController.</p>
<h2 id="TodayViewController-的实现"><a href="#TodayViewController-的实现" class="headerlink" title="TodayViewController 的实现"></a>TodayViewController 的实现</h2><p>首先在 TodayViewController里添加一个 button</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">var button: UIButton!</span><br></pre></td></tr></table></figure>
<p>然后在 viewDidLoad 里面</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">override</span> <span class="function"><span class="keyword">func</span> <span class="title">viewDidLoad</span><span class="params">()</span></span> &#123;</span><br><span class="line"> <span class="keyword">super</span>.viewDidLoad()</span><br><span class="line"> button = <span class="type">UIButton</span>(type: .<span class="type">Custom</span>)</span><br><span class="line"> button.frame = <span class="type">CGRectMake</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">300</span>, <span class="number">40</span>)</span><br><span class="line"> button.setTitle(<span class="string">"open"</span>, forState: .<span class="type">Normal</span>)</span><br><span class="line"> button.addTarget(<span class="keyword">self</span>, action: <span class="string">"openAction:"</span>, </span><br><span class="line"> forControlEvents: .<span class="type">TouchUpInside</span>)</span><br><span class="line"> view.addSubview(button)</span><br><span class="line"> <span class="keyword">self</span>.preferredContentSize = <span class="type">CGSizeMake</span>(<span class="number">0</span>, <span class="number">300</span>) </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>其中 self.preferredContentSize是控制显示这个 extension 的高度为300</p>
<p>在viewWillAppear里显示存储的值</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">override</span> <span class="function"><span class="keyword">func</span> <span class="title">viewWillAppear</span><span class="params">(animated: Bool)</span></span> &#123;</span><br><span class="line"> <span class="keyword">super</span>.viewWillAppear(animated)</span><br><span class="line"> button.setTitle(<span class="type">ShareData</span>.getShareData(), forState: .<span class="type">Normal</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>必须把ShareData 添加到 extension的Target 里面</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">openAction</span><span class="params">(sender: AnyObject?)</span></span> &#123;</span><br><span class="line"> <span class="keyword">let</span> url = <span class="type">NSURL</span>(string: <span class="type">ShareData</span>.scheme + <span class="string">"com.123"</span>)</span><br><span class="line"> <span class="keyword">self</span>.extensionContext?.openURL(url!, completionHandler: <span class="literal">nil</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>编译运行 你会得到</p>
<h2 id="一个错误"><a href="#一个错误" class="headerlink" title="一个错误"></a>一个错误</h2><p>&gt;<br>testEExtension[6048:147384] <strong><em> Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘</em></strong> setObjectForKey: object cannot be nil (key: <__nsconcreteuuid 0x7f8ca0513420=""> 9DB046F6-C18B-44CC-9148-1860D9C35B8F)’</__nsconcreteuuid></p>
<h3 id="这个问题只在-swift里才有-解决方法是"><a href="#这个问题只在-swift里才有-解决方法是" class="headerlink" title="这个问题只在 swift里才有, 解决方法是:"></a>这个问题只在 swift里才有, 解决方法是:</h3><ol>
<li>在extension的 build setting 的Packaging 里的Defines Module 设置为 YES.</li>
<li>在info.plist 的 NSExtensionPrincipalClass 前加上模块名 testEExtension.<br>然后编译运行 记得选中Extension 的target<br>最后的效果图:<br><img src="result.png" alt="最后的效果图"><br>点击 button 也可以打开 Contaier App .结束!</li>
</ol>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p> 可以看到, 做一个 extension 还是很简单的 一起动手做一个吧!</p>
<h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul>
<li><a href="https://developer.apple.com/library/ios/documentation/General/Conceptual/ExtensibilityPG/NotificationCenter.html#//apple_ref/doc/uid/TP40014214-CH11-SW1" target="_blank" rel="external">官方文档</a></li>
<li><a href="wangzz.github.io/blog/2014/06/23/wwdc2014zhi-app-extensionsxue-xi-bi-ji/">App Extension编程指南 中文版</a></li>
<li><a href="onevcat.com/2014/08/notification-today-widget/">WWDC2014之App Extensions学习笔记</a></li>
<li><a href="www.raywenderlich.com/83809/ios-8-today-extension-tutorial">iOS 8 Extensions</a></li>
<li><a href="onevcat.com/2014/08/notification-today-widget/">WWDC 2014 Session笔记 - iOS 通知中心扩展制作入门</a></li>
<li><a href="www.cocoachina.com/ios/20140904/9527.html">App Extension编程指南(iOS8/OS X v10.10):扩展类型–Today</a></li>
<li><a href="www.raywenderlich.com/83809/ios-8-today-extension-tutorial">iOS 8 Today Extension Tutorial</a></li>
<li><a href="code.tutsplus.com/tutorials/ios-8-creating-a-today-widget--cms-22379">iOS 8: Creating a Today Widget</a></li>
<li><a href="www.cocoachina.com/industry/20140522/8514.html">自定义 URL Scheme 完全指南</a></li>
<li><a href="blog.sina.com.cn/s/blog_407fb5bc01013v6s.html">iOS Framework制作全攻略</a></li>
<li><a href="http://www.cocoachina.com/ios/20140904/9527.html" target="_blank" rel="external">http://www.cocoachina.com/ios/20140904/9527.html</a></li>
<li><a href="http://www.toptal.com/ios/ios-8-app-extensions" target="_blank" rel="external">http://www.toptal.com/ios/ios-8-app-extensions</a></li>
<li><a href="https://developer.apple.com/library/prerelease/ios/documentation/UserExperience/Conceptual/MobileHIG/AppExtensions.html#//apple_ref/doc/uid/TP40006556-CH67-SW1" target="_blank" rel="external">https://developer.apple.com/library/prerelease/ios/documentation/UserExperience/Conceptual/MobileHIG/AppExtensions.html#//apple_ref/doc/uid/TP40006556-CH67-SW1</a></li>
</ul>
</content>
<summary type="html">
<p>本文重点介绍Today Extension的机制,并通过一个Demo讲解整个实现过程。</p>
</summary>
<category term="iOS开发" scheme="http://yoursite.com/categories/iOS%E5%BC%80%E5%8F%91/"/>
<category term="基础" scheme="http://yoursite.com/tags/%E5%9F%BA%E7%A1%80/"/>
</entry>
<entry>
<title>iOS技术周刊第1期</title>
<link href="http://yoursite.com/2016/02/28/ios-technology-magazine/"/>
<id>http://yoursite.com/2016/02/28/ios-technology-magazine/</id>
<published>2016-02-28T12:30:00.000Z</published>
<updated>2016-03-06T11:16:39.000Z</updated>
<content type="html"><p>本技术周刊旨在汇总每周的技术知识及行业动态,内容严格按照技术标签分类,望为每位iOS伙伴提供一些帮助,本篇涉及Swift,Hybrid, 性能优化等技术标签。</p>
<a id="more"></a>
<h2 id="沪江原创"><a href="#沪江原创" class="headerlink" title="沪江原创"></a>沪江原创</h2><ul>
<li><a href="http://hujiangtechnology.github.io/2016/03/01/swift-today-extension-practice/" target="_blank" rel="external">探索Today Extension的奥秘</a></li>
<li><a href="http://hujiangtechnology.github.io/2016/02/27/tech-tags-study/" target="_blank" rel="external">iOS 技术标签知识范围及学习资源整理</a></li>
</ul>
<h2 id="Swift"><a href="#Swift" class="headerlink" title="Swift"></a>Swift</h2><ul>
<li><a href="https://medium.com/@ryanolsonk/is-apple-using-swift-4a6c80f74599#.m9wr5tr7k" target="_blank" rel="external">Is Apple Using Swift in iOS ?</a> 目前看来仅Calculator采用Pure Swift,任重道远</li>
<li><a href="https://developer.apple.com/swift/blog/?id=27" target="_blank" rel="external">Increasing Performance by Reducing Dynamic Dispatch</a></li>
<li>@Swift大会1月10号在北京召开,大会资源如下:<ul>
<li><a href="https://github.com/atConf/atswift-2016-resources" target="_blank" rel="external">Keynote</a></li>
<li><a href="http://www.imooc.com/video/11118" target="_blank" rel="external">视频</a></li>
</ul>
</li>
<li><a href="http://swift.gg/2016/02/01/protocol-oriented-segue-identifiers-swift/" target="_blank" rel="external">使用 Swift 的面向协议编程定义 Segue 标识</a></li>
<li><a href="https://bugs.swift.org/projects/SR/issues/SR-581?filter=allopenissues" target="_blank" rel="external">Swift 也用 Jira 来管理Bug</a></li>
</ul>
<h2 id="Hybrid"><a href="#Hybrid" class="headerlink" title="Hybrid"></a>Hybrid</h2><ul>
<li>Chrome App 48.0.2564.87 版本开始使用WKWebView,<a href="https://appsto.re/cn/NVp8F.i" target="_blank" rel="external">https://appsto.re/cn/NVp8F.i</a></li>
<li><a href="http://vdisk.weibo.com/s/sWYf0noAplM?from=page_100505_profile&amp;wvr=6" target="_blank" rel="external">极致的Hybrid-Con2015</a></li>
</ul>
<h2 id="疑难杂症"><a href="#疑难杂症" class="headerlink" title="疑难杂症"></a>疑难杂症</h2><ul>
<li>当 iPhone 的应用跑在 iPad iOS 8.0.2 上时,编译出 <xibname>~iphone.nib 这样的文件是无法识别的,会 Crash。<a href="http://koze.hatenablog.jp/entry/2015/08/21/093000" target="_blank" rel="external">http://koze.hatenablog.jp/entry/2015/08/21/093000</a> 这篇文章介绍到了,解决这样的问题需要关闭 Size Class</xibname></li>
</ul>
<h2 id="越狱破解"><a href="#越狱破解" class="headerlink" title="越狱破解"></a>越狱破解</h2><ul>
<li><a href="http://mp.weixin.qq.com/s?__biz=MjM5NTIyNTUyMQ==&amp;mid=455373552&amp;idx=1&amp;sn=522bdeaa8a3a8a37423634f4e6ad0334&amp;scene=23&amp;srcid=0223kyi4SqD0Mu6UafGoaQ0T#rd" target="_blank" rel="external">在非越狱手机上进行App Hook</a></li>
</ul>
<h2 id="性能优化"><a href="#性能优化" class="headerlink" title="性能优化"></a>性能优化</h2><ul>
<li>View复用<ul>
<li><a href="https://github.com/peter-iakovlev/telegram" target="_blank" rel="external">Telegram iOS版本</a>,复用各种View,alloc的view都先缓存一下</li>
<li><a href="http://pingguohe.net/2016/01/31/lazyscroll.html" target="_blank" rel="external">iOS 高性能异构滚动视图构建方案 —— LazyScrollView</a></li>
</ul>
</li>
<li><a href="https://github.com/TencentOpen/GT" target="_blank" rel="external">腾讯性能测试开源</a></li>
<li><a href="https://github.com/stepanhruda/dyld-image-loading-performance" target="_blank" rel="external">动态库加载性能检测及解决方案</a></li>
<li>Watch Dog强制杀掉App,短时间内线程数达到峰值<ul>
<li><a href="http://stackoverflow.com/questions/25848441/app-shutdown-with-exc-resource-wakeups-exception-on-ios-8-gm" target="_blank" rel="external">http://stackoverflow.com/questions/25848441/app-shutdown-with-exc-resource-wakeups-exception-on-ios-8-gm</a></li>
<li>YYKit中有个方案可供参考:<a href="https://github.com/ibireme/YYDispatchQueuePool" target="_blank" rel="external">https://github.com/ibireme/YYDispatchQueuePool</a></li>
</ul>
</li>
</ul>
<h2 id="GitHub源码"><a href="#GitHub源码" class="headerlink" title="GitHub源码"></a>GitHub源码</h2><ul>
<li><a href="http://mp.weixin.qq.com/s?__biz=MzA3ODg4MDk0Ng==&amp;mid=402123943&amp;idx=1&amp;sn=9de12e74c32510a245d6195836653d5f#rd" target="_blank" rel="external">告别“伪连接”!如何高效检测iOS中的真实连接状态</a> . <a href="https://github.com/dustturtle/RealReachability" target="_blank" rel="external">https://github.com/dustturtle/RealReachability</a> </li>
<li>A queue to keep and reusing objects. <a href="https://github.com/acoomans/ACReuseQueue" target="_blank" rel="external">https://github.com/acoomans/ACReuseQueue</a></li>
<li>Telegram iOS App. <a href="https://github.com/peter-iakovlev/telegram" target="_blank" rel="external">https://github.com/peter-iakovlev/telegram</a></li>
</ul>
<h2 id="工具"><a href="#工具" class="headerlink" title="工具"></a>工具</h2><ul>
<li>CocoaPods 1.0.0.beta 4, <a href="http://blog.cocoapods.org/CocoaPods-1.0/" target="_blank" rel="external">查看1.0 更新记录</a></li>
<li><a href="https://github.com/alcatraz/Alcatraz" target="_blank" rel="external">Xcode插件管理器</a></li>
<li>翻墙<ul>
<li><a href="https://shadowsocks.com/" target="_blank" rel="external">https://shadowsocks.com/</a> 这个翻墙软件,速度稳定,看YouTube上的视频720P极其流畅 </li>
<li><a href="http://do1.glbproxy.tk/html/ec/index.html" target="_blank" rel="external">海豚畅游</a>, 目前仅支持Chrome和Android</li>
</ul>
</li>
<li>戴尔(DELL)专业P2415Q 23.8英寸16:9宽屏 LED背光 4K液晶显示器,效果与Macbook非常一致</li>
<li><a href="https://github.com/buunguyen/octotree/" target="_blank" rel="external">Octotree</a>, Code tree for GitHub and GitLab</li>
<li>技术学习视频网站,须翻墙,<a href="http://www.raywenderlich.com/" target="_blank" rel="external">http://www.raywenderlich.com/</a></li>
<li><a href="https://itunes.apple.com/cn/app/replica-web-developer-tool/id1068196306?l=en&amp;mt=8" target="_blank" rel="external">Replica - Web Developer Tool</a>,移动设备的Charles</li>
<li><a href="https://github.com/poboke/Miku" target="_blank" rel="external"><code>程序员鼓励师</code>的插件</a></li>
</ul>
<h2 id="后花园"><a href="#后花园" class="headerlink" title="后花园"></a>后花园</h2><ul>
<li>Apple Pay来了 <a href="https://developer.apple.com/apple-pay/get-started/cn/" target="_blank" rel="external">https://developer.apple.com/apple-pay/get-started/cn/</a></li>
<li>花生地铁WiFi_测试,可以在地铁里愉快地上网了</li>
<li>神奇的iOS 9.2.1版本 <a href="http://toutiao.com/i6252784551433601537/" target="_blank" rel="external">http://toutiao.com/i6252784551433601537/</a></li>
<li><a href="http://2016.qconbeijing.com/" target="_blank" rel="external">2016 QCon 北京主题招募中</a></li>
<li>干货集中营, <a href="http://gank.io/" target="_blank" rel="external">http://gank.io/</a></li>
<li><a href="http://mp.weixin.qq.com/s?__biz=MzAwMDU1MTE1OQ==&amp;mid=403520625&amp;idx=1&amp;sn=c59f3944760a055ee7b6d1dda4431e0a&amp;scene=23&amp;srcid=0113zrH6MRCoo0NBX3AOOSDO#r" target="_blank" rel="external">关于烂代码的那些事 - 为什么每个团队存在大量烂代码</a></li>
</ul>
</content>
<summary type="html">
<p>本技术周刊旨在汇总每周的技术知识及行业动态,内容严格按照技术标签分类,望为每位iOS伙伴提供一些帮助,本篇涉及Swift,Hybrid, 性能优化等技术标签。</p>
</summary>
<category term="iOS开发" scheme="http://yoursite.com/categories/iOS%E5%BC%80%E5%8F%91/"/>
<category term="iOS技术周刊" scheme="http://yoursite.com/tags/iOS%E6%8A%80%E6%9C%AF%E5%91%A8%E5%88%8A/"/>
</entry>
<entry>
<title>iOS 技术标签的知识范围及学习资源</title>
<link href="http://yoursite.com/2016/02/27/tech-tags-study/"/>
<id>http://yoursite.com/2016/02/27/tech-tags-study/</id>
<published>2016-02-27T11:30:00.000Z</published>
<updated>2016-03-05T10:22:49.000Z</updated>
<content type="html"><p>本文旨在为每个技术标签的进阶提供一些参考,Hybrid,Swift,性能优化,设计模式,页面布局,本地数据存储,网络交互,音视频等。</p>
<a id="more"></a>
<h2 id="Hybrid"><a href="#Hybrid" class="headerlink" title="Hybrid"></a>Hybrid</h2><h3 id="初级"><a href="#初级" class="headerlink" title="初级"></a>初级</h3><ol>
<li>熟练掌握<code>UIWebView</code> 的使用</li>
<li>熟练掌握<code>UIWebView</code>和<code>HTML</code>页面的交互,包括<code>拦截请求</code>以及<code>JavascriptCore</code></li>
<li>熟练掌握譬如 <strong>Charles</strong> 等抓包工具的使用</li>
</ol>
<h3 id="中级"><a href="#中级" class="headerlink" title="中级"></a>中级</h3><ol>
<li>熟练掌握<code>WKWebView</code>的使用,掌握其与<code>UIWebView</code>的不同之处</li>
<li>熟悉整个<code>HTML</code>页面的加载流程,熟知常见的<code>DOM</code>元素以及相关事件</li>
<li>熟练使用 <strong>Safari</strong> 对内嵌页面进行调试,掌握基本的<code>Javascript</code>书写</li>
<li>了解<code>Javascript</code>跨域安全问题,掌握<code>NSURLCache</code>、<code>NSURLProtocol</code>的使用</li>
</ol>
<h3 id="高级"><a href="#高级" class="headerlink" title="高级"></a>高级</h3><ol>
<li>熟练掌握<code>Javascript</code>以及<code>HTML5</code>特性,可独立完成一套完整的<code>HTML5</code>页面</li>
<li>阅读<code>WebKit</code>以及<code>JavascriptCore</code>源码,了解它们的核心逻辑</li>
<li>熟悉<code>HTTP</code>协议,以及基于<code>HTTP</code>的通讯协议,如<code>Soap</code>、<code>XMLRPC</code>等</li>
<li>熟悉 <strong>Web服务器</strong> 的基本工作原理,可在应用内内嵌,如<code>GCDWebServer</code>、<code>CocoaHTTPServer</code>等</li>
</ol>
<h3 id="学习资源"><a href="#学习资源" class="headerlink" title="学习资源"></a>学习资源</h3><ul>
<li>Apple 开发文档</li>
<li>W3School:<a href="http://www.w3school.com.cn/" target="_blank" rel="external">http://www.w3school.com.cn/</a></li>
<li>HTML5中国:<a href="http://www.html5cn.org/" target="_blank" rel="external">http://www.html5cn.org/</a></li>
<li>HTTP协议详解:<a href="http://blog.csdn.net/gueter/article/details/1524447" target="_blank" rel="external">http://blog.csdn.net/gueter/article/details/1524447</a></li>
<li>GCDWebServer:<a href="https://github.com/swisspol/GCDWebServer" target="_blank" rel="external">https://github.com/swisspol/GCDWebServer</a></li>
<li>CocoaHTTPServer:<a href="https://github.com/robbiehanson/CocoaHTTPServer" target="_blank" rel="external">https://github.com/robbiehanson/CocoaHTTPServer</a></li>
<li><a href="http://mp.weixin.qq.com/s?__biz=MzAxNDEwNjk5OQ==&amp;mid=401883911&amp;idx=1&amp;sn=938df448000d3c193a146c4f41b36f6c&amp;scene=23&amp;srcid=02239yd9HOhP7shWCE0LP0cH#rd" target="_blank" rel="external">Weex</a></li>
<li><a href="Ehttp://mp.weixin.qq.com/s?__biz=MzAwNDY1ODY2OQ==&amp;mid=400011053&amp;idx=1&amp;sn=81ed095f6fb9f7a4345ff50285264be1&amp;scene=23&amp;srcid=0223rWbN9Rk4GDeBuI78JmGC#rd" target="_blank" rel="external">JSPatch</a></li>
<li><a href="http://facebook.github.io/react-native/" target="_blank" rel="external">React-Native</a></li>
</ul>
<h2 id="Swift"><a href="#Swift" class="headerlink" title="Swift"></a>Swift</h2><h3 id="入门"><a href="#入门" class="headerlink" title="入门"></a>入门</h3><ol>
<li>熟练基本语法</li>
<li>熟练OC混编能力</li>
<li>了解函数式编程范式</li>
</ol>
<h3 id="进阶"><a href="#进阶" class="headerlink" title="进阶"></a>进阶</h3><ol>
<li>熟练掌握Swift高级用法</li>
<li>深入剖析Swift源码</li>
</ol>
<h3 id="学习资源-1"><a href="#学习资源-1" class="headerlink" title="学习资源"></a>学习资源</h3><ul>
<li><a href="https://developer.apple.com/library/mac/documentation/Swift/Conceptual/Swift_Programming_Language/" target="_blank" rel="external">The Swift Programming Language(Swift 2.1)</a></li>
<li><a href="https://developer.apple.com/library/mac/documentation/Swift/Conceptual/BuildingCocoaApps/" target="_blank" rel="external">Using Swift with Cocoa and Objective-C (Swift 2.1)</a></li>
<li><a href="https://swift.org/" target="_blank" rel="external">Swift 官网</a></li>
<li><a href="https://github.com/apple/swift" target="_blank" rel="external">Swift 源码</a></li>
<li><a href="http://swift.gg/" target="_blank" rel="external">Swift翻译组</a></li>
<li><a href="https://github.com/ReactiveCocoa/ReactiveCocoa" target="_blank" rel="external">ReactiveCocoa</a></li>
</ul>
<h2 id="性能优化"><a href="#性能优化" class="headerlink" title="性能优化"></a>性能优化</h2><h3 id="入门-1"><a href="#入门-1" class="headerlink" title="入门"></a>入门</h3><ol>
<li>熟悉Instruments基本使用,内存泄漏监测</li>
<li>了解FPS,图层绘制基本原理</li>
<li>了解App的加载及运行机制</li>
<li>了解内存分配机制</li>
<li>了解多线程机制</li>
</ol>
<h3 id="进阶-1"><a href="#进阶-1" class="headerlink" title="进阶"></a>进阶</h3><ol>
<li>熟练掌握Instruments各类分析工具</li>
<li>熟练掌握FPS,内存,线程等运行机制</li>
<li>深入DTrace细节</li>
<li>自动化性能测试</li>
</ol>
<h3 id="学习资源-2"><a href="#学习资源-2" class="headerlink" title="学习资源"></a>学习资源</h3><ol>
<li><a href="http://www.solarisinternals.com/wiki/index.php/DTrace_Topics_Intro" target="_blank" rel="external">DTrace 介绍</a></li>
<li><a href="https://developer.apple.com/library/prerelease/watchos/documentation/DeveloperTools/Conceptual/InstrumentsUserGuide/" target="_blank" rel="external">Apple 文档</a></li>
<li><a href="https://github.com/tapwork/HeapInspector-for-iOS" target="_blank" rel="external">测试定位内存泄露</a></li>
<li>FPS提升-AsyncDisplayKit: <ul>
<li><a href="https://github.com/facebook/AsyncDisplayKit" target="_blank" rel="external">Github 源码</a></li>
<li><a href="http://www.raywenderlich.com/86365/asyncdisplaykit-tutorial-achieving-60-fps-scrolling" target="_blank" rel="external">视频介绍</a></li>
</ul>
</li>
</ol>
<p> </p>
<h2 id="设计模式"><a href="#设计模式" class="headerlink" title="设计模式"></a>设计模式</h2><h3 id="初级-1"><a href="#初级-1" class="headerlink" title="初级"></a>初级</h3><ol>
<li>深入理解面向对象设计,并理解与编程范式之间的区别</li>
<li>熟练掌握面向对象的基本设计原则(<em>SOLID</em>)</li>
<li>熟悉<code>GoF</code>的23种面向对象设计模式</li>
<li>熟悉一些辅助设计的框架,包括<code>AOP</code>、<code>IoC</code>等</li>
</ol>
<h3 id="中级-1"><a href="#中级-1" class="headerlink" title="中级"></a>中级</h3><ol>
<li>熟练掌握常见的一些架构模式,如<code>MVC</code>、<code>MVP</code>、<code>MVVM</code>等</li>
<li>熟练掌握分层架构(<em>常见的三层架构、N层架构等</em>),以及分层的基本原则</li>
<li>熟悉常见的软件体系结构风格,包括 <strong>管道-过滤器式</strong>、<strong>层次式</strong>、<strong>面向对象式</strong>,以及它们之间的区别</li>
</ol>
<h3 id="高级-1"><a href="#高级-1" class="headerlink" title="高级"></a>高级</h3><ol>
<li>熟悉常见的软件开发模式,包括敏捷、瀑布、迭代等,包括常见的<code>XP</code>、<code>SCRUM</code>等</li>
<li>熟悉常见的软件设计方式,包括 <strong>领域驱动设计</strong>、<strong>模型\数据驱动设计</strong>、<strong>测试驱动设计</strong> 等</li>
<li>熟悉企业级应用的架构模型,包括 <strong>分布式</strong>、<strong>负载均衡</strong> 等</li>
</ol>
<h3 id="学习资源-3"><a href="#学习资源-3" class="headerlink" title="学习资源"></a>学习资源</h3><ul>
<li>面向对象设计原则:<a href="http://www.cnblogs.com/feipeng/archive/2007/03/02/661840.html" target="_blank" rel="external">http://www.cnblogs.com/feipeng/archive/2007/03/02/661840.html</a></li>
<li>23种设计模式:<a href="http://blog.csdn.net/longyulu/article/details/9159589" target="_blank" rel="external">http://blog.csdn.net/longyulu/article/details/9159589</a></li>
<li>分层架构设计原则:<a href="http://www.cnblogs.com/chencheng/archive/2012/07/05/2575406.html" target="_blank" rel="external">http://www.cnblogs.com/chencheng/archive/2012/07/05/2575406.html</a></li>
<li>领域驱动设计:<a href="http://www.cnblogs.com/xishuai/category/572887.html" target="_blank" rel="external">http://www.cnblogs.com/xishuai/category/572887.html</a></li>
<li>书籍:《敏捷软件开发:原则、模式与实践》</li>
<li>博客:<a href="http://casatwy.com/" target="_blank" rel="external">http://casatwy.com/</a></li>
</ul>
<h2 id="页面布局"><a href="#页面布局" class="headerlink" title="页面布局"></a>页面布局</h2><h3 id="入门-2"><a href="#入门-2" class="headerlink" title="入门"></a>入门</h3><ol>
<li>熟悉Xib, Storyboard工具</li>
<li>熟悉Autolayout,SizeClass,UIStackView,CGRect等布局方式</li>
</ol>
<h3 id="进阶-2"><a href="#进阶-2" class="headerlink" title="进阶"></a>进阶</h3><ol>
<li>iOS 多设备的布局优化</li>
<li>深入理解布局相关的源码与机制</li>
<li>多类布局方式的最佳实践</li>
</ol>
<h3 id="学习资源-4"><a href="#学习资源-4" class="headerlink" title="学习资源"></a>学习资源</h3><ol>
<li>像素是如何绘制到屏幕上的: <a href="http://objccn.io/issue-3-1/" target="_blank" rel="external">http://objccn.io/issue-3-1/</a></li>
<li>页面布局,页面渲染的原理: <a href="https://developer.apple.com/library/ios/documentation/2DDrawing/Conceptual/DrawingPrintingiOS/GraphicsDrawingOverview/GraphicsDrawingOverview.html#//apple_ref/doc/uid/TP40010156-CH14-SW14" target="_blank" rel="external">https://developer.apple.com/library/ios/documentation/2DDrawing/Conceptual/DrawingPrintingiOS/GraphicsDrawingOverview/GraphicsDrawingOverview.html#//apple_ref/doc/uid/TP40010156-CH14-SW14</a></li>
<li>AutoLayout: <a href="http://www.cocoachina.com/ios/20151021/13825.html" target="_blank" rel="external">http://www.cocoachina.com/ios/20151021/13825.html</a></li>
<li>SizeClass<ul>
<li><a href="http://blog.callmewhy.com/2014/09/12/learn-ios8-size-class/" target="_blank" rel="external">http://blog.callmewhy.com/2014/09/12/learn-ios8-size-class/</a> </li>
<li><a href="http://onevcat.com/2014/07/ios-ui-unique/" target="_blank" rel="external">http://onevcat.com/2014/07/ios-ui-unique/</a></li>
</ul>
</li>
<li>UIView及其扩展接口深入理解 <a href="http://my.oschina.net/w11h22j33/blog/208574?fromerr=4ylkDitz" target="_blank" rel="external">http://my.oschina.net/w11h22j33/blog/208574?fromerr=4ylkDitz</a></li>
<li>MASConstraint <a href="https://github.com/SnapKit/Masonry" target="_blank" rel="external">https://github.com/SnapKit/Masonry</a></li>
</ol>
<h2 id="本地数据存储"><a href="#本地数据存储" class="headerlink" title="本地数据存储"></a>本地数据存储</h2><h3 id="入门-3"><a href="#入门-3" class="headerlink" title="入门"></a>入门</h3><ol>
<li>熟悉各类本地数据存储,SQLite(FMDB),CoreData,UserDefaults</li>
<li>熟悉SQL基本语法</li>
<li>了解NOSQL基本原理,并熟悉YapDatabase, Realm等</li>
<li>熟练使用数据库查看工具SQLite Professional</li>
</ol>
<h3 id="进阶-3"><a href="#进阶-3" class="headerlink" title="进阶"></a>进阶</h3><ol>
<li>最佳实践</li>
</ol>
<h3 id="学习资源-5"><a href="#学习资源-5" class="headerlink" title="学习资源"></a>学习资源</h3><ul>
<li><a href="http://www.sqlite.org/" target="_blank" rel="external">SQLite 官网</a></li>
<li>CoreData<ul>
<li><a href="https://developer.apple.com/library/watchos/documentation/Cocoa/Conceptual/CoreData/index.html" target="_blank" rel="external">Core Data Programming Guide</a></li>
<li><a href="http://blog.csdn.net/kesalin/article/details/6739319" target="_blank" rel="external">框架详解</a></li>
<li><a href="http://blog.csdn.net/kesalin/article/details/6746117" target="_blank" rel="external">手动编写代码</a></li>
<li><a href="http://blog.csdn.net/kesalin/article/details/6757279" target="_blank" rel="external">使用绑定1</a></li>
<li><a href="http://blog.csdn.net/kesalin/article/details/6757412" target="_blank" rel="external">使用绑定2</a></li>
</ul>
</li>
<li>Realm<ul>
<li><a href="https://realm.io/" target="_blank" rel="external">Realm 官网</a></li>
<li><a href="http://www.jianshu.com/p/052c763d5693" target="_blank" rel="external">基础教程</a></li>
</ul>
</li>
<li>源码<ul>
<li><a href="https://github.com/ChrislosChen/ANKeyValue" target="_blank" rel="external">https://github.com/ChrislosChen/ANKeyValue</a></li>
<li><a href="https://github.com/yapstudios/YapDatabase" target="_blank" rel="external">https://github.com/yapstudios/YapDatabase</a></li>
<li><a href="https://github.com/ccgus/fmdb" target="_blank" rel="external">https://github.com/ccgus/fmdb</a></li>
<li><a href="https://github.com/marcoarment/FCModel" target="_blank" rel="external">https://github.com/marcoarment/FCModel</a></li>
</ul>
</li>
</ul>
<h2 id="网络交互"><a href="#网络交互" class="headerlink" title="网络交互"></a>网络交互</h2><h3 id="知识范围"><a href="#知识范围" class="headerlink" title="知识范围"></a>知识范围</h3><ul>
<li>BSD Socket</li>
<li>CFNetwork</li>
<li>缓存</li>
<li>HTTP / TCP 协议</li>
</ul>
<h3 id="学习资源-6"><a href="#学习资源-6" class="headerlink" title="学习资源"></a>学习资源</h3><ul>
<li><a href="https://github.com/ChenYilong/ParseSourceCodeStudy/tree/master/02_Parse%E7%9A%84%E7%BD%91%E7%BB%9C%E7%BC%93%E5%AD%98%E4%B8%8E%E7%A6%BB%E7%BA%BF%E5%AD%98%E5%82%A8" target="_blank" rel="external">网络缓存与离线存储</a></li>
<li><a href="http://blog.csdn.net/kesalin/article/details/8801156" target="_blank" rel="external">CFNetwork</a></li>
<li><a href="http://blog.csdn.net/kesalin/article/details/8798039" target="_blank" rel="external">Socket</a></li>
<li><a href="http://blog.csdn.net/kesalin/article/details/8867781" target="_blank" rel="external">NSStream</a></li>
<li><a href="http://casatwy.com/iosying-yong-jia-gou-tan-wang-luo-ceng-she-ji-fang-an.html" target="_blank" rel="external">iOS应用架构谈 网络层设计方案</a></li>
</ul>
<h2 id="音视频"><a href="#音视频" class="headerlink" title="音视频"></a>音视频</h2><h3 id="学习资源-7"><a href="#学习资源-7" class="headerlink" title="学习资源"></a>学习资源</h3><ul>
<li>《Learning-AV-Foundation》 <a href="http://www.amazon.com/Learning-Foundation-Hands-Mastering-Framework/dp/0321961803" target="_blank" rel="external">http://www.amazon.com/Learning-Foundation-Hands-Mastering-Framework/dp/0321961803</a></li>
<li><a href="https://developer.apple.com/av-foundation/" target="_blank" rel="external">https://developer.apple.com/av-foundation/</a></li>
</ul>
</content>
<summary type="html">
<p>本文旨在为每个技术标签的进阶提供一些参考,Hybrid,Swift,性能优化,设计模式,页面布局,本地数据存储,网络交互,音视频等。</p>
</summary>
<category term="iOS开发" scheme="http://yoursite.com/categories/iOS%E5%BC%80%E5%8F%91/"/>
<category term="基础" scheme="http://yoursite.com/tags/%E5%9F%BA%E7%A1%80/"/>
</entry>
<entry>
<title>带你领略 Swift 的风骚</title>
<link href="http://yoursite.com/2016/02/24/take-you-to-learn-swift-feng-sao/"/>
<id>http://yoursite.com/2016/02/24/take-you-to-learn-swift-feng-sao/</id>
<published>2016-02-24T08:26:13.000Z</published>
<updated>2016-03-05T10:22:34.000Z</updated>
<content type="html"><p><code>Swift</code>,已经出来一年多了,想必不少人都曾对它抱着观望的态度,如果现在的你还是这样的态度,那么是时候去改变了。作为一个年龄比我还大的语言————<code>Objective-C</code>,是时候淡出历史的舞台了,有人说<code>Objective-C</code>和<code>Swift</code>会并存,但我觉得,短期内会这样,从长远的角度来看,并存的可能性并不大。毕竟,它们不是<code>C</code>和<code>C++</code>这样的关系,它们更像<code>Delphi</code>与<code>C#</code>这样的存在。从可维护的角度来说,并存的代价比较大,所以,对于<code>Objective-C</code>,放手吧。</p>
<a id="more"></a>
<p>作为一个绝对现代化的编程语言,Swift集合了当下众多流行语言的特性,这与保守的<code>Objective-C</code>产生了鲜明的对比。如果你还在犹豫踌躇中,那么就让本文带你领略下新欢的风骚,这一切,你值得拥有!</p>
<h2 id="不得不说的简洁和优雅"><a href="#不得不说的简洁和优雅" class="headerlink" title="不得不说的简洁和优雅"></a>不得不说的简洁和优雅</h2><p>谈到Swift,不得不把它的优雅放在第一位,作为一门现代化的编程语言,优雅是它必备的特质,那么什么是<strong>优雅</strong>?所谓优雅就是在书写或阅读时,有种简单自然、一气呵成的感觉,不罗嗦、不做作,这是语法上的优雅,而设计中的优雅,又何尝不是这样?</p>
<h3 id="声明和实现的合并"><a href="#声明和实现的合并" class="headerlink" title="声明和实现的合并"></a>声明和实现的合并</h3><p>这可能是Swift向现代化语言看齐的第一步,在很多传统的编程语言里,例如C++、Pascal、Objective-C,类或方法的声明与实现是分离开的。当然C++是可以在头文件里直接实现内联方法,但这很不自然,也不安全,因为它会暴露给最终用户。</p>
<p>以往声明和实现的分离,其实更像是<code>接口定义</code>与<code>接口实现</code>,但这种强制性的设定给使用者也会带来一些麻烦。就拿Objective-C来说,如果我要定义一个私有类,则会写在“.m”文件中,但这时候又不得不将声明也书写一遍,这是没有太多意义的;又比如,我一个简单到只有属性的<code>PONSO</code>,还不得不将一个空的实现书写一遍(<em>请抛开自动属性这些用户不需要知道的细节</em>)。</p>
<p>任何一门现代化的编程语言,都摈弃了这种分离的方式,其原因是为了追求更加的简洁和优雅。而在这种合并的方式下,也迫使我们不会将一个类写得特别庞大,以前的团队合作中,大家只会看你的头文件,而现在,更多的是看你具体的代码实现了(<em>无疑,这也是一种Code Review</em>)。</p>
<h3 id="扩展(Extension)"><a href="#扩展(Extension)" class="headerlink" title="扩展(Extension)"></a>扩展(Extension)</h3><p>扩展,这也是Swift中非常有特色的设计,也是苹果编程语言的一贯传承。Swift中的扩展,其实是对Objective-C中<code>Category</code>和<code>Extension</code>的合并,在Objective-C中,我们会书写类似下面的代码:</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">@interface</span> <span class="title">OCObject</span> : <span class="title">NSObject</span></span></span><br><span class="line"><span class="keyword">@end</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">@interface</span> <span class="title">OCObject</span> (<span class="title">Category</span>) &lt;<span class="title">NSCoding</span>&gt;</span></span><br><span class="line"><span class="keyword">@end</span></span><br></pre></td></tr></table></figure>
<p>对比Swift,如下:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">SwiftObject</span> </span>&#123; &#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">extension</span> <span class="title">SwiftObject</span>: <span class="title">NSCoding</span> </span>&#123; &#125;</span><br></pre></td></tr></table></figure>
<p>扩展主要可应用于以下场景:</p>
<ol>
<li>对原有类增加新协议适配,新方法</li>
<li>对实现代码按逻辑块进行划分(<em>比如按实现的协议划分</em>)</li>
</ol>
<p>在我所接触的其它语言里,也有扩展一说,比如C#,但C#的扩展其实完全只是语法糖,而Objective-C或Swift中的扩展,并不仅仅是语法上的便利,更有运行时的支持,考虑下面代码:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">protocol</span> <span class="title">SwiftProtocol</span> </span>&#123; &#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">SwiftClass</span> </span>&#123; &#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">extension</span> <span class="title">SwiftClass</span>: <span class="title">SwiftProtocol</span> </span>&#123; &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> swiftObject = <span class="type">SwiftClass</span>()</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(swiftObject <span class="keyword">is</span> <span class="type">SwiftProtocol</span>)</span><br><span class="line"><span class="comment">// print: true</span></span><br></pre></td></tr></table></figure>
<p>不得不说,扩展对代码的优雅性上,也给出了不少的支援,相比于一个实现了N多协议的类,我们用<code>extension</code>进行划分会清晰很多。</p>
<h3 id="可选类型和可选链"><a href="#可选类型和可选链" class="headerlink" title="可选类型和可选链"></a>可选类型和可选链</h3><p>可选类型,也就是在一个类型的定以后,增加一个<code>?</code>号,代表这个变量可以为<code>nil</code>。这也是Swift相对于Objective-C的一个重大改进,使得代码更加安全,表述性更强。但,需要注意的是,这并不是Swifit特有的,在.Net平台中,可选类型也是非常常见的。对于以下的Objective-C代码:</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">- (<span class="built_in">NSString</span> *)userAddress:(HJUser *)user &#123;</span><br><span class="line"> <span class="keyword">if</span> (user == <span class="literal">nil</span>) <span class="keyword">return</span> <span class="literal">nil</span>;</span><br><span class="line"> <span class="keyword">if</span> (user.city == <span class="literal">nil</span>) <span class="keyword">return</span> <span class="literal">nil</span>;</span><br><span class="line"> <span class="keyword">if</span> (user.street == <span class="literal">nil</span>) <span class="keyword">return</span> <span class="literal">nil</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> [<span class="built_in">NSString</span> stringWithFormat:<span class="string">@"%@%@"</span>, user.city, user.street];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>从使用者的角度来说,我们必须要判断返回值,因为我们根本不确定是否会返回<code>nil</code>;而从设计者的角度来说,我们也很头疼,我们也不知道调皮的用户到底会给我们传入什么。而<code>__nonnull</code>这样的标示,根本就无法阻止这样的行为:</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">- (<span class="built_in">NSString</span> * __nonnull)test:(<span class="built_in">NSString</span> * __nonnull)arg &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">nil</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这段代码在Xcode 7 beta2中编译,没有任何错误和警告。想一想上面的那段<code>userAddress</code>代码,其实我们很确定,如果传入的参数为空或者其属性为空,则返回值肯定为空;而对方法的设计而言,参数为空是没有任何意义的,我们应该让使用者保证他传入的参数是不能为<code>nil</code>的,而不是在两端都对<code>nil</code>进行判定,这无疑增加了复杂度(<em>三个方面:设计、使用、调试</em>),也不合理。所以,在Swift中对其进行改善:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">getUserAddress</span><span class="params">(user: HJUser)</span></span> -&gt; <span class="type">String</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> user.city + user.street</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>我们可以安全的调用上面的方法,并不需要多余的<code>nil</code>判断,那么可选类型应该应用于什么场景呢?<strong>可选类型,应该应用于某个变量、参数或返回值,存在空或非空两种合理的状态下</strong>。也就是说它可以为空,也可以不为空,并且,从逻辑的角度考虑很合理。比如,下面的代码:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">findUser</span><span class="params">(username: String)</span></span> -&gt; <span class="type">HJUser</span>? &#123;</span><br><span class="line"> <span class="comment">// 如果找到了则返回</span></span><br><span class="line"> <span class="comment">// 否则返回nil</span></span><br><span class="line"> <span class="keyword">return</span> <span class="literal">nil</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>从逻辑的角度考虑,我们查找数据,有两种结果:找到和没有找到,这样的场景下就特别适合使用可选类型。那么配合可选链,我们的代码会非常简洁和优雅:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">findUserRealName</span><span class="params">(username: String)</span></span> -&gt; <span class="type">String</span>? &#123;</span><br><span class="line"> <span class="keyword">return</span> findUser(username)?.info?.realName</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="模式匹配(Patterns)"><a href="#模式匹配(Patterns)" class="headerlink" title="模式匹配(Patterns)"></a>模式匹配(Patterns)</h3><p>模式匹配,是函数式编程里非常常见的一个特性,这也和语言的优雅性息息相关,在Swift中,大概有以下几种模式匹配:</p>
<ol>
<li>通配符(Wildcard Pattern)</li>
<li>标识符(Identifier Pattern)</li>
<li>值绑定(Value-Binding Pattern)</li>
<li>元组(Tuple Pattern)</li>
<li>枚举(Enumeration Case Pattern)</li>
<li>可选(Optional Pattern)</li>
<li>类型转换(Type-Casting Pattern)</li>
<li>表达式(Expression Pattern)</li>
</ol>
<p>关于模式匹配,下面的代码进行了很好的阐述:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Identifier Pattern</span></span><br><span class="line"><span class="keyword">let</span> points: [(<span class="type">Int</span>, <span class="type">Int</span>)?] = [(<span class="number">0</span>, <span class="number">0</span>), (<span class="number">1</span>, <span class="number">1</span>), <span class="literal">nil</span>, (<span class="number">3</span>, <span class="number">3</span>)]</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> <span class="keyword">case</span> .<span class="type">Some</span>(<span class="keyword">let</span> p) = points[<span class="number">0</span>] &#123; <span class="comment">// Enumeration Pattern</span></span><br><span class="line"> <span class="built_in">print</span>(p)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> <span class="keyword">let</span> p = points[<span class="number">0</span>] &#123; <span class="comment">// Identifier Pattern</span></span><br><span class="line"> <span class="built_in">print</span>(p)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> <span class="keyword">case</span> <span class="keyword">let</span> point? <span class="keyword">in</span> points &#123; <span class="comment">// Optional Pattern</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> (x, <span class="number">_</span>) = point <span class="comment">// Tuple Pattern</span></span><br><span class="line"> <span class="built_in">print</span>(x)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">switch</span> point &#123;</span><br><span class="line"> <span class="keyword">case</span> (<span class="number">0</span>, <span class="number">0</span>): <span class="comment">// Expression Pattern</span></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"0, 0"</span>)</span><br><span class="line"> <span class="keyword">case</span> <span class="keyword">let</span> (<span class="number">1</span>, y): <span class="comment">// Value-Binding Pattern</span></span><br><span class="line"> <span class="built_in">print</span>(y)</span><br><span class="line"> <span class="keyword">case</span> <span class="number">_</span>: <span class="comment">// Wildcard Pattern</span></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Wildcard Pattern"</span>)</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>对于模式匹配的深入理解,有助于对Swift的阅读,也有助于编写出更加简洁优雅的代码。anyway,只要记住,<strong>模式匹配是一种类似于正则表达式的捕获规则</strong>,比如通配符<code>_</code>可以捕获任何值,<code>(x, y)</code>只能捕获二元组,<code>(0, y)</code>只能匹配以0为第一元的二元组,可选和枚举也类似。从抽象的角度来说,所有的模式匹配,都有以下特质:</p>
<ul>
<li>需要有一个输入与之进行匹配测试</li>
<li>匹配结果有两种:成功或失败</li>
<li>匹配成功时,可捕获所匹配到的值</li>
</ul>
<p>That’s cool!感受下,如果没有这些便捷的模式匹配,用传统的条件分支语句,会写成怎样?</p>
<h3 id="闭包(Closure)"><a href="#闭包(Closure)" class="headerlink" title="闭包(Closure)"></a>闭包(Closure)</h3><p>没有闭包的编程语言,就不能称之为函数式编程语言,我们来看看百度百科对闭包的解释:</p>
<blockquote>
<p>闭包是可以包含自由(<em>未绑定到特定对象</em>)变量的代码块;这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义(<em>局部变量</em>)。</p>
</blockquote>
<p>Objective-C中也有闭包,也就是<code>block</code>,但就语法的反人类程度就已经很令人发指了,更别说和函数指针定义在一起时,是多么令人奔溃了。也就是说<code>block</code>的设计并不简洁、优雅,这点在Swift中有了很好的改进,Swift中,已直接将它称之为<strong>闭包</strong>:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">sendRequest</span><span class="params">(url: String, response:<span class="params">(String)</span></span></span> -&gt; <span class="type">Void</span>) &#123;</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>在Swift中,闭包的定义其实可以抽象成<code>() -&gt; ()</code>这样的通用模式,也就是一个输入<strong>推导出</strong>一个输出,这是非常直观的定义方式,也更贴近其它编程语言中闭包的定义方式(<em>C#,Java8中的Lambda表达式</em>)。而在Swift中,为了更加优雅,放置在参数最后的闭包使用时可以放置到参数括号外,闭包输入参数可以用<code>$0、$1...</code>这样的方式来捕获,如下:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">sendRequest(<span class="string">"http://www.baidu.com"</span>) &#123;</span><br><span class="line"> <span class="built_in">print</span>($<span class="number">0</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这种闭包放置在参数括号外的特性,Swift将它称之为<strong>尾随闭包</strong>。</p>
<p>在Swift中,和其它函数式编程语言一样,闭包更像是嵌套函数,或者称之为内部函数,Swift中所有的函数声明,都可以用闭包表达式来描述,例如:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">login</span><span class="params">(username: String, password: String)</span></span> -&gt; <span class="type">Bool</span> &#123;</span><br><span class="line"> <span class="comment">//....</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>用闭包表达式来描述:<code>(String, String) -&gt; Bool</code>,<strong>所有类型为闭包的参数,都可以用签名相同的函数来替代</strong>,所以,先前的<code>sendRequest</code>,可以直接如下使用:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sendRequest(<span class="string">"http://www.baidu.com"</span>, response: <span class="built_in">print</span>)</span><br></pre></td></tr></table></figure>
<p>这便是<strong>函数式编程</strong>的一个精髓所在,函数不仅可以被调用,还可以将其作为参数或返回值进行传递,这可能是和<strong>命令式编程</strong>最大的区别了。</p>
<p>另外在使用闭包时,由于它能捕获当前上下文中的变量,特别是对<code>self</code>而言,这很容易导致循环引用。以往在Objective-C中的常见做法是定义一个<code>weak</code>的<code>self</code>,只使用那个<code>weak</code>的<code>self</code>,但这样依然会有出错的可能,这点在Swift中也进行了改良:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">sendRequest(<span class="string">"http://www.baidu.com"</span>) &#123; [<span class="keyword">weak</span> <span class="keyword">self</span>] resp -&gt; <span class="type">Void</span> <span class="keyword">in</span></span><br><span class="line"> <span class="comment">//...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这种情况下,闭包内所有的<code>self</code>都是<code>weak</code>的,强制性的需要你进行<code>nil</code>的判断。这样的设计,使得Swift更加安全和优雅。关于安全,这正是我们接下来需要探讨的内容!</p>
<h2 id="类型和类型安全"><a href="#类型和类型安全" class="headerlink" title="类型和类型安全"></a>类型和类型安全</h2><p>Objective-C是一个弱类型的语言,或者说是一个比较动态的语言,而Swift与之截然不同,Swift是一个名副其实的强类型语言。相比之下,弱类型的语言更加灵活,但更容易出错,而强类型的语言,描述性和约束性更强,也更加安全。</p>
<h3 id="类型推断"><a href="#类型推断" class="headerlink" title="类型推断"></a>类型推断</h3><p>类型推断是现代化编程语言的趋势,在<code>C#</code>中引入了<code>var</code>关键字,大大简化了方法和变量的定义。Swift中自然是拥有了类型推断的能力,没有类型推断的Objective-C代码如下:</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">- (<span class="keyword">void</span>)doSomething &#123;</span><br><span class="line"> OCObject *obj = [OCObject new];</span><br><span class="line"> <span class="built_in">NSString</span> *str = [obj test:<span class="string">@"hello world"</span>];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>由于没有类型推断,变量定义时必须要定义它所属于的类型,方法的返回值也是类似。而从某种角度来说,其实通过等号右侧完全可以推断出左侧的类型,所以在Swift中,编译器会智能的做这样的推断,可以帮我们省略下很多代码的编写:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">doSomething</span> </span>&#123;</span><br><span class="line"> <span class="keyword">let</span> obj = <span class="type">SwiftObject</span>()</span><br><span class="line"> <span class="keyword">let</span> str = obj.test(<span class="string">"hello world"</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>当然,类型推断的好处远远不止这种形式,在泛型和闭包中,类型推断简直就是其设计的点睛之笔。而类型推断,也进一步的阐述了Swift是强类型语言,否则就不可能推断出所需类型,那么在Objective-C中常见的<code>unrecognized selector</code> Crash,在纯粹的Swift中永远不会发生。</p>
<h3 id="值类型"><a href="#值类型" class="headerlink" title="值类型"></a>值类型</h3><p>值类型也是Swift中伟大的创举之一,值类型的一个显著特征便是在赋值和传递时会进行复制。为什么说Swift中值类型是一个创举呢?因为在我所经历的高级语言里,从未见过将字符串和框架内集合类型定义为值类型的,不得不说,Swift是第一个。</p>
<p>选择值类型,往往是为了对象在多线程环境下更加安全,因为它<code>复制</code>的特性,我们需要面对的只是单个实例对象,这使得我们对代码更加可控。另外,值类型中方法如果要修改成员变量,则必须使用<code>mutating</code>修饰:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">mutating</span> <span class="function"><span class="keyword">func</span> <span class="title">withMutableCharacters</span>&lt;R&gt;<span class="params">(body: <span class="params">(<span class="keyword">inout</span> String.CharacterView)</span></span></span> -&gt; <span class="type">R</span>) -&gt; <span class="type">R</span></span><br></pre></td></tr></table></figure>
<p>这会让我们在使用或设计时,更清楚值对象的变化原因,而外界对值对象的修改也是有很大限制,考虑下面的代码:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">TestStruct</span> </span>&#123;</span><br><span class="line"> <span class="keyword">var</span> field1 = <span class="number">3</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">modifyStruct</span><span class="params">(<span class="keyword">var</span> st: TestStruct)</span></span> &#123;</span><br><span class="line"> st.field1 = <span class="number">4</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> st = <span class="type">TestStruct</span>()</span><br><span class="line"></span><br><span class="line">modifyStruct(st)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(st.field1)</span><br></pre></td></tr></table></figure>
<p>执行结果为<code>3</code>,为什么会这样?因为在进行参数传递的时候,函数体操作的只是<code>st</code>的副本,<code>st</code>在传递时进行了复制。所以,即便是将<code>var st = TestStruct()</code>改为<code>let st = TestStruct()</code>也不会有任何编译问题,这是完全合法的操作。如果的确要在函数内修改传入参数,则使用下面的方式:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">modifyStruct</span><span class="params">(<span class="keyword">inout</span> st: TestStruct)</span></span> &#123;</span><br><span class="line"> st.field1 = <span class="number">4</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> st = <span class="type">TestStruct</span>()</span><br><span class="line"></span><br><span class="line">modifyStruct(&amp;st)</span><br></pre></td></tr></table></figure>
<p>这与很多编程语言中的参数传递方式是类似的,也就是两种:<strong>传引用</strong>和<strong>传值</strong>,默认情况下,Swift参数的传递都是“传值”的方式,这种情况下,引用类型会多出一个指向实例内存的指针,而值类型会进行复制(<em>可以说,指针就是值类型</em>)。</p>
<p>了解了值类型与引用类型的本质区别,那么还有很多值得去尝试的地方,比如在值类型中定义引用类型的成员,那么在该值对象的副本上修改该引用成员,依然会影响到主体。具体实践就留给在座的各位了。</p>
<h3 id="枚举类型"><a href="#枚举类型" class="headerlink" title="枚举类型"></a>枚举类型</h3><p>在目前主流的编程语言中,枚举是非常常见的一种值类型,而大多数人对它的用法一直还停留在<code>C或C++</code>的那种形式上。枚举是什么?顾名思义,<strong>枚举是一系列有限的状态集合</strong>,那么涉及到状态时,我们会很自然的想到用枚举来表示。如果仅仅用来表示状态,那么枚举的使用范围就非常有限,但在实际的开发中,有很多时候,我们有些数据仅在某种状态下才具有意义,或者说,这种数据只能存在于特定状态中。比如我们做网络请求时,会有两种状态:<strong>成功</strong>或<strong>失败</strong>,而仅仅在失败时,<strong>错误消息</strong>这个数据才有意义。那么这时候如果使用面向对象的思维来解决,我们可能需要定义一个通用的<strong>状态</strong>基类,然后有两个子类来实现不同状态。但在Swift中,你有另外的选择,那就是<strong>枚举关联值</strong>。</p>
<p>所谓枚举关联值,便是在枚举中的每一种状态下,都可以关联一些个数据。其实这种做法在Java中早就有了,但Java的枚举比起Swift的,还是有所不足的。我们先来看看Java的枚举:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">enum</span> Color &#123;</span><br><span class="line"> RED(<span class="string">"红色"</span>, <span class="number">1</span>), GREEN(<span class="string">"绿色"</span>, <span class="number">2</span>), BLANK(<span class="string">"白色"</span>, <span class="number">3</span>), YELLO(<span class="string">"黄色"</span>, <span class="number">4</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> String name;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">int</span> index;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="title">Color</span><span class="params">(String name, <span class="keyword">int</span> index)</span> </span>&#123;</span><br><span class="line"> <span class="keyword">this</span>.name = name;</span><br><span class="line"> <span class="keyword">this</span>.index = index;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>可以看到,Java的枚举的确也有关联值,但数量和数值都是固定死的,它也无法解决上面我们提到的那个问题。所以,还是看看Swift是怎样解决这样的问题吧:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">enum</span> <span class="title">ResponseStatus</span> </span>&#123;</span><br><span class="line"> <span class="keyword">case</span> <span class="type">Success</span></span><br><span class="line"> <span class="keyword">case</span> <span class="type">Failure</span>(errorMessage: <span class="type">String</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> status = <span class="type">ResponseStatus</span>.<span class="type">Failure</span>(errorMessage: <span class="string">"网络连接中断"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 模式匹配中的 Enumeration Pattern 哦~</span></span><br><span class="line"><span class="keyword">if</span> <span class="keyword">case</span> .<span class="type">Failure</span>(<span class="keyword">let</span> msg) = status &#123;</span><br><span class="line"> <span class="built_in">print</span>(msg)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">switch</span> status &#123;</span><br><span class="line"><span class="keyword">case</span> .<span class="type">Success</span>:</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"success"</span>)</span><br><span class="line"><span class="keyword">case</span> .<span class="type">Failure</span>(<span class="keyword">let</span> msg):</span><br><span class="line"> <span class="built_in">print</span>(msg)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>是不是很酷?相当酷!这样给我们减少了一些细小类的编写,并且更加合理和直观。除了关联值之外,枚举还可以有它自己的构造函数和方法,这会给我们在设计状态相关的逻辑时提供不少的便利。除此之外,枚举还是可以定义成泛型的,这样的灵活性给了我们更大的发挥空间,所以,我们再来看看泛型!</p>
<h3 id="泛型"><a href="#泛型" class="headerlink" title="泛型"></a>泛型</h3><p>泛型,这是多么激动人心的一个特性啊,不知道你是否和我一样对泛型抱有极高的期待。作为一个现代化的编程语言,怎么可以没有泛型呢?转入Objective-C之后,很多时候的设计,都卡在了泛型这块,使得我不得不多写出一些类来完成设计。虽然如今的Objective-C中加入了泛型,但依然没有达到我的预期,而Swift中的泛型,虽然还有些欠缺,却也已经是足够强大了。</p>
<p>什么时候该用泛型呢?我总结如下:<strong>当某种逻辑,可应用于一系列有相似点的对象,为了确保拥有强类型的特性时,则需要使用泛型</strong>。对于这句话的理解,需要进行一些深度分析的,很多时候其实我们并不需要泛型,使用基类即可满足。聚个例子吧,当我们进行一些图形绘制程序的设计时,一个图形元素的绘制可能会抽象出这样的接口:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">protocol</span> <span class="title">GraphicElement</span> </span>&#123;</span><br><span class="line"> <span class="function"><span class="keyword">func</span> <span class="title">draw</span><span class="params">(panel: GraphicPanel)</span></span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>那么在我们的绘图引擎中,绘制元素的方法应该如何定义?使用泛型么?这里其实并不适合使用泛型,因为我们没有必要保留强类型的特性,我们只关心<code>draw</code>方法,所以使用基类即可:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">drawElement</span><span class="params">(element: GraphicElement)</span></span> &#123;</span><br><span class="line"> element.draw(<span class="keyword">self</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>而在某些情况下,我们需要使用强类型的特性,这会使得我们代码更加简洁和安全,这时候,我们就需要使用泛型。比如,在一个通用的消息过滤模块,我们需要对消息内容进行关键字过滤,那么过滤前和过滤后的消息类型应该是一致的,这时候,我们就需要保留强类型的特性,所以,要使用泛型:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Message</span> </span>&#123;</span><br><span class="line"> <span class="keyword">let</span> content: <span class="type">String</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">init</span>(content: <span class="type">String</span>) &#123;</span><br><span class="line"> <span class="keyword">self</span>.content = content</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">GroupMessage</span> : <span class="title">Message</span> </span>&#123;</span><br><span class="line"> <span class="keyword">private</span>(<span class="keyword">set</span>) <span class="keyword">var</span> groupId: <span class="type">Int</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">init</span>(id: <span class="type">Int</span>, content: <span class="type">String</span>) &#123;</span><br><span class="line"> <span class="keyword">self</span>.groupId = id</span><br><span class="line"> <span class="keyword">super</span>.<span class="keyword">init</span>(content: content)</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">filterMessage</span>&lt;T: Message&gt;<span class="params">(message: T)</span></span> -&gt; <span class="type">T</span> &#123;</span><br><span class="line"> <span class="comment">// ... filter </span></span><br><span class="line"> <span class="keyword">return</span> message</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> message = <span class="type">GroupMessage</span>(id: <span class="number">12</span>, content: <span class="string">"hello world"</span>)</span><br><span class="line">message = filterMessage(message)</span><br></pre></td></tr></table></figure>
<p>请时刻记住泛型的使用场景,避免没必要的泛型设计。在Swift中,目前泛型还是有欠缺的,少了<code>逆变</code>和<code>协变</code>的支持,而在某些场景下,这是必须的。相信在不久的将来,这个特性会被弥补上来的,毕竟,Objective-C中已经有了这样的支持,虽然不尽人意。</p>
<p>逆变和协变,不仅体现在泛型上,在继承链中的方法覆写上,也有应用。而很多人对这样的概念仍是一知半解,甚至陷入了错误的认知,希望在这里可以帮助大家真正理解它的适用场景。最基本的概念如下:</p>
<ul>
<li><strong>逆变</strong>:父类可以替代子类</li>
<li><strong>协变</strong>:子类可以替代父类</li>
</ul>
<p>为什么会有这两种概念存在?其实最根本的原因还是为了类型安全,在<code>C#</code>的语法设计中,对于数组默认是允许协变的,这样导致存在安全隐患,这也是<code>C#</code>为数不多的设计缺陷之一。考虑下面的<code>C#</code>代码:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">class Super &#123; &#125;</span><br><span class="line"></span><br><span class="line">class Sub : Super &#123;</span><br><span class="line"> public void test() &#123; &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">public static void Main(string[] args) &#123;</span><br><span class="line"> var subs = new Sub[3];</span><br><span class="line"> subs[0] = new Sub();</span><br><span class="line"> subs[1] = new Sub();</span><br><span class="line"> </span><br><span class="line"> Super[] supers = subs; // 协变</span><br><span class="line"> supers[1] = new Super();</span><br><span class="line"> </span><br><span class="line"> // 这里会崩溃掉,抛出 ArrayTypeMismatchException</span><br><span class="line"> subs[1].test(); </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>可以说,这种设计是很糟糕的,因为它没有保证到类型的安全,我们明明声明的是一个<code>Sub</code>的数组,里面却可以混入一个不是<code>Sub</code>的类型,这便是<strong>协变的陷阱</strong>,相同的问题在Objective-C和Java中同样存在(<em>Java中会抛出ArrayStoreException异常</em>)。而在Swift中,如下的代码:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Super</span> </span>&#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Sub</span> : <span class="title">Super</span> </span>&#123;</span><br><span class="line"> <span class="function"><span class="keyword">func</span> <span class="title">test</span><span class="params">()</span></span> &#123;&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> subs = [<span class="type">Sub</span>]()</span><br><span class="line">subs.append(<span class="type">Sub</span>())</span><br><span class="line">subs.append(<span class="type">Sub</span>())</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> supers: [<span class="type">Super</span>] = subs</span><br><span class="line"></span><br><span class="line">supers[<span class="number">1</span>] = <span class="type">Super</span>()</span><br><span class="line">subs[<span class="number">1</span>].test()</span><br></pre></td></tr></table></figure>
<p>这样的代码是不会出现任何问题的,因为在Swift中,<code>Array</code>是值类型,<code>supers[1] = Super()</code>只是对副本的修改,并不会影响到<code>subs</code>。这样的协变特性,目前只有系统库能享有,我们自己定义泛型是无法做到的,但,从安全性的角度来说,值类型的泛型应该默认支持协变,这是没有任何副作用的。</p>
<p>协变和逆变的另一个使用场景,便是方法参数和返回值的约束,<strong>参数和返回值应该是可协变的,而闭包中的参数应该是可逆变的</strong>,请好好的理解我说的这句话,加以实践,你会明白这其间的道理。关于泛型,也就说到这里,更多内容还需大家自己去领悟,接下来,我们看看更多有意思的东西!</p>
<h3 id="元组类型"><a href="#元组类型" class="headerlink" title="元组类型"></a>元组类型</h3><p>似乎所有的函数式编程语言里都有元组类型,包括<code>C#</code>这种命令式编程语言里也引入了元组,其实元组是很简单的东西,但在Swift中却尤为重要。<strong>元组类型可以看作是一种有序字典</strong>,在编程语言中,所有的<code>Plain Object</code>都是可以用字典来表示,只是使用起来不是很便利。而,元组是一种表现力更强,使用起来更方便的<code>Plain Object</code>。</p>
<p>什么时候适合使用元组?大体在下面几种情况下:</p>
<ol>
<li>多返回值的函数里</li>
<li>用于临时的数据传输对象(<em>DTO</em>)</li>
</ol>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">mutilReturn</span><span class="params">()</span></span> -&gt; (<span class="type">String</span>, <span class="type">String</span>) &#123;</span><br><span class="line"> <span class="keyword">return</span> (<span class="string">"Hello"</span>, <span class="string">"World"</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">tempDTO</span><span class="params">()</span></span> &#123;</span><br><span class="line"> <span class="keyword">var</span> cellSummary: (id: <span class="type">Int</span>, display: <span class="type">String</span>)</span><br><span class="line"> cellSummary.id = <span class="number">1</span></span><br><span class="line"> cellSummary.display = <span class="string">"hello world"</span></span><br><span class="line"></span><br><span class="line"> <span class="built_in">print</span>(cellSummary)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>元组在面对这种有意义但又很小的简单对象时非常有用,可以帮我们减少很多细小类的编写,<strong>需要注意的是,元组是值对象</strong>,所以它拥有所有值对象的特性。另外,<strong>Swift中,所有的变量定义都是一个一元组</strong>,可以通过下面的代码验证:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> i = <span class="number">456</span></span><br><span class="line"><span class="keyword">let</span> i2: <span class="type">Int</span> = <span class="number">566</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 通过下标访问元组项</span></span><br><span class="line"><span class="built_in">print</span>(i.<span class="number">0.0</span>.<span class="number">0.0</span>)</span><br><span class="line"><span class="built_in">print</span>(i2.<span class="number">0.0</span>.<span class="number">0.0</span>)</span><br></pre></td></tr></table></figure>
<p>这种设计可以让元组类型和普通的值类型进行平滑过渡,所以<code>Type</code>和<code>(Type)</code>在Swift中是等同的,考虑下面的代码:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Item</span> </span>&#123;</span><br><span class="line"> <span class="keyword">let</span> value: <span class="type">Int</span></span><br><span class="line"> <span class="keyword">init</span>(<span class="number">_</span> value: <span class="type">Int</span>) &#123;</span><br><span class="line"> <span class="keyword">self</span>.value = value</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 一元组数组</span></span><br><span class="line"><span class="keyword">var</span> array: [(<span class="type">Item</span>)] = [(<span class="type">Item</span>(<span class="number">1</span>)), (<span class="type">Item</span>(<span class="number">2</span>)),<span class="type">Item</span>(<span class="number">3</span>), <span class="type">Item</span>(<span class="number">4</span>), <span class="type">Item</span>(<span class="number">5</span>)];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">test</span><span class="params">(i: Item)</span></span> &#123;</span><br><span class="line"> <span class="built_in">print</span>(i.value)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">test(array[<span class="number">0</span>])</span><br></pre></td></tr></table></figure>
<p>上面的代码是没有任何问题的,所以,在Swift中,元组是一等公民,也可以说,只要你使用了Swift,你就已经在使用元组了。</p>
<h3 id="嵌套类型"><a href="#嵌套类型" class="headerlink" title="嵌套类型"></a>嵌套类型</h3><p>嵌套类型也是Swift中靠近现代化语言的重要一步,各种主流高级设计语言中,对嵌套类型的定义都有细微区别。比如,Java中的成员内部类其实就是一个闭包,而静态内部类才是和Swift内部类类似的存在。不过,<strong>嵌套类型的设计,基本都是为了提供更严格的访问控制,和隔离实现</strong>。除此之外,由于Swift中没有名称空间(<em>Name Space</em>)和包(<em>Package</em>),只有模块(<em>Module</em>)的概念,嵌套类型也常用来组织一系列相关的类,用以类更精细化的管理。参考下面这样一段代码:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">protocol</span> <span class="title">GraphicElement</span> </span>&#123;</span><br><span class="line"> <span class="function"><span class="keyword">func</span> <span class="title">draw</span><span class="params">()</span></span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">GraphicElementFactory</span> </span>&#123;</span><br><span class="line"></span><br><span class="line"> <span class="class"><span class="keyword">class</span> <span class="title">func</span> <span class="title">createElement</span>(<span class="title">text</span>: <span class="title">String</span>) -&gt; <span class="title">GraphicElement</span> </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="type">TextGraphicElement</span>(text: text)</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">TextGraphicElement</span>: <span class="title">GraphicElement</span> </span>&#123;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">let</span> text: <span class="type">String</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">init</span>(text: <span class="type">String</span>) &#123;</span><br><span class="line"> <span class="keyword">self</span>.text = text</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="function"><span class="keyword">func</span> <span class="title">draw</span><span class="params">()</span></span> &#123;</span><br><span class="line"> <span class="built_in">print</span>(<span class="keyword">self</span>.text)</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> element = <span class="type">GraphicElementFactory</span>.createElement(<span class="string">"hello world"</span>)</span><br><span class="line">element.draw()</span><br></pre></td></tr></table></figure>
<p>上面的示例,我们通过嵌套类来对外屏蔽实现细节,从面向对象的角度来考虑,这提供了良好的封装性,约束了使用者必须通过某种唯一途径来获得接口的实例。考虑下Foundation中的类簇,这种更严格的访问控制使得我们能设计出对客户代码侵入性更小的类库,减少使用者对某些他们并不关心类的困惑。</p>
<h3 id="安全的覆写"><a href="#安全的覆写" class="headerlink" title="安全的覆写"></a>安全的覆写</h3><p>最后稍微提及一下Swift在<code>override</code>上的改进,也就是面向对象中的覆写。这一点在Objective-C中简直是糟透了,因为当你继承一个类时,一不小心你就可能覆写掉了父类的某个私有方法,结果当然是你无法预计的。所以,为了防止这样的情况出现,我们会在私有方法命名前加上一些毫无意义的标识,这对追求优雅的人来说,是极度痛苦的(<em>有段时间我一直在比较,究竟用几个下划线比较好看</em>)。好在Swift里对此做出了很好的改进,<strong>如果子类要覆写父类中的方法,那么必须使用<code>override</code>关键字,如果子类中出现了与父类签名相同的方法,并且没有标记<code>override</code>则编译不会通过</strong>。这很棒!不是么?编译器向我们保证了继承链中不会存在意外覆写的状况,又为我们减少了一个可能会掉入的坑,所以,现在的程序员,真是太幸福了。</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Foo</span> </span>&#123;</span><br><span class="line"> <span class="function"><span class="keyword">func</span> <span class="title">foo</span><span class="params">()</span></span> &#123;</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"foo"</span>)</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Bar</span> : <span class="title">Foo</span> </span>&#123;</span><br><span class="line"> <span class="function"><span class="keyword">func</span> <span class="title">foo</span><span class="params">()</span></span> &#123; <span class="comment">// 编译不通过</span></span><br><span class="line"></span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="重新定义函数"><a href="#重新定义函数" class="headerlink" title="重新定义函数"></a>重新定义函数</h2><p>众所周知,Swift是一个支持函数式编程的语言,所以在函数这块与传统的命令式编程有较大的区别。首先,我们要搞清楚,什么是<strong>函数</strong>,什么是<strong>方法</strong>?函数是统称,而方法是主体的行为,也就是定义在类或其它主体中的函数。</p>
<h3 id="高阶函数"><a href="#高阶函数" class="headerlink" title="高阶函数"></a>高阶函数</h3><p>在函数式编程里,我们不得不说说高阶函数,这是函数式与命令式最大的区别。在函数式编程里,函数是可以做为函数的输入参数和返回值,而<strong>高阶函数便是参数或返回值中有函数的函数</strong>。Swift中的高阶函数定义使用的是闭包表达式,这在闭包的章节里已经有所提及,参看下面这个高阶函数:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">extension</span> <span class="title">Array</span> </span>&#123;</span><br><span class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">func</span> <span class="title">select</span>&lt;T&gt;<span class="params">(trans: <span class="params">(Element)</span></span></span> -&gt; <span class="type">T</span>) -&gt; [<span class="type">T</span>] &#123;</span><br><span class="line"> <span class="keyword">var</span> result = [<span class="type">T</span>]()</span><br><span class="line"> <span class="keyword">for</span> ele <span class="keyword">in</span> <span class="keyword">self</span> &#123;</span><br><span class="line"> result.append(trans(ele))</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> result</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">translateInt</span><span class="params">(i: Int)</span></span> -&gt; <span class="type">String</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"<span class="subst">\(i)</span>"</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> array = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>]</span><br><span class="line"><span class="keyword">let</span> strArray = array.select(translateInt)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(strArray)</span><br></pre></td></tr></table></figure>
<p>上面<code>Array</code>扩展中的<code>select</code>方法便是一个高阶函数,因为它接受一个方法参数,当然,我们也可以用闭包直接代替。高阶函数的使用,可以简化一些算法的实现,并且能有效的减少一些多余的中间变量。也因为有高阶函数的存在,使得函数和普通变量站在了同等的地位,这是函数式编程很大的特点。在其它的函数式编程语言中,还会有一些更加高级的函数式特性,相信在不久的将来,这些特性也都会加入到Swift中,参考下面<code>F#</code>的一段代码:</p>
<figure class="highlight fsharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> pa f x = (f (x - <span class="number">1.0</span>), f (x + <span class="number">1.0</span>))</span><br><span class="line"><span class="keyword">let</span> g1 x y = x ** y</span><br><span class="line"><span class="keyword">let</span> h = pa g1 <span class="number">2.0</span> <span class="comment">// 函数的局部应用</span></span><br></pre></td></tr></table></figure>
<h3 id="函数参数"><a href="#函数参数" class="headerlink" title="函数参数"></a>函数参数</h3><p>谈到函数,那不得不说说它的参数了,这也是Swift区别与很多其它语言的地方。在古老的Objective-C中,方法的参数命名与其它同等级语言差别是巨大的,虽然褒贬不一,但不得不说,相比于Java或C#,它的可读性是最强的。苹果似乎一直想要保持这种的<code>代码即文档</code>的作风,所以在Swift中保留了这样类似的特性,并且对它做了简化,对比一下定义即可:</p>
<p><strong>Objective-C</strong>:</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">- (<span class="built_in">BOOL</span>)loginWithUsername:(<span class="built_in">NSString</span> *)username password:(<span class="built_in">NSString</span> *)password;</span><br></pre></td></tr></table></figure>
<p><strong>Swift</strong>:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">loginWithUsername</span><span class="params">(username: String, password: String)</span></span> -&gt; <span class="type">Bool</span></span><br></pre></td></tr></table></figure>
<p>算一算,一共帮你省略了多少字符,这对保护你珍贵Mac键盘还是很有好处的!另外与参数话题相关的,就是<strong>参数默认值</strong>了,因为Swift参数是携有命名的特性,所以参数的默认值并不像其它语言中那样必须放置在最后的几个参数,这又让Swift能对自己拥有命名参数而引以为豪了:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 放置在第一位的默认值</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">loginWithUsername</span><span class="params">(username: String = <span class="string">"admin"</span>, password: String)</span></span> -&gt; <span class="type">Bool</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>然后要说的便是<strong>可变参数列表</strong>了,在Objective-C中也有可变参数列表,比如<code>NSLog</code>中后续的参数,但是,在不进行任何文档查阅和网络搜索的情况下,你能默写出来么?我觉得大多数人都写不出来,而在Swift中,这种情况得以改变,可变参数列表直接与数组使用类似:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">Log</span><span class="params">(format: String, args: String ...)</span></span> &#123;</span><br><span class="line"> <span class="keyword">for</span> arg <span class="keyword">in</span> args &#123;</span><br><span class="line"> <span class="built_in">print</span>(arg)</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>上面代码片段中的<code>args</code>,其实就是一个类似与数组的参数,这比起Objective-C中的使用要简单的很多,也与其它编程语言中类似。</p>
<h3 id="自定义操作符"><a href="#自定义操作符" class="headerlink" title="自定义操作符"></a>自定义操作符</h3><p>自定义操作符是一个非常酷的特性,它可以帮我们<strong>将一些嵌套调用的代码变得更加清晰</strong>,在Swift中,操作符其实就是一个特定的函数,这也是与众多函数式编程语言保持一致的地方。假设我们要做一个图片滤镜的程序,也就是说,可以对图片应用各种滤镜效果,那么应该有以下这样类似的代码:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Image</span> </span>&#123;</span><br><span class="line"> <span class="keyword">var</span> filterNames = [<span class="type">String</span>]()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">protocol</span> <span class="title">ImageFilter</span> </span>&#123;</span><br><span class="line"> <span class="function"><span class="keyword">func</span> <span class="title">apply</span><span class="params">(image: Image)</span></span> -&gt; <span class="type">Image</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">GrayFilter</span> : <span class="title">ImageFilter</span> </span>&#123;</span><br><span class="line"> <span class="function"><span class="keyword">func</span> <span class="title">apply</span><span class="params">(image: Image)</span></span> -&gt; <span class="type">Image</span> &#123;</span><br><span class="line"> image.filterNames.append(<span class="string">"gray"</span>)</span><br><span class="line"> <span class="keyword">return</span> image</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">BlurFilter</span> : <span class="title">ImageFilter</span> </span>&#123;</span><br><span class="line"> <span class="function"><span class="keyword">func</span> <span class="title">apply</span><span class="params">(image: Image)</span></span> -&gt; <span class="type">Image</span> &#123;</span><br><span class="line"> image.filterNames.append(<span class="string">"blur"</span>)</span><br><span class="line"> <span class="keyword">return</span> image</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ContrastFilter</span> : <span class="title">ImageFilter</span> </span>&#123;</span><br><span class="line"> <span class="function"><span class="keyword">func</span> <span class="title">apply</span><span class="params">(image: Image)</span></span> -&gt; <span class="type">Image</span> &#123;</span><br><span class="line"> image.filterNames.append(<span class="string">"contrast"</span>)</span><br><span class="line"> <span class="keyword">return</span> image</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>当我们要对图片应用滤镜时,则可能会写出类似下面这样的嵌套调用:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> image = <span class="type">Image</span>()</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> gray = <span class="type">GrayFilter</span>()</span><br><span class="line"><span class="keyword">let</span> blur = <span class="type">BlurFilter</span>()</span><br><span class="line"><span class="keyword">let</span> contrast = <span class="type">ContrastFilter</span>()</span><br><span class="line"></span><br><span class="line">contrast.apply(blur.apply(gray.apply(image)))</span><br></pre></td></tr></table></figure>
<p>这时候,我们可以用自定义操作符来解开这样的嵌套,类似下面代码:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">infix</span> <span class="keyword">operator</span> |&gt; &#123; <span class="keyword">associativity</span> <span class="keyword">left</span> <span class="keyword">precedence</span> <span class="number">140</span> &#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> |&gt; <span class="params">(<span class="keyword">left</span>: Image, <span class="keyword">right</span>: ImageFilter)</span></span> -&gt; <span class="type">Image</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">right</span>.apply(<span class="keyword">left</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> outputImage = image |&gt; gray |&gt; blur |&gt; contrast</span><br><span class="line"><span class="built_in">print</span>(outputImage.filterNames)</span><br></pre></td></tr></table></figure>
<p>通过自定义<code>|&gt;</code>这样一个操作符,我们使用者的代码表述性变得更强,也将相关性的处理放置在了同一条语句里。这种感觉是不是非常棒?似乎已经看到你们在YY一些奇怪的操作符了。关于自定义操作符的语法,这里简单的说明下:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">infix</span> <span class="keyword">operator</span> |&gt; &#123; <span class="keyword">associativity</span> <span class="keyword">left</span> <span class="keyword">precedence</span> <span class="number">140</span> &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">infix</span> : 代表操作符类型,可以为 <span class="keyword">prefix</span>(前置) <span class="keyword">infix</span>(中置) <span class="keyword">postfix</span>(后置)</span><br><span class="line"><span class="keyword">operator</span> : 固定关键字</span><br><span class="line">|&gt; : 要定义的操作符</span><br><span class="line"><span class="keyword">associativity</span> : 可以为 <span class="keyword">left</span> <span class="keyword">right</span> <span class="keyword">none</span>,表示当两个同等优先级的中置操作符出现时,优先使用哪个。如果为 <span class="keyword">none</span> 则不能将操作符连接。</span><br><span class="line"><span class="keyword">precedence</span> : 操作符的优先级,值越大,优先级越高,+ 的优先级为 <span class="number">140</span></span><br></pre></td></tr></table></figure>
<p>介绍完操作符,函数这块的内容应该也可以告一段落了,接下来介绍下属性相关的特性!</p>
<h2 id="强大的属性"><a href="#强大的属性" class="headerlink" title="强大的属性"></a>强大的属性</h2><p>属性在面向对象的设计中,也是非常重要的一个概念,属性是对对象某种状态值的抽象,比如颜色、大小、重量等。在Objective-C中,属性又称之为自动合成属性,因为是编译器将<strong>设置</strong>和<strong>获取</strong>方法,按照属性的关键字进行自动合成的。这种方式在其它语言里也很常见,比如C#的属性也是类似,可以通过反射获取到单独的设置和获取方法。Swift中的属性也是传承了Objective-C属性的一些特性,并做了一些调整,比如去除了原子性描述,由于目前并没有太多关于Swift运行时的文档,也没有做一些关于这方面的Hack,所以原理性的东西这边就不提及了。</p>
<h3 id="延时属性"><a href="#延时属性" class="headerlink" title="延时属性"></a>延时属性</h3><p>在Objective-C中,我们可以手动的实现一个延时属性,也就是只有当属性第一次被调用时,才真正的去构建属性的实例。这种特性在Swift中,已经在语法层级得到了支持,这样对<strong>处理一些占用内存较大,但又不是很常用的属性</strong>时,能有效的降低内存使用率。下面是在Objective-C中实现延时属性的代码:</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">@interface</span> <span class="title">DataManager</span> : <span class="title">NSObject</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">@property</span> (<span class="keyword">nonatomic</span>, <span class="keyword">strong</span>, <span class="keyword">readonly</span>) <span class="built_in">NSData</span> *data;</span><br><span class="line"></span><br><span class="line"><span class="keyword">@end</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">@implementation</span> <span class="title">DataManager</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">@synthesize</span> data = _data;</span><br><span class="line"></span><br><span class="line">- (<span class="built_in">NSData</span> *)data &#123;</span><br><span class="line"> <span class="keyword">if</span> (_data == <span class="literal">nil</span>) &#123;</span><br><span class="line"> _data = [<span class="built_in">NSData</span> dataWithContentsOfFile:<span class="string">@"a/big/file"</span>];</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> _data;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">@end</span></span><br></pre></td></tr></table></figure>
<p>上面的写法虽然代码量不大,但毕竟会增加工作量,另外这种写法在多线程环境中并不严谨,如果再加上线程互斥的代码,整个延时属性的实现就会有更多的代码量和复杂度。所以,在Swift中,多线程并发的这种需求完全由编译器来保证,那么,我们实现这个延时属性,使用下面的代码即可:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">DataManager</span> </span>&#123;</span><br><span class="line"> <span class="built_in">lazy</span> <span class="keyword">private</span>(<span class="keyword">set</span>) <span class="keyword">var</span> data: <span class="type">NSData</span> = <span class="type">NSData</span>(contentsOfFile: <span class="string">"a/big/file"</span>)!</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>Bingo!!这样的特性太棒了,为我们省略了不少工作量啊!</p>
<h3 id="属性监听"><a href="#属性监听" class="headerlink" title="属性监听"></a>属性监听</h3><p>属性监听也是Swift中从语法层级支持的特性,当然,没有语法层级的支持,我们也可以手动撸出同样效果的代码,但总归是麻烦了点。首先我们要区别<code>KVO</code>和属性监听的区别,<code>KVO</code>是对其它对象属性的变化进行监听,而属性监听是对自身属性的变化进行监听。在语法层支持属性监听,使得我们可以将属性的存储逻辑和监听逻辑分离,这会使得我们代码结构更加清晰。</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">StepCounter</span> </span>&#123;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> step: <span class="type">Int</span> = <span class="number">0</span> &#123;</span><br><span class="line"> <span class="keyword">willSet</span>(newValue) &#123;</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"new value <span class="subst">\(newValue)</span>"</span>)</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">didSet</span> &#123;</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"old value <span class="subst">\(oldValue)</span>"</span>)</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> stepCounter = <span class="type">StepCounter</span>()</span><br><span class="line">stepCounter.step = <span class="number">200</span></span><br><span class="line">stepCounter.step = <span class="number">201</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// new value 200</span></span><br><span class="line"><span class="comment">// old value 0</span></span><br><span class="line"><span class="comment">// new value 201</span></span><br><span class="line"><span class="comment">// old value 200</span></span><br></pre></td></tr></table></figure>
<p>在属性监听代码块里,我们可以获取到新设定的值和原始的值,而且这个块中的操作是线程安全的。所以,我们是可以在这个块里,<strong>做一些与属性值相关的策略逻辑</strong>,比如只在特定某些值下触发的逻辑。</p>
<h3 id="下标"><a href="#下标" class="headerlink" title="下标"></a>下标</h3><p>下标在Objective-C中是使用非正式协议的方式实现的,但对下标类型有所限制,而在Swift中对下标类型和数量是没有什么限制的,并且下标是支持<strong>重载</strong>的。这又给了我们创造的空间,比如用字符串或索引来取自定义配置文件中的值:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ConfigurationFile</span> </span>&#123;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">subscript</span>(key: <span class="type">String</span>) -&gt; <span class="type">String</span> &#123;</span><br><span class="line"> <span class="keyword">get</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"hello"</span></span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">subscript</span>(index: <span class="type">Int</span>) -&gt; <span class="type">String</span> &#123;</span><br><span class="line"> <span class="keyword">get</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"world"</span></span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> config = <span class="type">ConfigurationFile</span>()</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(config[<span class="string">"a key"</span>] + <span class="string">" "</span> + config[<span class="number">1</span>])</span><br></pre></td></tr></table></figure>
<p>下标主要是适用场景是<strong>需要一种能通过索引快速取值</strong>设计,它和属性非常相似,所以将其归类到了属性这一块。</p>
<h2 id="运行时的安全"><a href="#运行时的安全" class="headerlink" title="运行时的安全"></a>运行时的安全</h2><p>前面谈过了类型安全,这里再简单谈谈Swift在运行时是如何处理异常的。与很多传统的编程语言一样,Swift引入了<code>try - catch</code>机制,在Objective-C中也有<code>try</code>和<code>catch</code>,但与其它语言中的不同,Objective-C的异常处理在内存管理上存在泄露的风险。所以我们一直都在用<code>NSError</code>这样的错误处理模型,苹果提供的类库中也都采取了这样的处理方式,而在Swift中,这点得以改进,Swift中的<code>throws</code>函数必须用<code>try</code>去调用,所以很容易在ARC环境下生成<code>retain</code>和<code>release</code>代码,所以再也不用当心内存泄露的问题了。</p>
<p>在Swift中,所有可以抛出的异常,必须实现<code>ErrorType</code>协议,当然<code>NSError</code>实现了这样的协议。而如果我们要自定义异常,则必须使用枚举类型,原因很简单,因为枚举配合它的关联值特别适合做这样的事情:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">enum</span> <span class="title">LoginError</span>: <span class="title">ErrorType</span> </span>&#123;</span><br><span class="line"> <span class="keyword">case</span> <span class="type">InvalidUsername</span>, <span class="type">InvalidPassword</span></span><br><span class="line"> <span class="keyword">case</span> <span class="type">Other</span>(<span class="type">String</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">login</span><span class="params">(username: String, password: String)</span></span> <span class="keyword">throws</span> -&gt; <span class="type">Int</span> &#123;</span><br><span class="line"> <span class="keyword">if</span> username != <span class="string">"makee"</span> &#123;</span><br><span class="line"> <span class="keyword">throw</span> <span class="type">LoginError</span>.<span class="type">InvalidUsername</span></span><br><span class="line"> &#125; <span class="keyword">else</span> <span class="keyword">if</span> password != <span class="string">"sun"</span> &#123;</span><br><span class="line"> <span class="keyword">throw</span> <span class="type">LoginError</span>.<span class="type">InvalidPassword</span></span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> <span class="keyword">throw</span> <span class="type">LoginError</span>.<span class="type">Other</span>(<span class="string">"unknow error"</span>)</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">do</span> &#123;</span><br><span class="line"> <span class="keyword">try</span> login(<span class="string">"makee"</span>, password: <span class="string">"sun"</span>)</span><br><span class="line">&#125; <span class="keyword">catch</span> <span class="type">LoginError</span>.<span class="type">InvalidUsername</span> &#123;</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">&#125; <span class="keyword">catch</span> <span class="type">LoginError</span>.<span class="type">InvalidPassword</span> &#123;</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">&#125; <span class="keyword">catch</span> <span class="type">LoginError</span>.<span class="type">Other</span>(<span class="keyword">let</span> msg) &#123;</span><br><span class="line"> <span class="built_in">print</span>(msg)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>当然,如果确定没有任何异常,我们只要使用<code>try!</code>去调用即可,这样可以省略掉<code>do - cacth</code>这样的代码结构。</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span>! login(<span class="string">"username"</span>, password: <span class="string">"password"</span>)</span><br></pre></td></tr></table></figure>
<p>这时候需要注意,就和可选类型的强制解包一样,如果失败了是会导致Crash的。Swift引入了这样的异常处理机制,虽然在语法的角度上与其它语言大相径庭,但如果从ARC的角度去考虑,就会觉得,这不失为一种很好的妥协。比较Swift没有垃圾回收机制,所有的内存管理都是靠程序本身去处理,有了这样的异常处理机制,我们应该更少的使用<code>nil</code>,这样我们的程序会更加健壮。</p>
<p>与Java的异常处理类似,Swift中的异常处理也存在<strong>冒泡机制</strong>,也就是异常向上传递,这种特性使得我们的异常是可传递的,但为了内存管理考虑,还是必须要使用上<code>try</code>关键字。比如下面的代码:</p>
<figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">test</span><span class="params">()</span></span> <span class="keyword">throws</span> &#123;</span><br><span class="line"> <span class="keyword">try</span> login(<span class="string">"usr"</span>, password: <span class="string">"pwd"</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这时候,调用<code>test</code>函数时,异常是从<code>login</code>冒上来的,这就是异常传递机制。通过这样的设定,我们不会忽略掉任何异常,配合<strong>模式匹配</strong>中所讲述的内容,我们的<code>catch</code>块可控性也是非常灵活的。唯一不足的是,并不能像Java那样知道到底会抛出何种异常,这点和<code>C#</code>中倒是有点类似,也只能靠文档来弥补了。</p>
<h2 id="最后再说一点"><a href="#最后再说一点" class="headerlink" title="最后再说一点"></a>最后再说一点</h2><p>作为单篇文章,本次所讲述的内容可能有点过多,因为我觉得Swift真的有太多比Objective-C强大的地方,也非常愿意作为Swift的传道士。碍于篇幅,这篇文章中还有很多Swift的小特性没有提及,比如表达式的<code>where</code>子句,枚举类型的<code>rawValue</code>等,这些就留给在座各位自己去摸索了。</p>
<p>本篇文章中,我想要达到的目的并不仅仅是让在座各位了解到Swift的特性,更希望能让大家明白在什么场景下去使用这样的特性。所以文章中花了很大的篇幅描述使用场景和我认为的设计初衷,语法细节都是很简单的略过了,因为语法是很容易从官方文档中找到说明的,而使用场景和相应的一些思想是很难从文档上找到的。</p>
<p>那么,本篇就到这里了,希望大家能够有所收获,<strong>一同学习,成就更好的自己</strong>!</p>
</content>
<summary type="html">
<p><code>Swift</code>,已经出来一年多了,想必不少人都曾对它抱着观望的态度,如果现在的你还是这样的态度,那么是时候去改变了。作为一个年龄比我还大的语言————<code>Objective-C</code>,是时候淡出历史的舞台了,有人说<code>Objective-C</code>和<code>Swift</code>会并存,但我觉得,短期内会这样,从长远的角度来看,并存的可能性并不大。毕竟,它们不是<code>C</code>和<code>C++</code>这样的关系,它们更像<code>Delphi</code>与<code>C#</code>这样的存在。从可维护的角度来说,并存的代价比较大,所以,对于<code>Objective-C</code>,放手吧。</p>
</summary>
<category term="iOS开发" scheme="http://yoursite.com/categories/iOS%E5%BC%80%E5%8F%91/"/>
<category term="Swift" scheme="http://yoursite.com/tags/Swift/"/>
</entry>
</feed>