-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
517 lines (506 loc) · 140 KB
/
search.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
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[iOS Universal Links]]></title>
<url>http://raykle.coding.me/2017/04/16/iOS-Universal-Links/</url>
<content type="html"><![CDATA[<h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p>iOS 9 以前,从外部启动 App 都是通过 <code>URL Scheme</code> 实现跳转的。这种方式虽然可自定程度很高,能够巧妙地实现很多跳转,但是也存在很多弊端。</p>
<p>在 2015 年苹果 WWDC 上,苹果宣布了 <code>Universal Links</code> 这项新技术,实现了在 iOS 9 及之后的系统上,在只做少量额外开发的条件下,不同 App 之间能够直接、顺畅、无缝衔接的跳转,让用户体验又提升了一个级别!</p>
<hr>
<h2 id="URL-Scheme-VS-Universal-Links"><a href="#URL-Scheme-VS-Universal-Links" class="headerlink" title="URL Scheme VS Universal Links"></a>URL Scheme VS Universal Links</h2><h3 id="URL-schemes"><a href="#URL-schemes" class="headerlink" title="URL schemes"></a>URL schemes</h3><p>非常有效,能够让 app 之间彼此交流,传递数据。</p>
<h4 id="缺陷"><a href="#缺陷" class="headerlink" title="缺陷"></a>缺陷</h4><ul>
<li>需要提前加入到白名单,且会询问用户“是否打开‘xxx’应用”。</li>
<li>不会总能映射到正确的 app,两个 app 可能拥有同样的 scheme,而开发者不能明确地表示他们指的是那个 app。</li>
<li>app 没有安装的时候不能工作。</li>
<li>不能有效地保护用户隐私。app 需要查明是否已经被安装在设备上,这意味着它可以嗅探出用户是否安装了某些 app,而这本应属于用户的个人信息。</li>
</ul>
<hr>
<h3 id="Universal-Links"><a href="#Universal-Links" class="headerlink" title="Universal Links"></a>Universal Links</h3><p>Universal Links 是苹果在 2015 年 WWDC 上推出的一项新功能,官方功能描述如下:</p>
<blockquote>
<p>In iOS 9 and later, universal links let users open your app when they tap links to your website within WKWebView and UIWebView views and Safari pages, in addition to links that result in a call to openURL:, such as those that occur in Mail, Messages, and other apps.</p>
</blockquote>
<p>即当用户在 WKWebView、UIWebView 或者 Safari 中点击一个链接,如果设备上安装了适配该链接的 app,就可以跳转该 app 对应的页面,否则仍然展示网页 。<br>由于目前微信内置浏览器不支持 openURL 的方式进行应用间的跳转,不少 app 都是通过接入 Universal Links 实现微信浏览器一键跳转到自己 app 的功能,比如网易新闻,知乎等。</p>
<h4 id="优点"><a href="#优点" class="headerlink" title="优点"></a>优点</h4><p>相比于 Custom URL Scheme,官方文档中给出了几个优点:</p>
<ul>
<li><strong>Unique.</strong> Unlike custom URL schemes, universal links can’t be claimed by other apps, because they use standard HTTP or HTTPS links to your website.</li>
<li><strong>Secure.</strong> When users install your app, iOS checks a file that you’ve uploaded to your web server to make sure that your website allows your app to open URLs on its behalf. Only you can create and upload this file, so the association of your website with your app is secure.</li>
<li><strong>Flexible.</strong> Universal links work even when your app is not installed. When your app isn’t installed, tapping a link to your website opens the content in Safari, as users expect.</li>
<li><strong>Simple.</strong> One URL works for both your website and your app.</li>
<li><strong>Private.</strong> Other apps can communicate with your app without needing to know whether your app is installed.</li>
</ul>
<p>即:</p>
<ul>
<li><strong>独特性</strong> 与自定义的URL链接相比,通用链接不能被其他的应用程序所访问,因为它们使用标准的 HTTP 或 HTTPS 链接到您的网站。</li>
<li><strong>安全性</strong> 当用户安装您的应用程序时,iOS 会检查您已上传到 Web 服务器的文件,以确保您的网站允许您的 app 代表其打开 URL (点击一个 URL 链接时,直接打开相应 app)。 只有您可以创建和上传此文件,因此您的网站与您的应用程序的关联是安全的。</li>
<li><strong>灵活性</strong> 即使没有安装 app,Universal links 也可以正常工作。 当没有安装 app 时,点击一个指向您网站的链接,会按照用户的期望在 Safari 中打开。</li>
<li><strong>简单性</strong> 通用链接同时适用于你的网站和 app。</li>
<li><strong>私有性</strong> 其他应用程序可以与您的应用程序通信,而无需知道您的应用程序是否已安装。</li>
</ul>
<h4 id="缺陷-1"><a href="#缺陷-1" class="headerlink" title="缺陷"></a>缺陷</h4><ul>
<li>至少 iOS 9 以上的系统才支持 UL。</li>
</ul>
<hr>
<h2 id="UL-开发流程"><a href="#UL-开发流程" class="headerlink" title="UL 开发流程"></a>UL 开发流程</h2><h3 id="准备"><a href="#准备" class="headerlink" title="准备"></a>准备</h3><ul>
<li>一个 HTTPS 的后台域名</li>
<li>可以上传一个 JSON 文件到服务器的权限</li>
<li>iOS 9 at least</li>
<li>Xcode 7 at least</li>
</ul>
<h3 id="iOS-端"><a href="#iOS-端" class="headerlink" title="iOS 端"></a>iOS 端</h3><h4 id="1-添加-Associated-Domain"><a href="#1-添加-Associated-Domain" class="headerlink" title="1. 添加 Associated Domain"></a>1. 添加 Associated Domain</h4><p>在开发者网站找到想要开启 UL 的 App ID(<code>Account -> Certificates, Identifiers & Profiles -> App IDs -> YourAppId</code>),点击 <code>Edit</code> 进入编辑页面,找到 <code>Associated Domains</code> 将对应的复选框选中,然后点击 <code>Done</code> 就可以了。</p>
<p><img src="media/790890-40795a68e8fe351f.jpg" alt="Associated Domains in developer.apple.com"></p>
<blockquote>
<p>如果有对应的 <code>Provision Profiles</code>,记得点击 <code>Edit</code>,重新 <code>Generate</code>,并 <code>Download</code> 更新到 <code>Xcode</code> 中。</p>
</blockquote>
<p>然后,在 <code>Xcode</code> 中,<code>TARGETS -> YourTarget -> Capabilities -> Associated Domains</code>,打开此功能开关。<br>点击 <code>+</code> 添加需要支持跳转的 <code>domain</code>,需要以 <code>applinks:</code> 为前缀;同时可以添加一些子域名。比如:<code>applinks:www.example.com</code>,<code>applinks:news.example.com</code> 等。</p>
<p><img src="media/14920951359976.jpg" alt="Associated Domains in Xcode"></p>
<p><strong><em>Tips:</em></strong><br>做完上面这些步骤,你的 app 将能够从你指定的这些域名请求一个特殊的 <code>JSON</code> 文件:<code>apple-app-site-association</code>。当你 <strong>第一次安装,或者版本更新</strong> 的时候,设备将从指定的域名请求文件:<code>https://www.example.com/.well-known/apple-app-site-association</code>,如果不存在,则会请求:<code>https://www.example.com/apple-app-site-association</code>。</p>
<h4 id="2-创建-apple-app-site-association-文件"><a href="#2-创建-apple-app-site-association-文件" class="headerlink" title="2. 创建 apple-app-site-association 文件"></a>2. 创建 apple-app-site-association 文件</h4><p>格式及内容如下:</p>
<figure class="highlight"><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><br><span class="line"> "applinks" : {</span><br><span class="line"> "apps" : [],</span><br><span class="line"> "details" : [</span><br><span class="line"> //如果有不止一个 app,或者 app 有多种环境(比如:dev,pro),可以在这个数组中添加多个元素。</span><br><span class="line"> {</span><br><span class="line"> //appID: App Prefix(Team ID) + Bundle ID</span><br><span class="line"> "appID" : "ABCDE12345.com.apple.wwdc",</span><br><span class="line"> "paths" : [</span><br><span class="line"> //路径数组:告知我们 网站的哪部分可以通过 app 支持。</span><br><span class="line"> //如果整个网站都可以通过,可以直接使用 `*`。</span><br><span class="line"> "*", //全部网站都支持</span><br><span class="line"> "/wwdc/news/", //`完全匹配`</span><br><span class="line"> "/videos/wwdc/2015/*" // `路径前缀`</span><br><span class="line"> ]</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> "appID" : "HIJKL67890.com.apple.wwdc",</span><br><span class="line"> "paths" : [</span><br><span class="line"> //"?" 表示替换路径中的一个字符</span><br><span class="line"> "/foo/*/bar/201?/mypage",</span><br><span class="line"> // 支持使用 `NOT` 关键字,指定不作为通用链接处理的区域</span><br><span class="line"> "NOT /path/to/exclude"</span><br><span class="line"> ]</span><br><span class="line"> }</span><br><span class="line"> ]</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<blockquote>
<p><strong>注意:</strong><br><code>appID</code> 由开发者账号的 <code>Team ID</code> 和 app 的 <code>Bundle ID</code> 构成。<br><code>paths</code> 是<code>大小写敏感</code>的!其他 URL 组件比如 <code>query</code>,<code>fragment</code> 则不是。</p>
<p>此文件名必须为 <code>apple-app-site-association</code>,不能有后缀。</p>
<p>必须为每一个你的 app 支持的拥有独立内容的 <code>domain</code> 创建单独的 <code>apple-app-site-association</code> 文件,比如 <code>apple.com</code> 和 <code>developer.apple.com</code>,需要单独的 <code>apple-app-site-association</code> 文件,因为这些域名提供不同的服务。</p>
</blockquote>
<h4 id="3-iOS-实现代码"><a href="#3-iOS-实现代码" class="headerlink" title="3. iOS 实现代码"></a>3. iOS 实现代码</h4><p>在 <code>AppDelegate.m</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><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><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// https://developer.apple.com/videos/play/wwdc2015/509/</span></span><br><span class="line">- (<span class="built_in">BOOL</span>)application:(<span class="built_in">UIApplication</span> *)application continueUserActivity:(<span class="built_in">NSUserActivity</span> *)userActivity restorationHandler:(<span class="keyword">void</span> (^)(<span class="built_in">NSArray</span> * _Nullable))restorationHandler {</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> ([userActivity.activityType isEqualToString:<span class="built_in">NSUserActivityTypeBrowsingWeb</span>]) {</span><br><span class="line"> </span><br><span class="line"> <span class="built_in">NSURL</span> *webUrl = userActivity.webpageURL;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (![<span class="keyword">self</span> presentUrl:webUrl]) {</span><br><span class="line"> <span class="comment">// 当跳转到 app 不能跳转到相应的界面时,苹果建议优雅地解决这种错误,</span></span><br><span class="line"> <span class="comment">// 即:使用 Safari 打开该链接,避免 app 中可能出现空白界面等异常情况。</span></span><br><span class="line"> [[<span class="built_in">UIApplication</span> sharedApplication] openURL:webUrl];</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">YES</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">- (<span class="built_in">BOOL</span>)presentUrl:(<span class="built_in">NSURL</span> *)url {</span><br><span class="line"> <span class="built_in">NSURLComponents</span> *urlComponents = [<span class="built_in">NSURLComponents</span> componentsWithURL:url resolvingAgainstBaseURL:<span class="literal">YES</span>];</span><br><span class="line"> </span><br><span class="line"> <span class="built_in">NSString</span> *host = urlComponents.host;</span><br><span class="line"> <span class="built_in">NSArray</span> *pathComponent = urlComponents.path.pathComponents;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 此处根据不同的业务需求进行判断</span></span><br><span class="line"> <span class="keyword">if</span> ([host isEqualToString:<span class="string">@"www.example.com"</span>]) {</span><br><span class="line"> <span class="keyword">if</span> (pathComponent.count > <span class="number">4</span>) {</span><br><span class="line"> <span class="comment">//<span class="doctag">TODO:</span> Do some router here.</span></span><br><span class="line"> <span class="keyword">return</span> <span class="literal">YES</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">NO</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h4 id="4-从认可的证书签发机构获取-SSL-证书"><a href="#4-从认可的证书签发机构获取-SSL-证书" class="headerlink" title="4. 从认可的证书签发机构获取 SSL 证书"></a>4. 从认可的证书签发机构获取 SSL 证书</h4><h4 id="5-签名-JSON-文件"><a href="#5-签名-JSON-文件" class="headerlink" title="5. 签名 JSON 文件"></a>5. 签名 JSON 文件</h4><p>使用上一步的 SSL 证书对 JSON 文件进行签名。需要是用 Mac 终端的 <code>openssl smime</code> 命令。</p>
<figure class="highlight ruby"><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">openssl smime \</span><br><span class="line"> -sign \</span><br><span class="line"> -nodetach \</span><br><span class="line"> -<span class="keyword">in</span> <span class="string">"unsigned.json"</span> \ <span class="comment"># JSON file 文件名(未签名的 JSON 文件)</span></span><br><span class="line"> -out <span class="string">"apple-app-site-association"</span> \ <span class="comment"># 这里的名称必须完全匹配</span></span><br><span class="line"> -outform DER \</span><br><span class="line"> -inkey <span class="string">"private-key.pem"</span> \ <span class="comment"># 证书生成期间生成的私钥</span></span><br><span class="line"> -signer <span class="string">"certificate.pem"</span> <span class="comment"># 证书签发机构提供的证书</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>iOS 9 Beta 2 版本开始,<strong><em>👉 不再需要此签名步骤 👈</em></strong>,只需要上传未签名过的 <code>apple-app-site-association</code> 文件到 <code>HTTPS</code> 服务器即可,iOS 会处理其余工作。</p>
</blockquote>
<hr>
<h3 id="Server-端"><a href="#Server-端" class="headerlink" title="Server 端"></a>Server 端</h3><blockquote>
<p>Upload it to the root of your HTTPS web server. The file needs to be accessible via HTTPS—without any redirects—at <a href="https:///apple-app-site-association" target="_blank" rel="external">https:///apple-app-site-association</a>. Next, you need to handle universal links in your app.</p>
</blockquote>
<p>将 <code>apple-app-site-association</code> 文件存放在服务器跟目录,或者根目录的 <code>.well-known</code> 目录下。比如:</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">https://www.example.com/.well-known/apple-app-site-association</span><br></pre></td></tr></table></figure>
<blockquote>
<p>建议存放于 <code>.well-known</code> 目录下,以减少访问次数,因为设备会优先请求 <code>.well-known</code> 目录,如果没有,才会去根目录下请求。</p>
<p><strong><em>注意:</em></strong><br><code>apple-app-site-association</code> 文件的访问必须不支持重定向。<br>服务器上 <code>apple-app-site-association</code> 的更新不会让 iOS 本地的 <code>apple-app-site-association</code> 同步更新,即 iOS 只会在 App 第一次启动时请求一次,以后除非 App 更新或重新安装否则不会在每次打开时请求 <code>apple-app-site-association</code>。</p>
</blockquote>
<hr>
<h3 id="前端"><a href="#前端" class="headerlink" title="前端"></a>前端</h3><h4 id="Smart-App-Banner"><a href="#Smart-App-Banner" class="headerlink" title="Smart App Banner"></a>Smart App Banner</h4><h5 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h5><p><code>Smart App Banner</code> 是在 <code>Safari</code> 中浮动在页面顶端的一个 <code>Banner</code>,它是一个对网页无侵入的 UI 元素,可以在 <code>Safari</code> 自动呈现。</p>
<p>如果用户未安装 app,点击自动跳转到 App Store 的下载页。如果已安装,则会自动启动相应的 app。</p>
<p><img src="media/IMG_1036.PNG" alt="Smart App Banner in zhihu.com"></p>
<h5 id="让网页支持-Smart-App-Banner-的显示"><a href="#让网页支持-Smart-App-Banner-的显示" class="headerlink" title="让网页支持 Smart App Banner 的显示"></a>让网页支持 Smart App Banner 的显示</h5><p>在网站的 <code>head</code> 标签中添加一个 <code>meta</code> 的标签。</p>
<figure class="highlight html"><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="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"apple-itunes-app"</span> <span class="attr">content</span>=<span class="string">"app-id=123456789, app-argument=https://developer.apple.com/wwdc/schedule, affiliate-data=optionalAffiliateData"</span>></span></span><br><span class="line"><span class="tag"></<span class="name">head</span>></span></span><br></pre></td></tr></table></figure>
<p><code>name="apple-itunes-app"</code> 会告诉 Safari 你想要显示 <code>Smart App Banner</code>。<br><code>content</code> 包含 <code>app-id</code>,<code>app id</code> 是每个 app 在 <code>iTunesConnect</code> 网站创建之后,获取到的一个 <code>id</code>。此处的功能是:Safari 会通过此 <code>id</code> 向 <code>App Store</code> 抓取 app 的信息显示在 <code>Smart App Banner</code> 中,包括 app 名称,厂商等。当用户没有安装相应的 app 时,点击之后会根据此 <code>id</code> 跳转到 <code>App Store</code> 进入相应的下载页。<br><code>app-argument</code> 属性应该包括当前显示页面的 <code>URL</code>,或者自定义的一些参数等,当用户点击 <code>Banner</code> 打开 app,此参数值会被传递到 iOS 的<br><code>-application:continueUserActivity:restorationHandler:</code><br>方法中,通过 <code>userActivity.webpageURL</code> 可以获取到。</p>
<p>可以通过扒取别人的网页代码,看看一些其他大公司的例子。<br>比如:</p>
<figure class="highlight html"><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><br><span class="line"><span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string"></span>"<span class="attr">apple-itunes-app</span>" <span class="attr">content</span>=<span class="string"></span>"<span class="attr">app-id</span>=<span class="string">425349261,</span> <span class="attr">affiliate-data</span>=<span class="string">myAffiliateData,</span> <span class="attr">app-argument</span>=<span class="string">newsapp://doc/CHLFGFCB00097U7S?s</span>=<span class="string">sps_article</span>"></span></span><br><span class="line"></span><br><span class="line">//知乎:</span><br><span class="line"><span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string"></span>"<span class="attr">apple-itunes-app</span>" <span class="attr">content</span>=<span class="string"></span>"<span class="attr">app-id</span>=<span class="string">432274380,</span> <span class="attr">app-argument</span>=<span class="string">zhihu://answers/154659733</span>" <span class="attr">data-react-helmet</span>=<span class="string"></span>"<span class="attr">true</span>"></span></span><br><span class="line"></span><br><span class="line">//多看</span><br><span class="line"><span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string"></span>"<span class="attr">apple-itunes-app</span>" <span class="attr">content</span>=<span class="string"></span>"<span class="attr">app-id</span>=<span class="string">517850153,</span> <span class="attr">app-argument</span>=<span class="string">duokan-reader://store/book/09d88212e38611e18f0200163e0123ac</span>"></span></span><br></pre></td></tr></table></figure>
<p><em>可以关注一下,这些代码中 <code>app-argument</code> 的值的写法。</em></p>
<p><code>Smart App Banner</code> 在模拟器中也会出现,但显示的是一个空白的 <code>Banner</code>,app 名称等信息不会显示。</p>
<blockquote>
<p><em>温馨提示:</em><br>如果一个网站的域名是被 iOS 开发人员配置在了 <code>Associated Domains</code> 中的,且在该域名上传了 <code>apple-app-site-association</code> 的文件,那么当一台 iOS 设备安装了相应的 app 之后,在 <code>Safari</code> 浏览器打开该域名下的网址,则会自动显示 <code>Smart App Banner</code>,网页不用做任何额外开发,包括添加 <code>meta</code> 标签。但是这种情况有一个 <strong>缺点</strong>,就是如果没有安装相应的 app,则 <code>Smart App Banner</code> 不会显示。</p>
<p>如果想要 <strong>时刻显示</strong> <code>Smart App Banner</code>,则前端开发人员必须手动添加 <code>meta</code> 标签。</p>
</blockquote>
<p>当前端开发手动添加了 <code>meata</code> 标签,则在网页中显示的 <code>banner</code> 左边会有一个 <code>X</code> 按钮,允许用户关闭掉这个 <code>banner</code>,而一旦用户关闭了,则在这个网站上就不会再显示了,即便重新加载网页也一样。</p>
<p>目前唯一能够让 <code>Smart App Banner</code> 再次显示的方法就是 <strong>还原所有设置</strong> (设置 -> 通用 -> 还原 -> 还原所有设置)。具体请查看 <a href="https://www.raywenderlich.com/80347/smart-app-banners-tutorial" target="_blank" rel="external">raywenderlich - Smart App Banners Tutorial</a> 和 <a href="https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariWebContent/PromotingAppswithAppBanners/PromotingAppswithAppBanners.html" target="_blank" rel="external">Apple - Promoting Apps with Smart App Banners</a></p>
<h4 id="自定义链接支持-UL-跳转"><a href="#自定义链接支持-UL-跳转" class="headerlink" title="自定义链接支持 UL 跳转"></a>自定义链接支持 UL 跳转</h4><p>如果想要在网页中支持自定义 UL 跳转,比如在底部浮动一个 <code>banner</code> ,上面显示 <code>打开 App</code>,在安装了相应的 app 的情况下,如果想要达到点击这个链接自动跳转到该 app 的目的,此时这个 <code>banner</code> 的跳转链接必须是之前 <code>Xcode</code> 中配置的 <code>Associated Domains</code> 中域名下的链接。</p>
<h5 id="跨域条件"><a href="#跨域条件" class="headerlink" title="* 跨域条件"></a>* 跨域条件</h5><p>想要达到自定义跳转的目的,必须满足 <strong>跨域</strong> 的条件,举例假设当前 Safari 或者微信浏览器中网页链接为 <code>www.baidu.com/XXX/YYY</code>,底部“打开App”按钮对应的链接URL为 <code>www.baidu.com/AAA/BBB</code>,由于两者都在百度的域名下,因此实际不能完成跳转到 app。</p>
<h5 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h5><p>由于对前端开发不熟,所以没怎么研究,这里提供两个前端使用 UL 的参考链接。</p>
<p><a href="https://juejin.im/post/57b6ba9f128fe10054bb5401" target="_blank" rel="external">浏览器中唤起native app || 跳转到应用商城下载(一)</a><br><a href="https://juejin.im/post/57bd1c3079bc440063b38f9d" target="_blank" rel="external">浏览器中唤起native app || 跳转到应用商城下载(二) 之 universal links</a></p>
<hr>
<h2 id="注意点及总结"><a href="#注意点及总结" class="headerlink" title="注意点及总结"></a>注意点及总结</h2><h3 id="注意点"><a href="#注意点" class="headerlink" title="注意点"></a>注意点</h3><ol>
<li>iOS 9.3 之后必须跨域才能支持 UL 跳转。</li>
<li>服务端必须是 HTTPS。</li>
<li><code>apple-app-site-association</code> 文件的访问必须不支持重定向。</li>
</ol>
<h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>放一张自己总结的简单的 UL 工作流程图,方便理解跟回顾:</p>
<p><img src="media/Apple%20Universal%20Links%20Work%20Flow.png" alt="Apple Universal Links Work Flow"></p>
<hr>
<h2 id="补充"><a href="#补充" class="headerlink" title="补充"></a>补充</h2><h3 id="域名验证"><a href="#域名验证" class="headerlink" title="域名验证"></a>域名验证</h3><p>苹果官方提供了网址验证工具:<a href="https://search.developer.apple.com/appsearch-validation-tool/" target="_blank" rel="external">App Search API Validation Tool</a><br>用于验证 UL 的条件有没有满足:</p>
<ol>
<li>服务器后台正确上传了 <code>apple-app-site-association</code> 文件。</li>
<li>iOS 端开启且正确配置了 <code>Associated Domains</code>。</li>
</ol>
<p>当验证通过,但是你的 app 还没有发布到 App Store 时显示的结果是:<br><img src="media/QQ20170414-023032@2x.png" alt=""></p>
<p>如果 app 已经发布,且验证通过,则显示的是 PASSED 状态:<br><img src="media/716949-1755979ce9d20f91.jpeg" alt=""></p>
<h3 id="第三方支持-UL-跳转的-APP-平台"><a href="#第三方支持-UL-跳转的-APP-平台" class="headerlink" title="第三方支持 UL 跳转的 APP/平台"></a>第三方支持 UL 跳转的 APP/平台</h3><p>非完整版 : )</p>
<table>
<thead>
<tr>
<th style="text-align:center">App Name</th>
<th style="text-align:center">是否支持 UL 跳转</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">微博</td>
<td style="text-align:center">✔︎</td>
</tr>
<tr>
<td style="text-align:center">微信</td>
<td style="text-align:center">✔︎</td>
</tr>
<tr>
<td style="text-align:center">Safari</td>
<td style="text-align:center">✔︎</td>
</tr>
<tr>
<td style="text-align:center">QQ</td>
<td style="text-align:center">✔︎</td>
</tr>
<tr>
<td style="text-align:center">手机百度</td>
<td style="text-align:center">✔︎</td>
</tr>
<tr>
<td style="text-align:center">UC 浏览器</td>
<td style="text-align:center">✔︎</td>
</tr>
<tr>
<td style="text-align:center">支付宝</td>
<td style="text-align:center">✔︎</td>
</tr>
<tr>
<td style="text-align:center">QQ 浏览器</td>
<td style="text-align:center">✗</td>
</tr>
<tr>
<td style="text-align:center">Chrome 浏览器</td>
<td style="text-align:center">✗</td>
</tr>
<tr>
<td style="text-align:center">…</td>
<td style="text-align:center">…</td>
</tr>
</tbody>
</table>
<h3 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h3><p>当在 Safari 或者第三方分享的平台中点击 UL 链接打开 app 后,会在 app 右上角看到类似 <code>example.com</code> 的箭头指示,如果点击它会跳转到 <code>Safari</code> 打开之前点击的链接,同时系统会认为你选择使用 <code>Safari</code> 打开该域名的链接,而不是 app。那么下次你再点击该链接,它只会在 <code>Safari</code> 里面加载该链接。那么如何再次开启 app 跳转呢?在 <code>Safari</code> 页面中,手指往下拉动显示出 <code>Smart App Banner</code>,点击右侧的 <code>打开</code> 按钮,就会跳转到 app,此时系统就会认为你选择使用 app 跳转,下次再次点击链接时也会直接跳转到 app 了。</p>
<p><img src="media/IMG_1105.PNG" alt="-w300"> <img src="media/IMG_1107.PNG" alt="-w300"></p>
<h3 id="参考链接-1"><a href="#参考链接-1" class="headerlink" title="参考链接"></a>参考链接</h3><ul>
<li><p>iOS:<br> <a href="https://developer.apple.com/library/content/documentation/General/Conceptual/AppSearch/UniversalLinks.html" target="_blank" rel="external">https://developer.apple.com/library/content/documentation/General/Conceptual/AppSearch/UniversalLinks.html</a><br> <a href="https://developer.apple.com/videos/play/wwdc2015/509/" target="_blank" rel="external">https://developer.apple.com/videos/play/wwdc2015/509/</a></p>
<p> <a href="http://www.jianshu.com/p/c2ca5b5f391f" target="_blank" rel="external">http://www.jianshu.com/p/c2ca5b5f391f</a><br> <a href="http://www.jianshu.com/p/d1e0c1886046" target="_blank" rel="external">http://www.jianshu.com/p/d1e0c1886046</a><br> <a href="http://www.jianshu.com/p/aeca84ea2624" target="_blank" rel="external">http://www.jianshu.com/p/aeca84ea2624</a></p>
<p> <a href="http://stackoverflow.com/questions/35609667/how-to-support-universal-links-in-ios-app-and-setup-server-for-it/35609668#35609668" target="_blank" rel="external">http://stackoverflow.com/questions/35609667/how-to-support-universal-links-in-ios-app-and-setup-server-for-it/35609668#35609668</a><br> <a href="http://stackoverflow.com/questions/32751225/ios9-universal-links-does-not-work" target="_blank" rel="external">http://stackoverflow.com/questions/32751225/ios9-universal-links-does-not-work</a></p>
<p> <a href="http://blog.hokolinks.com/how-to-implement-apple-universal-links-on-ios-9/" target="_blank" rel="external">HOKO - Breaking down iOS 9 Universal Links</a></p>
</li>
<li><p>Android:<br> <a href="http://blog.hokolinks.com/android-m-app-links-implementation-drawbacks/" target="_blank" rel="external">HOKO - Android M App Links: implementation, drawbacks and solutions</a></p>
</li>
<li><p>前端:<br> <a href="https://juejin.im/post/57b6ba9f128fe10054bb5401" target="_blank" rel="external">浏览器中唤起native app || 跳转到应用商城下载(一)</a><br> <a href="https://juejin.im/post/57bd1c3079bc440063b38f9d" target="_blank" rel="external">浏览器中唤起native app || 跳转到应用商城下载(二) 之 universal links</a></p>
</li>
</ul>
<hr>
]]></content>
</entry>
<entry>
<title><![CDATA[使用 Moco 轻松搭建测试服务器]]></title>
<url>http://raykle.coding.me/2016/09/03/%E4%BD%BF%E7%94%A8-Moco-%E8%BD%BB%E6%9D%BE%E6%90%AD%E5%BB%BA%E6%B5%8B%E8%AF%95%E6%9C%8D%E5%8A%A1%E5%99%A8/</url>
<content type="html"><![CDATA[<p>新公司的项目使用了 <a href="http://rap.taobao.org/" target="_blank" rel="external">RAP</a> 这一阿里推出的可视化接口管理工具,通过在上面定义接口,RAP 可以通过分析接口结构,以一系列自动化工具帮助开发人员提升效率。重要的是 RAP 通过 Mock 服务可以生成模拟数据。</p>
<p>以上的这些都不是讨论的重点,本篇文章主要是记录如何通过使用 Moco 搭建本地服务器,以达到在接口定义初期,就可以通过 Moco 调试接口,推进移动开发的进程。</p>
<h2 id="Moco-介绍"><a href="#Moco-介绍" class="headerlink" title="Moco 介绍"></a>Moco 介绍</h2><p>Moco 是一个基于 Java 开发的开源项目,在 GitHub 上有不少的关注:<a href="/~https://github.com/dreamhead/moco" target="_blank" rel="external">/~https://github.com/dreamhead/moco</a></p>
<blockquote>
<p>Moco is an easy setup stub framework.</p>
</blockquote>
<h2 id="快速开始"><a href="#快速开始" class="headerlink" title="快速开始"></a>快速开始</h2><ol>
<li><p>下载编译好的 Jar 文件,目前版本是 0.11.0:<br><a href="https://repo1.maven.org/maven2/com/github/dreamhead/moco-runner/0.11.0/moco-runner-0.11.0-standalone.jar" target="_blank" rel="external">https://repo1.maven.org/maven2/com/github/dreamhead/moco-runner/0.11.0/moco-runner-0.11.0-standalone.jar</a></p>
<blockquote>
<p>需要注意的是,之后在运行此 Jar 文件时,需要预先安装好 Java 的 JDK。如果不知道有没有安装过,可在下载完上面的 Jar 文件后,双击打开,如果没有任何提示的话,说明已经安装过 JDK,否则会有一个提示去下载的弹出框。JDK 下载地址为:<a href="http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html" target="_blank" rel="external">http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html</a> ,选择适合自己电脑系统的版本进行下载安装。</p>
</blockquote>
</li>
<li><p>编写配置文件,像下面这个样子描述你的 Moco 服务的配置:</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></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"response"</span> : {</span><br><span class="line"> <span class="attr">"text"</span> : <span class="string">"Hello World"</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<p> 并命名为 foo.json 。</p>
</li>
<li><p>通过配置文件 foo.json 启动 Moco 服务<br> 在命令行输入:</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">$ java -jar moco-runner-<version>-standalone.jar http -p 12306 -c foo.json</span><br></pre></td></tr></table></figure>
<p> 其中 <code><version></code> 替换为下载的 Jar 包的版本,此处为 <code>0.11.0</code> ,所以执行:<code>java -jar moco-runner-0.11.0-standalone.jar http -p 12306 -c foo.json</code> 。<br> -p 指定 Moco 服务端口。</p>
<blockquote>
<p>需要注意的是,执行以上的命令,需要先 cd 到 Jar 文件所在的目录,然后 foo.json 文件也需要放在同一个目录才行,否则会报错。或者可以通过使用 Jar 文件和 .json 文件绝对路径的方式也可以。<br>json 文件路径也可以使用相对路径,如:./json/foo.json</p>
</blockquote>
</li>
<li><p>访问 Web 服务<br> 浏览器访问:<code>http://localhost:12306</code> ,就可以看到令人惊喜的 “Hello World” 了。: )</p>
<hr>
</li>
</ol>
<h2 id="进阶使用"><a href="#进阶使用" class="headerlink" title="进阶使用"></a>进阶使用</h2><p>具体使用可参考 Moco 的 <a href="/~https://github.com/dreamhead/moco/blob/master/moco-doc/apis.md" target="_blank" rel="external">官方文档</a></p>
<h3 id="description-字段作为注释"><a href="#description-字段作为注释" class="headerlink" title="description 字段作为注释"></a>description 字段作为注释</h3><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></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"description"</span>: <span class="string">"any response"</span>,</span><br><span class="line"> <span class="attr">"response"</span>: {</span><br><span class="line"> <span class="attr">"text"</span>: <span class="string">"foo"</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<p>在所有的 JSON APIs 中,使用 <code>description</code> 字段作为描述此配置的作用,在服务启动时此字段会被自动忽略掉。</p>
<hr>
<h3 id="Request"><a href="#Request" class="headerlink" title="Request"></a>Request</h3><h4 id="URI"><a href="#URI" class="headerlink" title="URI"></a>URI</h4><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></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"request"</span> : {</span><br><span class="line"> <span class="attr">"uri"</span> : <span class="string">"/foo"</span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"response"</span> : {</span><br><span class="line"> <span class="attr">"text"</span> : <span class="string">"bar"</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<p>访问 <code>http://localhost:12306/foo</code> ,就可以获取到内容:bar。</p>
<h4 id="Query-Parameter"><a href="#Query-Parameter" class="headerlink" title="Query Parameter"></a>Query Parameter</h4><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></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"request"</span> : {</span><br><span class="line"> <span class="attr">"uri"</span> : <span class="string">"/foo"</span>,</span><br><span class="line"> <span class="attr">"queries"</span> : {</span><br><span class="line"> <span class="attr">"param"</span> : <span class="string">"blah"</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"response"</span> : {</span><br><span class="line"> <span class="attr">"text"</span> : <span class="string">"bar"</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<p>访问 <code>http://localhost:12306/foo?param=blah</code> 获取到内容:bar。</p>
<h4 id="HTTP-Method"><a href="#HTTP-Method" class="headerlink" title="HTTP Method"></a>HTTP Method</h4><p>根据指定的 HTTP 方法返回 response ,也非常容易设置。<br>设置 GET 请求:</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></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"request"</span> : {</span><br><span class="line"> <span class="attr">"method"</span> : <span class="string">"get"</span>,</span><br><span class="line"> <span class="attr">"uri"</span> : <span class="string">"/foo"</span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"response"</span> : {</span><br><span class="line"> <span class="attr">"text"</span> : <span class="string">"bar"</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<p>GET 请求 <code>http://localhost:12306/foo</code> 获取到内容:bar。</p>
<p>设置其他请求同理。</p>
<h4 id="Header"><a href="#Header" class="headerlink" title="Header"></a>Header</h4><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></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"request"</span> : {</span><br><span class="line"> <span class="attr">"method"</span> : <span class="string">"post"</span>,</span><br><span class="line"> <span class="attr">"headers"</span> : {</span><br><span class="line"> <span class="attr">"content-type"</span> : <span class="string">"application/json"</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"response"</span> : {</span><br><span class="line"> <span class="attr">"text"</span> : <span class="string">"bar"</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<h4 id="Cookie"><a href="#Cookie" class="headerlink" title="Cookie"></a>Cookie</h4><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></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"request"</span> : {</span><br><span class="line"> <span class="attr">"uri"</span> : <span class="string">"/cookie"</span>,</span><br><span class="line"> <span class="attr">"cookies"</span> : {</span><br><span class="line"> <span class="attr">"login"</span> : <span class="string">"true"</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"response"</span> : {</span><br><span class="line"> <span class="attr">"text"</span> : <span class="string">"success"</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<h4 id="Form"><a href="#Form" class="headerlink" title="Form"></a>Form</h4><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></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"request"</span> : {</span><br><span class="line"> <span class="attr">"method"</span> : <span class="string">"post"</span>,</span><br><span class="line"> <span class="attr">"forms"</span> : {</span><br><span class="line"> <span class="attr">"name"</span> : <span class="string">"foo"</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"response"</span> : {</span><br><span class="line"> <span class="attr">"text"</span> : <span class="string">"bar"</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<h4 id="XML"><a href="#XML" class="headerlink" title="XML"></a>XML</h4><p>XML 格式目前也还在流行着,配置方法如下:</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></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"request"</span>: {</span><br><span class="line"> <span class="attr">"uri"</span>: <span class="string">"/xml"</span>,</span><br><span class="line"> <span class="attr">"text"</span>: {</span><br><span class="line"> <span class="attr">"xml"</span>: <span class="string">"<request><parameters><id>1</id></parameters></request>"</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"response"</span>: {</span><br><span class="line"> <span class="attr">"text"</span>: <span class="string">"foo"</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<p>如果 XML 文件很大的话,可以放在一个 xml 文件中,然后通过引用文件路径的方式来发起请求:</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></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"request"</span>: {</span><br><span class="line"> <span class="attr">"uri"</span>: <span class="string">"/xml"</span>,</span><br><span class="line"> <span class="attr">"file"</span>: {</span><br><span class="line"> <span class="attr">"xml"</span>: <span class="string">"your_file.xml"</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"response"</span>: {</span><br><span class="line"> <span class="attr">"text"</span>: <span class="string">"foo"</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<h4 id="JSON-Request"><a href="#JSON-Request" class="headerlink" title="JSON Request"></a>JSON Request</h4><h5 id="JSON-Text"><a href="#JSON-Text" class="headerlink" title="JSON Text"></a>JSON Text</h5><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></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"request"</span>: {</span><br><span class="line"> <span class="attr">"uri"</span>: <span class="string">"/json"</span>,</span><br><span class="line"> <span class="attr">"json"</span>: {</span><br><span class="line"> <span class="attr">"foo"</span>: <span class="string">"bar"</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"response"</span>: {</span><br><span class="line"> <span class="attr">"text"</span>: <span class="string">"foo"</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<p>访问 <code>http://localhost:12306/json</code> ,将 <code>{"foo": "bar"}</code> 作为字符串 POST 到服务器。</p>
<h4 id="Operator"><a href="#Operator" class="headerlink" title="Operator"></a>Operator</h4><h5 id="Match"><a href="#Match" class="headerlink" title="Match"></a>Match</h5><p>你可能想通过正则表达式来匹配你的 request,<strong>match</strong> 可以帮到你<br>比如:</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></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"request"</span>: {</span><br><span class="line"> <span class="attr">"uri"</span>: {</span><br><span class="line"> <span class="attr">"match"</span>: <span class="string">"/\\w*/foo"</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"response"</span>: {</span><br><span class="line"> <span class="attr">"text"</span>: <span class="string">"bar"</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<p>匹配任意类似 <code>http://localhost:12306/xxx/foo</code> 的请求,并返回:bar。其中的 <code>/\\w*</code> 表示以 <code>/</code> 开始,之后是任意数量的数字或字母。</p>
<h5 id="Starts-With-Ends-With-Contain"><a href="#Starts-With-Ends-With-Contain" class="headerlink" title="Starts With, Ends With, Contain"></a>Starts With, Ends With, Contain</h5><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></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"request"</span>: {</span><br><span class="line"> <span class="attr">"uri"</span>: {</span><br><span class="line"> <span class="attr">"startsWith"</span>: <span class="string">"/foo"</span></span><br><span class="line"> //<span class="string">"endsWith"</span>: <span class="string">"foo"</span></span><br><span class="line"> //<span class="string">"contain"</span>: <span class="string">"foo"</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"response"</span>: {</span><br><span class="line"> <span class="attr">"text"</span>: <span class="string">"bar"</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<hr>
<h3 id="Response"><a href="#Response" class="headerlink" title="Response"></a>Response</h3><h4 id="Content"><a href="#Content" class="headerlink" title="Content"></a>Content</h4><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></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"request"</span> : {</span><br><span class="line"> <span class="attr">"text"</span> : <span class="string">"foo"</span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"response"</span> : {</span><br><span class="line"> <span class="attr">"text"</span> : <span class="string">"bar"</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<p>访问 <code>http://localhost:12306/</code> 将 foo 作为字符串 POST 到服务端,则会直接返回内容:bar。<br>和 request 一样,如果 response 内容很多的话,可以放在一个文件中返回。例如:</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></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"request"</span> : {</span><br><span class="line"> <span class="attr">"text"</span> : <span class="string">"foo"</span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"response"</span> : {</span><br><span class="line"> <span class="attr">"file"</span> : <span class="string">"bar.response"</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<h4 id="Status-Code"><a href="#Status-Code" class="headerlink" title="Status Code"></a>Status Code</h4><p>Moco 支持 HTTP 状态码的返回:</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></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"request"</span> : {</span><br><span class="line"> <span class="attr">"text"</span> : <span class="string">"foo"</span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"response"</span> : {</span><br><span class="line"> <span class="attr">"status"</span> : <span class="number">200</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<h4 id="Header-1"><a href="#Header-1" class="headerlink" title="Header"></a>Header</h4><p>我们同样可以在 response 中指定 HTTP Header。</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></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"request"</span> : {</span><br><span class="line"> <span class="attr">"text"</span> : <span class="string">"foo"</span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"response"</span> : {</span><br><span class="line"> <span class="attr">"headers"</span> : {</span><br><span class="line"> <span class="attr">"content-type"</span> : <span class="string">"application/json"</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<h4 id="Proxy"><a href="#Proxy" class="headerlink" title="Proxy"></a>Proxy</h4><h5 id="Single-URL"><a href="#Single-URL" class="headerlink" title="Single URL"></a>Single URL</h5><p>我们可以像 proxy 一样,返回一个指定的 URL。</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></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"request"</span> : {</span><br><span class="line"> <span class="attr">"text"</span> : <span class="string">"foo"</span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"response"</span> : {</span><br><span class="line"> <span class="attr">"proxy"</span> : <span class="string">"http://www.github.com"</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<p>实际上,proxy 要更加的强大。它可以将所有的请求都指向指定的 url,包括 HTTP 的 method,version,header,content 等等。</p>
<h5 id="Failover"><a href="#Failover" class="headerlink" title="Failover"></a>Failover</h5><p>除了基础功能之外,proxy 也支持 failover,这意味着如果远程服务暂时不能起作用的话,moco 服务将知道从本地的配置中修复。<br>例如:</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></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"request"</span> : {</span><br><span class="line"> <span class="attr">"text"</span> : <span class="string">"foo"</span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"response"</span> : {</span><br><span class="line"> <span class="attr">"proxy"</span> : {</span><br><span class="line"> <span class="attr">"url"</span> : <span class="string">"http://localhost:12306/unknown"</span>,</span><br><span class="line"> <span class="attr">"failover"</span> : <span class="string">"failover.json"</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<p>Proxy 会将 request/response 成对地保存在你指定的 failover 文件中。如果 proxy 指定的目标不可用,proxy 将从这个文件中恢复。这个功能在开发环境下非常有用,尤其是当集成服务还没有稳定的时候。</p>
<h5 id="Playback"><a href="#Playback" class="headerlink" title="Playback"></a>Playback</h5><p>Moco 也支持 playback,支持将远程的 request 和 response 保存到本地文件中。failover 和 playback 的不同支持在于,playback 只会当本地的 request 和 response 不可用时才会进入远程服务。</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></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"request"</span> : {</span><br><span class="line"> <span class="attr">"text"</span> : <span class="string">"foo"</span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"response"</span> : {</span><br><span class="line"> <span class="attr">"proxy"</span> : {</span><br><span class="line"> <span class="attr">"url"</span> : <span class="string">"http://localhost:12306/unknown"</span>,</span><br><span class="line"> <span class="attr">"playback"</span> : <span class="string">"playback.json"</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<h4 id="Redirect"><a href="#Redirect" class="headerlink" title="Redirect"></a>Redirect</h4><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></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"request"</span> : {</span><br><span class="line"> <span class="attr">"uri"</span> : <span class="string">"/redirect"</span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"redirectTo"</span> : <span class="string">"http://www.github.com"</span></span><br><span class="line"> }</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<h4 id="Cookie-1"><a href="#Cookie-1" class="headerlink" title="Cookie"></a>Cookie</h4><p>Cookie 同样可以放在 response 中。比如:</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></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"request"</span> : {</span><br><span class="line"> <span class="attr">"uri"</span> : <span class="string">"/cookie"</span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"response"</span> : {</span><br><span class="line"> <span class="attr">"cookies"</span> : {</span><br><span class="line"> <span class="attr">"login"</span> : <span class="string">"true"</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<p>当访问 <code>http://localhost:12306/cookie</code> 时,在返回的 Header 中将会看到:<code>Set-Cookie : login=true; Path=/</code></p>
<h4 id="JSON-Response"><a href="#JSON-Response" class="headerlink" title="JSON Response"></a>JSON Response</h4><p>对于 JSON API, 直接返回一个 json object 就可以了。比如:</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></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"request"</span>: {</span><br><span class="line"> <span class="attr">"uri"</span>: <span class="string">"/json"</span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"response"</span>: {</span><br><span class="line"> <span class="attr">"json"</span>: {</span><br><span class="line"> <span class="attr">"foo"</span> : <span class="string">"bar"</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<p>访问 <code>http://localhost:12306/json</code>,则会返回 json:<code>{"foo":"bar"}</code></p>
<h2 id="使用技巧"><a href="#使用技巧" class="headerlink" title="使用技巧"></a>使用技巧</h2><h3 id="配置文件"><a href="#配置文件" class="headerlink" title="配置文件"></a>配置文件</h3><p>简单地启动 moco 服务时是通过命令来启动的:</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">$ java -jar moco-runner-<version>-standalone.jar http -p 12306 -c foo.json</span><br></pre></td></tr></table></figure>
<p>注意到这里有一个 <code>-c foo.json</code> ,此时只会将 <code>foo.json</code> 配置文件中的内容加载到服务中,如果仅仅通过以上的命令,则在调试多个接口时,需要不断的停止旧的服务、通过新的 json 配置文件启动新的服务,非常麻烦。</p>
<blockquote>
<p>Moco 支持动态加载配置文件,无论是修改还是添加配置文件都是不需要重启服务的。</p>
</blockquote>
<h4 id="配置文件的设置技巧"><a href="#配置文件的设置技巧" class="headerlink" title="配置文件的设置技巧"></a>配置文件的设置技巧</h4><p>配置文件的格式为一个 <strong>数组</strong> 类型的 JSON 格式,数组的每一个元素是一个 <code>request/response</code> 的配对。比如:</p>
<figure class="highlight"><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></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line"> {</span><br><span class="line"> // 此处配置了一个 request</span><br><span class="line"> "request" : {</span><br><span class="line"> "uri" : "/foo"</span><br><span class="line"> },</span><br><span class="line"> // 此处配置了对应于上方 request 的 response</span><br><span class="line"> "response" : {</span><br><span class="line"> "text" : "bar"</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<blockquote>
<p>一个 request/response 对,当中的 request 可以没有,则此时访问 <code>http://localhost:12306/</code> 时,Moco 就会直接将配置的 response 中的内容返回。</p>
</blockquote>
<p>上面说了配置文件内容是 <strong>数组</strong> 类型的,所以我们还可以这样写:</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></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"request"</span> : {</span><br><span class="line"> <span class="attr">"uri"</span> : <span class="string">"/foo"</span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"response"</span> : {</span><br><span class="line"> <span class="attr">"text"</span> : <span class="string">"bar"</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"request"</span> : {</span><br><span class="line"> <span class="attr">"uri"</span> : <span class="string">"/foo2"</span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"response"</span> : {</span><br><span class="line"> <span class="attr">"text"</span> : <span class="string">"bar2"</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<p>此时我们加载了这个配置文件后,可以通过 <code>http://localhost:12306/foo</code> 和 <code>http://localhost:12306/foo2</code> 分别获取到 bar 和 bar2。这种对于需要测试的接口数量较少时,比较快速方便。</p>
<h4 id="全局配置文件"><a href="#全局配置文件" class="headerlink" title="全局配置文件"></a>全局配置文件</h4><p>Moco 支持在全局的配置文件中引入其他配置文件,这样就可以分服务定义配置文件,便于管理。<br>例如你有两个不同路径的 API:<code>http://xxx.com/path1/login</code> 和 <code>http://xxx.com/path2/pay</code> (登录和支付接口)。<br>按照上一小节(4.1.1)所讲,我们可以写好 login 和 pay 的两个配置文件(或写在一起),分别设置 request 的 url 为 <code>/path1/login</code> 和 <code>/path2/pay</code> 。如果需要测试的接口很多,则不利于管理,且 path1、path2 这么混乱的分布于不同的配置文件中,对于以后想要更改也很不方便。</p>
<p>正确的姿势应该是这样的:<br>同样写好 login.json 和 pay.json 两个配置文件,然后写一个 <strong>全局配置文件</strong> ,配置如下:</p>
<figure class="highlight"><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">// config.json</span><br><span class="line">[</span><br><span class="line"> {"context":"/path1", "include":"login.json"},</span><br><span class="line"> {"context":"/path2", "include":"pay.json"}</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<p>login 和 pay 两个文件没有特殊要求,和之前的写法一样。比如:</p>
<figure class="highlight"><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><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line">// register.json</span><br><span class="line">[</span><br><span class="line"> {</span><br><span class="line"> "request": {</span><br><span class="line"> "uri": "/register",</span><br><span class="line"> "method": "POST",</span><br><span class="line"> "json": {</span><br><span class="line"> "phone":"18688886666",</span><br><span class="line"> "password":"123456"</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> "response": {</span><br><span class="line"> "json": {</span><br><span class="line"> "state":"0"</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line">// login.json</span><br><span class="line">[</span><br><span class="line"> {</span><br><span class="line"> "request": {</span><br><span class="line"> "uri": "/login",</span><br><span class="line"> "method": "POST",</span><br><span class="line"> "json": {</span><br><span class="line"> "amount":"100"</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> "response": {</span><br><span class="line"> "json": {</span><br><span class="line"> "state":"0"</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<p>然后启动 Moco 服务的命令是:</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">$ java -jar moco-runner-<version>-standalone.jar http -p 12306 -g config.json</span><br></pre></td></tr></table></figure>
<p>要注意的是,最后指定的参数是 <strong><code>-g config.json</code></strong> !</p>
<p>如果只是想引入多个 json 文件的话,全局配置文件中可以不使用 <code>context</code> 字段。比如:</p>
<figure class="highlight"><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">// 不使用 context 字段的 config.json。</span><br><span class="line">[</span><br><span class="line"> {"include":"login.json"},</span><br><span class="line"> {"include":"pay.json"}</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<hr>
<p>(持续更新中…)</p>
]]></content>
</entry>
<entry>
<title><![CDATA[《iOS 与 OS X 多线程和内存管理》读书笔记之 GCD(二)]]></title>
<url>http://raykle.coding.me/2016/08/16/%E3%80%8AiOS%20%E4%B8%8E%20OS%20X%20%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%92%8C%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86%E3%80%8B%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0%E4%B9%8B%20GCD%EF%BC%88%E4%BA%8C%EF%BC%89/</url>
<content type="html"><![CDATA[<blockquote>
<p><a href="/2016/08/16/《iOS 与 OS X 多线程和内存管理》读书笔记之 GCD(一)/">第一篇基础篇 👈</a></p>
</blockquote>
<hr>
<h2 id="dispatch-set-target-queue"><a href="#dispatch-set-target-queue" class="headerlink" title="dispatch_set_target_queue"></a>dispatch_set_target_queue</h2><blockquote>
<p>作用:变更生成的 Dispatch Queue 的执行优先级。</p>
</blockquote>
<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="built_in">dispatch_queue_t</span> targetQueue = dispatch_queue_create(<span class="string">"com.iBinaryOrg.targetQueue"</span>, DISPATCH_QUEUE_SERIAL);</span><br><span class="line"></span><br><span class="line"><span class="built_in">dispatch_queue_t</span> queue1 = dispatch_queue_create(<span class="string">"queue1"</span>, DISPATCH_QUEUE_SERIAL);</span><br><span class="line"><span class="comment">//设置优先级</span></span><br><span class="line">dispatch_set_target_queue(queue1, targetQueue);</span><br></pre></td></tr></table></figure>
<p>dispatch_set_target_queue 中第一个参数为指定要变更执行优先级的 Dispatch Queue,第二个参数为指定的与要使用的执行优先级相同优先级的 Dispatch Queue。</p>
<p>如上面代码中所示,将 Dispatch Queue 指定为 dispatch_set_target_queue 函数的参数,不仅可以变更 Dispatch Queue 的执行优先级,还可以作为 Dispatch Queue 的执行阶层。如果在多个 Serial Dispatch Queue 中用 dispatch_set_target_queue 函数指定目标为某一个 Serial Dispatch Queue,那么原先本应该并行执行的多个 Serial Dispatch Queue,在目标 Serial Dispatch Queue 上只能同时执行一个处理。</p>
<blockquote>
<p>在必须将不可并行执行的处理追加到多个 Serial Dispatch Queue 中时,如果使用 dispatch_set_target_queue 函数将目标指定为某一个 Serial Dispatch Queue,即可防止处理并行执行。</p>
</blockquote>
<p>比如:</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><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><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line">- (<span class="keyword">void</span>)testTargetQueue {</span><br><span class="line"> <span class="built_in">dispatch_queue_t</span> targetQueue = dispatch_queue_create(<span class="string">"com.iBinaryOrg.targetQueue"</span>, DISPATCH_QUEUE_SERIAL);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//创建 3 个同步队列</span></span><br><span class="line"> <span class="built_in">dispatch_queue_t</span> queue1 = dispatch_queue_create(<span class="string">"queue1"</span>, DISPATCH_QUEUE_SERIAL);</span><br><span class="line"> <span class="built_in">dispatch_queue_t</span> queue2 = dispatch_queue_create(<span class="string">"queue2"</span>, DISPATCH_QUEUE_SERIAL);</span><br><span class="line"> <span class="built_in">dispatch_queue_t</span> queue3 = dispatch_queue_create(<span class="string">"queue3"</span>, DISPATCH_QUEUE_SERIAL);</span><br><span class="line"></span><br><span class="line"> <span class="comment">//设置优先级(标记 1)</span></span><br><span class="line"> dispatch_set_target_queue(queue1, targetQueue);</span><br><span class="line"> dispatch_set_target_queue(queue2, targetQueue);</span><br><span class="line"> dispatch_set_target_queue(queue3, targetQueue);</span><br><span class="line"> </span><br><span class="line"> <span class="built_in">dispatch_async</span>(targetQueue, ^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"target queue in"</span>);</span><br><span class="line"> [<span class="built_in">NSThread</span> sleepForTimeInterval:<span class="number">4</span>];</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"target queue out"</span>);</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> <span class="built_in">dispatch_async</span>(queue1, ^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"1 in"</span>);</span><br><span class="line"> [<span class="built_in">NSThread</span> sleepForTimeInterval:<span class="number">3</span>];</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"1 out"</span>);</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> <span class="built_in">dispatch_async</span>(queue2, ^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"2 in"</span>);</span><br><span class="line"> [<span class="built_in">NSThread</span> sleepForTimeInterval:<span class="number">2</span>];</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"2 out"</span>);</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> <span class="built_in">dispatch_async</span>(queue3, ^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"3 in"</span>);</span><br><span class="line"> [<span class="built_in">NSThread</span> sleepForTimeInterval:<span class="number">1</span>];</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"3 out"</span>);</span><br><span class="line"> });</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>输出:</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></pre></td><td class="code"><pre><span class="line">2016-05-27 15:31:24.630 GCD_Demo[2540:25b] target queue in</span><br><span class="line">2016-05-27 15:31:28.634 GCD_Demo[2540:25b] target queue out</span><br><span class="line">2016-05-27 15:31:28.636 GCD_Demo[2540:25b] 1 in</span><br><span class="line">2016-05-27 15:31:31.640 GCD_Demo[2540:25b] 1 out</span><br><span class="line">2016-05-27 15:31:31.642 GCD_Demo[2540:25b] 2 in</span><br><span class="line">2016-05-27 15:31:33.645 GCD_Demo[2540:25b] 2 out</span><br><span class="line">2016-05-27 15:31:33.648 GCD_Demo[2540:25b] 3 in</span><br><span class="line">2016-05-27 15:31:34.651 GCD_Demo[2540:25b] 3 out</span><br></pre></td></tr></table></figure>
<p>以上的代码中,如果将(标记 1)处的 <code>dispatch_set_target_queue</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></pre></td><td class="code"><pre><span class="line">2016-05-27 14:34:40.245 GCD_Demo[1796:1403] target queue in</span><br><span class="line">2016-05-27 14:34:40.246 GCD_Demo[1796:21b] 1 in</span><br><span class="line">2016-05-27 14:34:40.247 GCD_Demo[1796:4203] 2 in</span><br><span class="line">2016-05-27 14:34:40.247 GCD_Demo[1796:4303] 3 in</span><br><span class="line">2016-05-27 14:34:41.253 GCD_Demo[1796:4303] 3 out</span><br><span class="line">2016-05-27 14:34:42.253 GCD_Demo[1796:4203] 2 out</span><br><span class="line">2016-05-27 14:34:43.249 GCD_Demo[1796:21b] 1 out</span><br><span class="line">2016-05-27 14:34:44.249 GCD_Demo[1796:1403] target queue out</span><br></pre></td></tr></table></figure>
<blockquote>
<p>正如在 <a href="/2016/08/16/《iOS 与 OS X 多线程和内存管理》读书笔记之 GCD(一)/#dispatch-queue-create">dispatch_queue_create</a> 小节中所说的,虽然 queue1、queue2 和 queue3 是串行队列,但由于将不同的 3 个 block 分别追加到了这 3 个串行队列中,所以这 3 个串行队列是<code>同时处理</code>的。</p>
</blockquote>
<hr>
<h2 id="dispatch-after"><a href="#dispatch-after" class="headerlink" title="dispatch_after"></a>dispatch_after</h2><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">dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(<span class="number">3</span> * <span class="built_in">NSEC_PER_SEC</span>));</span><br><span class="line">dispatch_after(time, dispatch_get_main_queue(), ^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"waited at least three seconds."</span>);</span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<blockquote>
<p>注意:dispatch_after 函数并不是在指定时间后执行处理,而只是在指定时间追加处理到 Dispatch Queue。此源码与在 3 秒后用 dispatch_async 函数追加 Block 到 Main Dispatch Queue 的相同。</p>
</blockquote>
<p>第二个参数指定要追加处理的 Dispatch Queue,第三个参数指定记述要执行处理的 Block。</p>
<hr>
<h2 id="Dispatch-Group"><a href="#Dispatch-Group" class="headerlink" title="Dispatch Group"></a>Dispatch Group</h2><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></pre></td><td class="code"><pre><span class="line">- (<span class="keyword">void</span>)testDispatchGroup {</span><br><span class="line"> <span class="built_in">dispatch_queue_t</span> queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, <span class="number">0</span>);</span><br><span class="line"> dispatch_group_t group = dispatch_group_create();</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//将队列添加进 group 中</span></span><br><span class="line"> dispatch_group_async(group, queue, ^{<span class="built_in">NSLog</span>(<span class="string">@"blk0"</span>);});</span><br><span class="line"> dispatch_group_async(group, queue, ^{<span class="built_in">NSLog</span>(<span class="string">@"blk1"</span>);});</span><br><span class="line"> dispatch_group_async(group, queue, ^{<span class="built_in">NSLog</span>(<span class="string">@"blk2"</span>);});</span><br><span class="line"> </span><br><span class="line"> dispatch_group_notify(group, queue, ^{<span class="built_in">NSLog</span>(<span class="string">@"done"</span>);});</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>执行结果如下:</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></pre></td><td class="code"><pre><span class="line">blk0</span><br><span class="line">blk2</span><br><span class="line">blk1</span><br><span class="line">done</span><br></pre></td></tr></table></figure>
<p><code>dispatch_group_notify</code> :用来监听队列中的任务已全部执行完毕,第三个参数 <code>block</code>用来执行 group 中任务完毕所响应的操作。</p>
<p>另外,在 Dispatch Group 中也可以使用 <code>dispatch_group_wait</code> 函数仅等待全部处理执行结束。</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">dispatch_group_wait(group, DISPATCH_TIME_FOREVER);<span class="comment">//第二个参数指定为等待的时间(超时),此处意味着永久等待。**只要属于 Dispatch Group 的处理尚未执行结束,就会一直等待,中途不能取消**</span></span><br></pre></td></tr></table></figure>
<p>如果想指定等待时间间隔为 1 秒时应做如下处理:</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></pre></td><td class="code"><pre><span class="line">dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(<span class="number">1</span> * <span class="built_in">NSEC_PER_SEC</span>));</span><br><span class="line"><span class="keyword">long</span> result = dispatch_group_wait(group, time);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (<span class="number">0</span> == result) {</span><br><span class="line"> <span class="comment">/**</span><br><span class="line"> * 属于 Dispatch Group 的全部处理执行结束</span><br><span class="line"> */</span></span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">/**</span><br><span class="line"> * 属于 Dispatch Group de 某一个处理还在执行中</span><br><span class="line"> */</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<blockquote>
<p>当等待时间为 DISPATCH_TIME_FOREVER、由 dispatch_group_wait 函数返回时,由于属于 Dispatch Group 的处理必定全部执行结束,所以返回值恒定为 0.<br>这里的“等待”是什么意思呢?这意味着一旦调用 dispatch_group_wait 函数,该函数就处于调用的状态而不返回。即执行 dispatch_group_wait 函数的现在的线程(当前线程)停止。在经过 dispatch_group_wait 函数中指定的时间或属于指定 Dispatch Queue 的处理全部执行结束前,执行该函数的线程停止。</p>
</blockquote>
<p>指定 DISPATCH_TIME_NOW,则不用任何等待即可判定属于 Dispatch Group 的处理是否执行结束。</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="keyword">long</span> result = dispatch_group_wait(group, DISPATCH_TIME_NOW);</span><br></pre></td></tr></table></figure>
<p>在主线程的 RunLoop 的每次循环中,可检查执行是否结束,从而不耗费多余的等待时间,虽然这样也可以,但一般在这种情况下,还是推荐用 <code>dispatch_group_notify</code> 函数追加结束处理到 Main Dispatch Queue 中。这是因为 <code>dispatch_group_notify</code> 函数可以简化源代码。</p>
<hr>
<h2 id="dispatch-barrier-async"><a href="#dispatch-barrier-async" class="headerlink" title="dispatch_barrier_async"></a>dispatch_barrier_async</h2><p>举例:</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><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><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line">- (<span class="keyword">void</span>)testDispatchBarrierAsync {</span><br><span class="line"> <span class="comment">// 创建并行队列</span></span><br><span class="line"> <span class="built_in">dispatch_queue_t</span> concurrentQueue = dispatch_queue_create(<span class="string">"com.gcd.ForBarrier"</span>, DISPATCH_QUEUE_CONCURRENT);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 异步执行并行队列</span></span><br><span class="line"> <span class="built_in">dispatch_async</span>(concurrentQueue, ^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"blk0_for_reading"</span>);</span><br><span class="line"> });</span><br><span class="line"> </span><br><span class="line"> <span class="built_in">dispatch_async</span>(concurrentQueue, ^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"blk1_for_reading"</span>);</span><br><span class="line"> });</span><br><span class="line"> </span><br><span class="line"> <span class="built_in">dispatch_async</span>(concurrentQueue, ^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"blk2_for_reading"</span>);</span><br><span class="line"> });</span><br><span class="line"> </span><br><span class="line"> <span class="built_in">dispatch_async</span>(concurrentQueue, ^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"blk3_for_reading"</span>);</span><br><span class="line"> });</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 此处会等待已经追加到 concurrentQueue 中的 block 操作全部执行完成,</span></span><br><span class="line"> <span class="comment">// 再调用 dispatch_barrier_async 函数。</span></span><br><span class="line"> dispatch_barrier_async(concurrentQueue, ^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"blk_for_writting"</span>);</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 继续异步执行并行队列</span></span><br><span class="line"> <span class="built_in">dispatch_async</span>(concurrentQueue, ^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"blk4_for_reading"</span>);</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> <span class="built_in">dispatch_async</span>(concurrentQueue, ^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"blk5_for_reading"</span>);</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> <span class="built_in">dispatch_async</span>(concurrentQueue, ^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"blk6_for_reading"</span>);</span><br><span class="line"> });</span><br><span class="line"> </span><br><span class="line"> <span class="built_in">dispatch_async</span>(concurrentQueue, ^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"blk7_for_reading"</span>);</span><br><span class="line"> });</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<blockquote>
<p>dispatch_barrier_async 函数会等待追加到 Concurrent Dispatch Queue 上的并行执行的处理全部结束之后,再将指定的处理追加到该 Concurrent Dispatch Queue 中。然后再由 dispatch_barrier_async 函数追加的处理执行完毕后,Concurrent Dispatch Queue 才恢复为一般的动作,追加到该 Concurrent Dispatch Queue 的处理又开始并行执行。</p>
</blockquote>
<p>输出:</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></pre></td><td class="code"><pre><span class="line">2016-05-27 16:12:41.103 GCD_Demo[2540:4507] blk1_for_reading</span><br><span class="line">2016-05-27 16:12:41.102 GCD_Demo[2540:43d7] blk0_for_reading</span><br><span class="line">2016-05-27 16:12:41.105 GCD_Demo[2540:43d7] blk3_for_reading</span><br><span class="line">2016-05-27 16:12:41.106 GCD_Demo[2540:4507] blk2_for_reading</span><br><span class="line">2016-05-27 16:12:41.108 GCD_Demo[2540:43d7] blk_for_writting //<--</span><br><span class="line">2016-05-27 16:12:41.109 GCD_Demo[2540:43d7] blk4_for_reading</span><br><span class="line">2016-05-27 16:12:41.112 GCD_Demo[2540:43d7] blk6_for_reading</span><br><span class="line">2016-05-27 16:12:41.109 GCD_Demo[2540:4507] blk5_for_reading</span><br><span class="line">2016-05-27 16:12:41.113 GCD_Demo[2540:43d7] blk7_for_reading</span><br></pre></td></tr></table></figure>
<p><em>使用 Concurrent Dispatch Queue 和 dispatch_barrier_async 函数可实现 高效率 的数据库访问和文件访问。</em></p>
<hr>
<h2 id="dispatch-sync-amp-死锁问题"><a href="#dispatch-sync-amp-死锁问题" class="headerlink" title="dispatch_sync & 死锁问题"></a>dispatch_sync & 死锁问题</h2><blockquote>
<p>既然有“async”,当然也有“sync”,即 <code>dispatch_sync</code> 函数。它意味着“同步”(synchonous),也就是将指定的 Block “同步”追加到制定的 Dispatch Queue 中。在追加 Block 执行结束之前,dispatch_sync 函数会一直等待。<br><strong>“等待” 意味着 <code>当前线程停止</code>。</strong></p>
</blockquote>
<p>一旦调用 dispatch_sync 函数,那么在制定的处理执行结束之前,该函数不会返回。</p>
<h3 id="死锁问题"><a href="#死锁问题" class="headerlink" title="死锁问题"></a>死锁问题</h3><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></pre></td><td class="code"><pre><span class="line">- (<span class="keyword">void</span>)viewDidLoad {</span><br><span class="line"> [<span class="keyword">super</span> viewDidLoad];</span><br><span class="line"> </span><br><span class="line"> <span class="built_in">dispatch_queue_t</span> queue = dispatch_get_main_queue();</span><br><span class="line"> <span class="built_in">dispatch_sync</span>(queue, ^{<span class="built_in">NSLog</span>@(<span class="string">@"Hello?"</span>);}); </span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>该源代码在 Main Dispatch Queue 即主线程中执行制定 Block,并等待其执行结束。而其实主线程正在执行 <code>这些源代码</code>,所以 <code>无法执行</code> 追加到 Main Dispatch Queue 中的 Block。下面的例子也一样:</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></pre></td><td class="code"><pre><span class="line">- (<span class="keyword">void</span>)viewDidLoad {</span><br><span class="line"> [<span class="keyword">super</span> viewDidLoad];</span><br><span class="line"> </span><br><span class="line"> <span class="built_in">dispatch_queue_t</span> queue = dispatch_get_main();</span><br><span class="line"> <span class="built_in">dispatch_async</span>(queue, ^{</span><br><span class="line"> <span class="built_in">dispatch_sync</span>(queue, ^{<span class="built_in">NSLog</span>(<span class="string">@"Hello?"</span>);});</span><br><span class="line"> });</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>首先异步地向串行队列(main queue)中追加一个 block1,在这个 block1 中又同步地向此串行队列(main queue)追加一个 block2。所以按照串行队列的工作原理,需要等 block1 执行完毕,才会执行 block2,而 block1 执行的内容,就是在同样的串行队列中同步执行 block2,block2 又需要等待 block1 执行完毕之后再执行。所以此处形成了一个互相等待的情况,造成<code>死锁</code>。<br>此处的 dispatch_async 函数会返回,而 dispatch_sync 永远不会返回。</p>
<p>Serial Dispatch Queue 也会引起相同的问题。</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="built_in">dispatch_queue_t</span> queue = dispatch_queue_create(<span class="string">"com.gcd.serialDispatchQueue"</span>, <span class="literal">NULL</span>);</span><br><span class="line"><span class="built_in">dispatch_async</span>(queue, ^{</span><br><span class="line"> <span class="built_in">dispatch_sync</span>(queue, ^{<span class="built_in">NSLog</span>(<span class="string">@"Hello?"</span>);});<span class="comment">// 无法输出 “Hello?”</span></span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<hr>
<h2 id="dispatch-apply"><a href="#dispatch-apply" class="headerlink" title="dispatch_apply"></a>dispatch_apply</h2><p>dispatch_apply 函数是 dispatch_sync 函数和 Dispatch Group 的关联 API。该函数按指定的次数将指定的 Block 追加到指定的 Dispatch Queue 中,并等待全部处理执行结束。</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="built_in">dispatch_queue_t</span> queue= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, <span class="number">0</span>);</span><br><span class="line">dispatch_apply(<span class="number">3</span>, queue, ^(size_t index) {</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"%zu"</span>, index);</span><br><span class="line">});</span><br><span class="line"><span class="built_in">NSLog</span>(<span class="string">@"done"</span>);</span><br></pre></td></tr></table></figure>
<p>输出:</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></pre></td><td class="code"><pre><span class="line">2</span><br><span class="line">0</span><br><span class="line">1</span><br><span class="line">done</span><br></pre></td></tr></table></figure>
<blockquote>
<p>由于 dispatch_apply 函数也与 dispatch_sync 函数相同,会等待处理执行结束,因此推荐在 <code>dispatch_async</code> 函数中 <strong>非同步</strong> 地执行 dispatch_apply 函数。</p>
</blockquote>
<hr>
<h2 id="dispatch-suspend-dispatch-resume"><a href="#dispatch-suspend-dispatch-resume" class="headerlink" title="dispatch_suspend / dispatch_resume"></a>dispatch_suspend / dispatch_resume</h2><p>挂起指定的 queue:</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">dispatch_suspend(queue);</span><br></pre></td></tr></table></figure>
<p>恢复指定的 queue:</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">dispatch_resume(queue);</span><br></pre></td></tr></table></figure>
<blockquote>
<p>这些函数对已经执行的处理没有影响。挂起后,追加到 Dispatch Queue 中但尚未执行的处理在此之后停止执行。而恢复则使得这些处理能够继续执行。</p>
</blockquote>
<hr>
<h2 id="Dispatch-Semaphore"><a href="#Dispatch-Semaphore" class="headerlink" title="Dispatch Semaphore"></a>Dispatch Semaphore</h2><p>Dispatch Semaphore 是持有计数的信号,该计数是多线程编程中的计数类型信号。所谓信号,类似于过马路时常用的手旗。可以通过时举起手旗,不可通过时放下手旗。而在 Dispatch Semaphore 中,使用计数来实现该功能。计数为 0 时等待,计数为 1 或大于 1 时。减去 1 而不等待。</p>
<p>使用方法:</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">dispatch_semaphore_t semaphore = dispatch_semaphore_create(<span class="number">1</span>);</span><br></pre></td></tr></table></figure>
<blockquote>
<p>参数表示计数的初始值。例子中将计数值初始化为 1。</p>
</blockquote>
<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">dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);</span><br></pre></td></tr></table></figure>
<p>dispatch_semaphore_wait 函数等待 Dispatch Semaphore 的计数值达到大于或等于 1.当计数值大于等于 1,或者在待机中计数值大于等于 1 时,对该计数进行减法并从 dispatch_semaphore_wait 函数返回。第二个函数指定等待时间。</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><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(<span class="number">1</span> * <span class="built_in">NSEC_PER_SEC</span>));</span><br><span class="line"><span class="keyword">long</span> result = dispatch_semaphore_wait(semaphore, time);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (<span class="number">0</span> == result) {</span><br><span class="line"></span><br><span class="line"> <span class="comment">/*</span><br><span class="line"> * 由于 Dispatch Semaphore 的计数值达到大于等于 1</span><br><span class="line"> * 或者在待机中的指定时间内</span><br><span class="line"> * Dispatch Semaphore 的计数值达到大于等于 1</span><br><span class="line"> * 所以 Dispatch Semaphore 的计数值减去 1</span><br><span class="line"> *</span><br><span class="line"> * 可执行需要进行排他控制的锤炼</span><br><span class="line"> */</span></span><br><span class="line"> </span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line"></span><br><span class="line"> <span class="comment">/*</span><br><span class="line"> * 由于 Dispatch Semaphore 的计数值为 0</span><br><span class="line"> * 因此在达到指定时间为止待机</span><br><span class="line"> */</span></span><br><span class="line"> </span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>dispatch_semaphore_wait 函数返回 0 时,可安全地执行需要进行排他控制的处理。<br>该处理结束时 <strong><code>通过 dispatch_semaphore_signal 函数将 Dispatch Semaphore 的计数值加 1</code></strong>.</p>
<p>Sample:</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><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><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><span class="line">- (<span class="keyword">void</span>)testDispatchSemaphore {</span><br><span class="line"> <span class="comment">//创建一个 semaphore</span></span><br><span class="line"> dispatch_semaphore_t semaphore = dispatch_semaphore_create(<span class="number">1</span>);</span><br><span class="line"> </span><br><span class="line"> __block <span class="built_in">NSString</span> *strTest = <span class="string">@"test"</span>;</span><br><span class="line"></span><br><span class="line"> <span class="built_in">dispatch_queue_t</span> concurrentQueue = dispatch_queue_create(<span class="string">"com.gcd.concurrentQueue"</span>, DISPATCH_QUEUE_CONCURRENT);</span><br><span class="line"> </span><br><span class="line"> <span class="built_in">dispatch_async</span>(concurrentQueue, ^{</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//每次执行之前,先判定信号量是否大于 0,是的话就执行,否则等待。</span></span><br><span class="line"> dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> ([strTest isEqualToString:<span class="string">@"test"</span>]) {</span><br><span class="line"> </span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"--%@--1--"</span>, strTest);</span><br><span class="line"> [<span class="built_in">NSThread</span> sleepForTimeInterval:<span class="number">1</span>];</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> ([strTest isEqualToString:<span class="string">@"test"</span>]) {</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"--%@--2--"</span>, strTest);</span><br><span class="line"> [<span class="built_in">NSThread</span> sleepForTimeInterval:<span class="number">1</span>]; </span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"==========changed========"</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//执行完毕之后,信号量进行 +1 处理</span></span><br><span class="line"> dispatch_semaphore_signal(semaphore);</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> <span class="built_in">dispatch_async</span>(concurrentQueue, ^{</span><br><span class="line"> </span><br><span class="line"> dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"--%@--3--"</span>, strTest);</span><br><span class="line"> </span><br><span class="line"> [<span class="built_in">NSThread</span> sleepForTimeInterval:<span class="number">1</span>];</span><br><span class="line"> dispatch_semaphore_signal(semaphore);</span><br><span class="line"> </span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> </span><br><span class="line"></span><br><span class="line"> <span class="built_in">dispatch_async</span>(concurrentQueue, ^{</span><br><span class="line"></span><br><span class="line"> dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);</span><br><span class="line"></span><br><span class="line"> strTest = <span class="string">@"modify"</span>;</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"--%@--4--"</span>, strTest);</span><br><span class="line"></span><br><span class="line"> [<span class="built_in">NSThread</span> sleepForTimeInterval:<span class="number">1</span>];</span><br><span class="line"> dispatch_semaphore_signal(semaphore);</span><br><span class="line"></span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> </span><br><span class="line"></span><br><span class="line"> <span class="built_in">dispatch_async</span>(concurrentQueue, ^{</span><br><span class="line"></span><br><span class="line"> dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"--%@--5--"</span>, strTest);</span><br><span class="line"> [<span class="built_in">NSThread</span> sleepForTimeInterval:<span class="number">1</span>];</span><br><span class="line"></span><br><span class="line"> dispatch_semaphore_signal(semaphore);</span><br><span class="line"></span><br><span class="line"> });</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>输出:</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></pre></td><td class="code"><pre><span class="line">2016-05-27 17:15:39.490 GCD_Demo[2540:4717] --test--1--</span><br><span class="line">2016-05-27 17:15:40.494 GCD_Demo[2540:4717] --test--2--</span><br><span class="line">2016-05-27 17:15:41.498 GCD_Demo[2540:4517] --test--3--</span><br><span class="line">2016-05-27 17:15:42.502 GCD_Demo[2540:442f] --modify--4--</span><br><span class="line">2016-05-27 17:15:43.505 GCD_Demo[2540:4283] --modify--5--</span><br></pre></td></tr></table></figure>
<blockquote>
<p>解析:以上虽然 Block 都是在异步队列中执行,但是由于初始化 Semaphore 时是以 1 的信号量创建的,所以每一个 Block 在追加到异步队列中之后,由于执行之前都通过了信号量的判断,所以同时只有一个 Block 能够执行。直到一个 Block 执行完毕,恢复了信号量之后,下一个 Block 才能得以执行。</p>
</blockquote>
<hr>
<h2 id="dispatch-once"><a href="#dispatch-once" class="headerlink" title="dispatch_once"></a>dispatch_once</h2><p>dispatch_once 函数是保证在应用程序执行中只执行一次指定处理的API。</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">static</span> <span class="built_in">dispatch_once_t</span> pred;</span><br><span class="line"><span class="built_in">dispatch_once</span>(&pred, ^{</span><br><span class="line"> <span class="comment">//初始化</span></span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<blockquote>
<p><strong>单例模式</strong>,此源代码能够保证即使在多线程环境下执行,也是百分之百安全。</p>
</blockquote>
<hr>
<h2 id="Dispatch-I-O"><a href="#Dispatch-I-O" class="headerlink" title="Dispatch I/O"></a>Dispatch I/O</h2><p>(待完善)</p>
<hr>
]]></content>
</entry>
<entry>
<title><![CDATA[《iOS 与 OS X 多线程和内存管理》读书笔记之 GCD(一)]]></title>
<url>http://raykle.coding.me/2016/08/16/%E3%80%8AiOS%20%E4%B8%8E%20OS%20X%20%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%92%8C%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86%E3%80%8B%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0%E4%B9%8B%20GCD%EF%BC%88%E4%B8%80%EF%BC%89/</url>
<content type="html"><![CDATA[<blockquote class="blockquote-center"><p>书籍链接:<a href="https://www.amazon.cn/Objective-C%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B-iOS%E4%B8%8EOS-X%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%92%8C%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86-%E5%9D%82%E6%9C%AC%E4%B8%80%E6%A0%91/dp/B00DE60G3S/ref=sr_1_1?ie=UTF8&qid=1471501826&sr=8-1&keywords=iOS+%E4%B8%8E+OS+X+%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%92%8C%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86" target="_blank" rel="external">Objective-C 高级编程: iOS 与 OS X 多线程和内存管理</a></p>
</blockquote>
<h2 id="几个概念"><a href="#几个概念" class="headerlink" title="几个概念"></a>几个概念</h2><p><code>异步</code>:提交任务之后立刻返回,在后台队列中执行任务。<br><code>同步</code>:提交的任务执行完之后,再返回。</p>
<p><code>并行执行</code>:提交到任务到队列中之后,比如按顺序提交了任务 1 和任务 2,在任务 1 开始执行之后,不管任务 1 有没有执行完毕,都开始执行任务 2。<br><code>串行执行</code>:提交到任务到队列中之后,比如按顺序提交了任务 1 和任务 2,只有在任务 1 执行完毕之后,才执行任务 2。</p>
<blockquote>
<p>异步 or 同步,由函数 <code>dispatch_sync(同步)</code> or <code>dispatch_async(异步)</code> 决定。<br>并行 or 串行,由队列本身决定。如添加任务到 <code>Serial Dispatch Queue(串行队列)</code> or <code>Concurrent Dispatch Queue(并行队列)</code> 队列中。</p>
</blockquote>
<hr>
<h2 id="Dispatch-Queue"><a href="#Dispatch-Queue" class="headerlink" title="Dispatch Queue"></a>Dispatch Queue</h2><blockquote>
<p>开发者要做的只是定义想执行的任务并追加到适当的 Dispatch Queue 中。</p>
</blockquote>
<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">dispatch_async</span>(queue, ^{</span><br><span class="line"> <span class="comment">//想要执行的任务</span></span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<p>该源代码使用 Block 语法“定义想执行的任务”,通过 dispatch_async 函数“追加”赋值在变量 queue 的“Dispatch Queue 中”。仅这样就可使指定的 Block 在另一线程中执行。</p>
<p>“Dispatch Queue”是执行处理的等待队列。应用程序编写人员通过 dispatch_async 函数等 API,在 Block 语法中记述想执行的处理并将其追加到 Dispatch Queue 中。Dispatch Queue 按照追加的顺序(<strong><code>先进先出 FIFO</code></strong>)执行处理。</p>
<hr>
<h2 id="Dispatch-Queue-种类"><a href="#Dispatch-Queue-种类" class="headerlink" title="Dispatch Queue 种类"></a>Dispatch Queue 种类</h2><p><code>Serial Dispatch Queue</code> 等待现在执行中处理结果,使用一个线程。<br><code>Concurrent Dispatch Queue</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><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//创建一个 Serial Dispatch Queue</span></span><br><span class="line"><span class="built_in">dispatch_queue_t</span> serialQueue = dispatch_queue_create(<span class="string">"come.iBinaryOrg.serial"</span>, DISPATCH_QUEUE_SERIAL);</span><br><span class="line"></span><br><span class="line"><span class="built_in">dispatch_async</span>(serialQueue, ^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"block 0"</span>);</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="built_in">dispatch_async</span>(serialQueue, ^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"block 1"</span>);</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="built_in">dispatch_async</span>(serialQueue, ^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"block 2"</span>);</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="built_in">dispatch_async</span>(serialQueue, ^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"block 3"</span>);</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">...</span><br></pre></td></tr></table></figure>
<p>当变量为 Serial Dispatch Queue 时,因为要等待现在执行中的处理结束,所以首先执行 blk0,blk0 执行结束后,接着执行 blk1,blk1 结束后再开始执行 blk2,如此重复。同时执行的处理数只能有 1 个。即执行该源码后,一定按照以下顺序进行处理:</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></pre></td><td class="code"><pre><span class="line">block 0</span><br><span class="line">block 1</span><br><span class="line">block 2</span><br><span class="line">block 3</span><br><span class="line">...</span><br></pre></td></tr></table></figure>
<p>当变量 queue 为 Concurrent Dispatch Queue 时,因为不用等待现在执行中的处理结果结束,所以首先执行 blk0,不管 blk0 的执行是否结束,都开始执行后面的 blk1,不管 blk1 的执行是否结束,都开始执行后面的 blk2,如此重复循环。</p>
<p>这样虽然不用等待处理结果,可以并行执行多个处理,但并行执行的处理数量取决于<code>当前系统的状态</code>。即 iOS 和 OS X 基于 Dispatch Queue 中的处理数、CPU 核数以及 CPU 负荷等当前系统的状态来决定 Concurrent Dispatch Queue 中并行执行的处理数。所谓“并行执行”,就是使用多个线程同时执行多个处理。</p>
<hr>
<h2 id="dispatch-queue-create"><a href="#dispatch-queue-create" class="headerlink" title="dispatch_queue_create"></a>dispatch_queue_create</h2><p>关于 Serial Dispatch Queue 生成个数的注意事项:</p>
<ul>
<li>使用 dispatch_queue_create 函数可生成任意多个 Dispatch Queue,当生成多个 Serial Dispatch Queue 时,各个 Serial Dispatch Queue 将 <strong><em>并行执行</em></strong>。虽然在一个 Serial Dispatch Queue 中同时只能执行一个追加处理,但如果将处理分别追加到4个 Serial Dispatch Queue 中,各个 Serial Dispatch Queue 执行一个,即为 <strong><em>同时执行4个处理</em></strong>。</li>
<li>一旦生成 Serial Dispatch Queue 并追加处理,系统对于一个 Serial Dispatch Queue 就只生成并使用一个线程。如果生成 2000 个 Serial Dispatch Queue,那么就生成 2000 个线程。<em>过多使用多线程,就会消耗大量内存,引起大量的上下文切换,大幅度降低系统的响应性能。</em></li>
</ul>
<p>使用方法:</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></pre></td><td class="code"><pre><span class="line"><span class="comment">//Serial Dispatch Queue</span></span><br><span class="line"><span class="built_in">dispatch_queue_t</span> mySerialDispatchQueue = dispatch_queue_create(<span class="string">"com.iBinaryOrg.gcd.MySerialDispatchQueue"</span>, <span class="literal">NULL</span><span class="comment">/* 等同于 DISPATCH_QUEUE_SERIAL,串行队列 */</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 异步执行</span></span><br><span class="line"><span class="built_in">dispatch_async</span>(mySerialDispatchQueue, ^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"Blk on mySerialDispatchQueue"</span>);</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="comment">//Concurrent Dispatch Queue</span></span><br><span class="line"><span class="built_in">dispatch_queue_t</span> myConcurrentDispatchQueue = dispatch_queue_create(<span class="string">"com.iBinaryOrg.gcd.MyConcurrentDispatchQueue"</span>, DISPATCH_QUEUE_CONCURRENT);<span class="comment">// 并行队列</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 异步执行</span></span><br><span class="line"><span class="built_in">dispatch_async</span>(myConcurrentDispatchQueue, ^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"Blk on myConcurrentDispatchQueue"</span>);</span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<hr>
<h2 id="Main-Dispatch-Queue-Global-Dispatch-Queue"><a href="#Main-Dispatch-Queue-Global-Dispatch-Queue" class="headerlink" title="Main Dispatch Queue / Global Dispatch Queue"></a>Main Dispatch Queue / Global Dispatch Queue</h2><blockquote>
<p>Main Dispatch Queue 是 Serial Dispatch Queue。<br>Global Dispatch Queue 是 Concurrent Dispatch Queue。 </p>
</blockquote>
<p><strong>系统提供的 Dispatch Queue 种类:</strong></p>
<table>
<thead>
<tr>
<th>名称</th>
<th>Dispatch Queue 种类</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>Main Dispatch Queue</td>
<td>Serial DIspatch Queue</td>
<td>主线程执行</td>
</tr>
<tr>
<td>Serial Dispatch Queue (Hign Priority)</td>
<td>Concurrent Dispatch Queue</td>
<td>执行优先级:高 (最高优先)</td>
</tr>
<tr>
<td>Serial Dispatch Queue (Default Priority)</td>
<td>Concurrent Dispatch Queue</td>
<td>执行优先级:默认</td>
</tr>
<tr>
<td>Serial Dispatch Queue (Low Priority)</td>
<td>Concurrent Dispatch Queue</td>
<td>执行优先级:低</td>
</tr>
<tr>
<td>Serial Dispatch Queue (Background Priority)</td>
<td>Concurrent Dispatch Queue</td>
<td>执行优先级:后台</td>
</tr>
</tbody>
</table>
<p><strong>使用举例</strong></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></pre></td><td class="code"><pre><span class="line"><span class="comment">//在默认优先级的 Global Dispatch Queue 中执行block</span></span><br><span class="line"><span class="built_in">dispatch_async</span>(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, <span class="number">0</span>), ^{</span><br><span class="line"> <span class="comment">/*</span><br><span class="line"> *可并行执行的处理</span><br><span class="line"> */</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">/*</span><br><span class="line"> *在 Main Dispatch Queue 中执行 block</span><br><span class="line"> */</span></span><br><span class="line"> <span class="built_in">dispatch_async</span>(dispatch_get_main_queue, ^{</span><br><span class="line"> <span class="comment">/*</span><br><span class="line"> *只能在主线程中执行的处理</span><br><span class="line"> */</span></span><br><span class="line"> });</span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<hr>
]]></content>
</entry>
<entry>
<title><![CDATA[Test]]></title>
<url>http://raykle.coding.me/2016/07/12/Test/</url>
<content type="html"><![CDATA[<h1 id="This-is-title"><a href="#This-is-title" class="headerlink" title="This is title"></a>This is title</h1><p>Hello, this is a <code>test</code> post. </p>
<blockquote>
<p>Life is like a box of chocolates, you never know what you’re going to get.</p>
</blockquote>
<p><em>this is code block</em><br><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="built_in">print</span>(<span class="string">"Hello Swift."</span>)</span><br></pre></td></tr></table></figure></p>
]]></content>
</entry>
<entry>
<title><![CDATA[Hello World]]></title>
<url>http://raykle.coding.me/2016/05/31/hello-world/</url>
<content type="html"><![CDATA[<p>Welcome to <a href="https://hexo.io/" target="_blank" rel="external">Hexo</a>! This is your very first post. Check <a href="https://hexo.io/docs/" target="_blank" rel="external">documentation</a> for more info. If you get any problems when using Hexo, you can find the answer in <a href="https://hexo.io/docs/troubleshooting.html" target="_blank" rel="external">troubleshooting</a> or you can ask me on <a href="/~https://github.com/hexojs/hexo/issues" target="_blank" rel="external">GitHub</a>.</p>
<a id="more"></a>
<h2 id="Quick-Start"><a href="#Quick-Start" class="headerlink" title="Quick Start"></a>Quick Start</h2><h3 id="Create-a-new-post"><a href="#Create-a-new-post" class="headerlink" title="Create a new post"></a>Create a new post</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo new <span class="string">"My New Post"</span></span><br></pre></td></tr></table></figure>
<p>More info: <a href="https://hexo.io/docs/writing.html" target="_blank" rel="external">Writing</a></p>
<h3 id="Run-server"><a href="#Run-server" class="headerlink" title="Run server"></a>Run server</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo server</span><br></pre></td></tr></table></figure>
<p>More info: <a href="https://hexo.io/docs/server.html" target="_blank" rel="external">Server</a></p>
<h3 id="Generate-static-files"><a href="#Generate-static-files" class="headerlink" title="Generate static files"></a>Generate static files</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo generate</span><br></pre></td></tr></table></figure>
<p>More info: <a href="https://hexo.io/docs/generating.html" target="_blank" rel="external">Generating</a></p>
<h3 id="Deploy-to-remote-sites"><a href="#Deploy-to-remote-sites" class="headerlink" title="Deploy to remote sites"></a>Deploy to remote sites</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo deploy</span><br></pre></td></tr></table></figure>
<p>More info: <a href="https://hexo.io/docs/deployment.html" target="_blank" rel="external">Deployment</a></p>
]]></content>
</entry>
</search>