-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.html
1336 lines (867 loc) · 81.7 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="baidu-site-verification" content="IjYHkLvavX" />
<title>袁克强的博客</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="description" content="description">
<meta property="og:type" content="website">
<meta property="og:title" content="袁克强的博客">
<meta property="og:url" content="http://iscript.github.io/index.html">
<meta property="og:site_name" content="袁克强的博客">
<meta property="og:description" content="description">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="袁克强的博客">
<meta name="twitter:description" content="description">
<link rel="alternative" href="/atom.xml" title="袁克强的博客" type="application/atom+xml">
<link rel="icon" href="/favicon.png">
<link rel="stylesheet" href="/css/style.css" type="text/css">
<!--[if lt IE 11]>
<script>
alert("请不要使用IE浏览器访问本站");
</script>
<![endif]-->
</head>
<body>
<div id="container">
<div class="left-col">
<div class="overlay"></div>
<div class="intrude-less">
<header id="header" class="inner">
<a href="/" class="profilepic">
<img lazy-src="http://7xnv0h.com1.z0.glb.clouddn.com/avatar.jpeg" class="js-avatar">
</a>
<hgroup>
<h1 class="header-author"><a href="/">袁克强</a></h1>
</hgroup>
<p class="header-subtitle">求知若渴,虚心若愚。</p>
<div class="switch-btn">
<div class="icon">
<div class="icon-ctn">
<div class="icon-wrap icon-house" data-idx="0">
<div class="birdhouse"></div>
<div class="birdhouse_holes"></div>
</div>
<div class="icon-wrap icon-ribbon hide" data-idx="1">
<div class="ribbon"></div>
</div>
<div class="icon-wrap icon-link hide" data-idx="2">
<div class="loopback_l"></div>
<div class="loopback_r"></div>
</div>
<div class="icon-wrap icon-me hide" data-idx="3">
<div class="user"></div>
<div class="shoulder"></div>
</div>
</div>
</div>
<div class="tips-box hide">
<div class="tips-arrow"></div>
<ul class="tips-inner">
<li>菜单</li>
<li>标签</li>
<li>友情链接</li>
<li>关于我</li>
</ul>
</div>
</div>
<div class="switch-area">
<div class="switch-wrap">
<section class="switch-part switch-part1">
<nav class="header-menu">
<ul>
<li><a href="/">主页</a></li>
<li><a href="/archives">所有文章</a></li>
</ul>
</nav>
<nav class="header-nav">
<div class="social">
<a class="github" target="_blank" href="https://github.com/iScript/" title="github">github</a>
<a class="weibo" target="_blank" href="http://weibo.com/u/2710578101" title="weibo">weibo</a>
<a class="rss" target="_blank" href="/#" title="rss">rss</a>
<a class="zhihu" target="_blank" href="/#" title="zhihu">zhihu</a>
</div>
</nav>
</section>
<section class="switch-part switch-part2">
<div class="widget tagcloud" id="js-tagcloud">
<a href="/tags/AMD/" style="font-size: 10px;">AMD</a> <a href="/tags/BSD/" style="font-size: 10px;">BSD</a> <a href="/tags/Envoy/" style="font-size: 10px;">Envoy</a> <a href="/tags/GCD/" style="font-size: 10px;">GCD</a> <a href="/tags/GD库/" style="font-size: 10px;">GD库</a> <a href="/tags/Gecko/" style="font-size: 10px;">Gecko</a> <a href="/tags/Laravel/" style="font-size: 13.33px;">Laravel</a> <a href="/tags/Linux/" style="font-size: 10px;">Linux</a> <a href="/tags/MIT/" style="font-size: 10px;">MIT</a> <a href="/tags/Node-js/" style="font-size: 11.67px;">Node.js</a> <a href="/tags/PHP/" style="font-size: 18.33px;">PHP</a> <a href="/tags/RESTful/" style="font-size: 10px;">RESTful</a> <a href="/tags/angular-js/" style="font-size: 10px;">angular.js</a> <a href="/tags/bower/" style="font-size: 10px;">bower</a> <a href="/tags/cookie/" style="font-size: 10px;">cookie</a> <a href="/tags/git/" style="font-size: 10px;">git</a> <a href="/tags/github/" style="font-size: 10px;">github</a> <a href="/tags/grunt/" style="font-size: 11.67px;">grunt</a> <a href="/tags/iOS/" style="font-size: 16.67px;">iOS</a> <a href="/tags/javascript/" style="font-size: 20px;">javascript</a> <a href="/tags/json/" style="font-size: 10px;">json</a> <a href="/tags/linux/" style="font-size: 10px;">linux</a> <a href="/tags/mongodb/" style="font-size: 10px;">mongodb</a> <a href="/tags/mongoose/" style="font-size: 10px;">mongoose</a> <a href="/tags/node-js/" style="font-size: 11.67px;">node.js</a> <a href="/tags/npm/" style="font-size: 10px;">npm</a> <a href="/tags/psr/" style="font-size: 10px;">psr</a> <a href="/tags/require-js/" style="font-size: 13.33px;">require.js</a> <a href="/tags/session/" style="font-size: 10px;">session</a> <a href="/tags/socket/" style="font-size: 10px;">socket</a> <a href="/tags/ssh/" style="font-size: 10px;">ssh</a> <a href="/tags/swift/" style="font-size: 15px;">swift</a> <a href="/tags/test/" style="font-size: 10px;">test</a> <a href="/tags/text-js/" style="font-size: 10px;">text.js</a> <a href="/tags/webkit/" style="font-size: 10px;">webkit</a> <a href="/tags/wordpress/" style="font-size: 11.67px;">wordpress</a> <a href="/tags/二次开发/" style="font-size: 10px;">二次开发</a> <a href="/tags/互联网/" style="font-size: 10px;">互联网</a> <a href="/tags/产品/" style="font-size: 10px;">产品</a> <a href="/tags/佛学/" style="font-size: 10px;">佛学</a> <a href="/tags/图片/" style="font-size: 10px;">图片</a> <a href="/tags/学习笔记/" style="font-size: 10px;">学习笔记</a> <a href="/tags/安全/" style="font-size: 10px;">安全</a> <a href="/tags/定时/" style="font-size: 10px;">定时</a> <a href="/tags/开源协议/" style="font-size: 10px;">开源协议</a> <a href="/tags/异步/" style="font-size: 10px;">异步</a> <a href="/tags/总结/" style="font-size: 10px;">总结</a> <a href="/tags/持续集成/" style="font-size: 10px;">持续集成</a> <a href="/tags/推送/" style="font-size: 11.67px;">推送</a> <a href="/tags/插件/" style="font-size: 10px;">插件</a> <a href="/tags/框架/" style="font-size: 11.67px;">框架</a> <a href="/tags/测试/" style="font-size: 10px;">测试</a> <a href="/tags/浏览器/" style="font-size: 10px;">浏览器</a> <a href="/tags/理财/" style="font-size: 10px;">理财</a> <a href="/tags/编码规范/" style="font-size: 10px;">编码规范</a> <a href="/tags/翻译/" style="font-size: 10px;">翻译</a> <a href="/tags/自动化/" style="font-size: 11.67px;">自动化</a> <a href="/tags/融云/" style="font-size: 10px;">融云</a> <a href="/tags/读书/" style="font-size: 13.33px;">读书</a> <a href="/tags/转载/" style="font-size: 11.67px;">转载</a>
</div>
</section>
<section class="switch-part switch-part3">
<div id="js-friends">
<a target="_blank" class="main-nav-link switch-friends-link" href="http://www.jianshu.com/">简书</a>
</div>
</section>
<section class="switch-part switch-part4">
<div id="js-aboutme">PHP开发工程师@温州。博客关于:技术、生活、读书、互联网。</div>
</section>
</div>
</div>
</header>
</div>
</div>
<div class="mid-col">
<nav id="mobile-nav">
<div class="overlay">
<div class="slider-trigger"></div>
<h1 class="header-author js-mobile-header hide">袁克强</h1>
</div>
<div class="intrude-less">
<header id="header" class="inner">
<div class="profilepic">
<img lazy-src="http://7xnv0h.com1.z0.glb.clouddn.com/avatar.jpeg" class="js-avatar">
</div>
<hgroup>
<h1 class="header-author">袁克强</h1>
</hgroup>
<p class="header-subtitle">求知若渴,虚心若愚。</p>
<nav class="header-menu">
<ul>
<li><a href="/">主页</a></li>
<li><a href="/archives">所有文章</a></li>
<div class="clearfix"></div>
</ul>
</nav>
<nav class="header-nav">
<div class="social">
<a class="github" target="_blank" href="https://github.com/iScript/" title="github">github</a>
<a class="weibo" target="_blank" href="http://weibo.com/u/2710578101" title="weibo">weibo</a>
<a class="rss" target="_blank" href="/#" title="rss">rss</a>
<a class="zhihu" target="_blank" href="/#" title="zhihu">zhihu</a>
</div>
</nav>
</header>
</div>
</nav>
<div class="body-wrap">
<article id="post-SSH免密码登录" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2016/03/SSH免密码登录/" class="article-date">
<time datetime="2016-03-31T08:25:00.000Z" itemprop="datePublished">2016-03-31</time>
</a>
</div>
<div class="article-inner">
<input type="hidden" class="isFancy" />
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2016/03/SSH免密码登录/">SSH免密码登录</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<h3 id="SSH">SSH</h3><p>SSH(Secure Shell )是一种网络协议,用于计算机之间的加密登录。</p>
<h3 id="公钥登录">公钥登录</h3><p>公钥(Public Key)与私钥(Private Key)是通过一种算法得到的一个密钥对(即一个公钥和一个私钥),公钥是密钥对中公开的部分,私钥则是非公开的部分。<br>所谓”公钥登录”,原理很简单,就是用户将自己的公钥储存在远程主机上。登录的时候,远程主机会向用户发送一段随机字符串,用户用自己的私钥加密后,再发回来。远程主机用事先储存的公钥进行解密,如果成功,就证明用户是可信的,直接允许登录shell,不再要求密码。</p>
<h3 id="具体步骤">具体步骤</h3><h5 id="1-_通过ssh-keygen生成秘钥">1. 通过ssh-keygen生成秘钥</h5><pre><code><span class="variable">$ </span>ssh-keygen -t rsa
</code></pre><p>-t指定加密类型为rsa,运行该命令以后,会在$HOME/.ssh/目录下,会新生成两个文件:id_rsa和id_rsa.pub。前者是你的私钥,后者是你的公钥。<br><img src="http://dn-yuankeqiang.qbox.me/0F6F97CD-7321-47A2-872A-5D881797572D.png?imageView/2/w/500" alt="袁克强的博客"></p>
<h6 id="2-_复制公钥到远程服务器">2. 复制公钥到远程服务器</h6><p>复制到/home/$USER/.ssh (.ssh目录不存在则创建一个,<strong>保证.ssh和authorized_keys都只有用户自己有写权限,否则验证无效: chmod 700 -R .ssh</strong> ) </p>
<pre><code>$ scp id_rsa<span class="class">.pub</span> root@myhost<span class="class">.com</span>:~/.ssh/id_rsa.pub
</code></pre><h6 id="3-_将公钥保存到authorized_keys文件">3. 将公钥保存到authorized_keys文件</h6><p>登录远程服务器后,将公钥保存到authorized_keys文件。 </p>
<pre><code>$ cd ~/<span class="class">.ssh</span>
$ cat id_rsa<span class="class">.pub</span> >> authorized_keys
</code></pre><hr>
<p>至此,即可免密码登录远程服务器。</p>
</div>
<div class="article-info article-info-index">
<div class="article-tag tagcloud">
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Linux/">Linux</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/ssh/">ssh</a></li></ul>
</div>
<div class="clearfix"></div>
</div>
</div>
</article>
<article id="post-iOS学习笔记之GCD" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2016/03/iOS学习笔记之GCD/" class="article-date">
<time datetime="2016-03-18T01:25:00.000Z" itemprop="datePublished">2016-03-18</time>
</a>
</div>
<div class="article-inner">
<input type="hidden" class="isFancy" />
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2016/03/iOS学习笔记之GCD/">iOS学习笔记之GCD</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<h3 id="Serial_Diapatch_Queue_串行队列">Serial Diapatch Queue 串行队列</h3><p>任务相互依赖,具有明显的先后顺序的时候</p>
<figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">var</span> queue1 : dispatch_queue_t = <span class="function"><span class="title">dispatch_queue_create</span><span class="params">(<span class="string">"com.y.queue"</span>, DISPATCH_QUEUE_SERIAL)</span></span></span><br></pre></td></tr></table></figure>
<h3 id="Concurrent_Diapatch_Queue_并发队列">Concurrent Diapatch Queue 并发队列</h3><p>不会存在任务间的相互依赖,并发执行</p>
<figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">var</span> queue2 : dispatch_queue_t = <span class="function"><span class="title">dispatch_queue_create</span><span class="params">(<span class="string">"com.y.queue"</span>, DISPATCH_QUEUE_CONCURRENT)</span></span></span><br></pre></td></tr></table></figure>
<h3 id="Global_Queue_&_Main_Queue">Global Queue & Main Queue</h3><p>这是系统为我们准备的2个队列:</p>
<ul>
<li>Global Queue 就是系统创建的Concurrent Diapatch Queue</li>
<li>Main Queue 就是系统创建的位于主线程的Serial Diapatch Queue</li>
</ul>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable"><span class="keyword">var</span> queue1</span> = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, <span class="number">0</span>);</span><br><span class="line"><span class="variable"><span class="keyword">var</span> queue2</span> = dispatch_get_main_queue()</span><br></pre></td></tr></table></figure>
<h3 id="常用的队列方法">常用的队列方法</h3><hr>
<h4 id="dispatch_set_target_queue">dispatch_set_target_queue</h4><p>dispatch_set_target_queue 可以指定2个队列的优先级:</p>
<figure class="highlight lisp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dispatch_set_target_queue<span class="list">(<span class="keyword">queue2</span>,queue1)</span><span class="comment">;</span></span><br></pre></td></tr></table></figure>
<h4 id="dispatch_sync">dispatch_sync</h4><p>同步执行队列,会在当前线程执行队列,并且阻塞当前线程中之后运行的代码。</p>
<figure class="highlight livescript"><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">dispatch_sync<span class="function"><span class="params">(dispatch_get_main_queue())</span> { <span class="params">()</span> -></span> Void <span class="keyword">in</span></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"我执行完了才执行后面的代码"</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h4 id="dispatch_aync">dispatch_aync</h4><p>异步执行队列,不会阻塞当前线程中之后运行的代码。</p>
<figure class="highlight livescript"><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">dispatch_sync<span class="function"><span class="params">(dispatch_get_main_queue())</span> { <span class="params">()</span> -></span> Void <span class="keyword">in</span></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"我不影响代码继续执行"</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h4 id="dispatch_after">dispatch_after</h4><p>dispatch_after 用于异步延迟执行,如在主线程中延迟10秒执行。</p>
<figure class="highlight livescript"><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">dispatch_after<span class="function"><span class="params">(dispatch_time(DISPATCH_TIME_NOW, Int64(<span class="number">10</span> * Double(NSEC_PER_SEC))), dispatch_get_main_queue())</span> { <span class="params">()</span> -></span> Void <span class="keyword">in</span></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"我延迟10秒执行"</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h4 id="dispatch_apply">dispatch_apply</h4><p>dispatch_apply, 作用是把指定次数指定的block添加到queue中, 第一个参数是迭代次数,第二个是所在的队列,第三个是当前索引.</p>
<figure class="highlight livescript"><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">dispatch_apply<span class="function"><span class="params">(<span class="number">10</span>, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, <span class="number">0</span>))</span> { <span class="params">(index)</span> -></span> Void <span class="keyword">in</span></span><br><span class="line"> <span class="built_in">print</span>(index);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h4 id="dispatchgroup*">dispatch<em>group</em>*</h4><p>队列组,当我们需要监听一个并发队列中,所有任务都完成了,就可以用到这个group,因为并发队列你并不知道哪一个是最后执行的,所以以单独一个任务是无法监听到这个点的,如果把这些单任务都放到同一个group,那么,我们就能通过dispatch_group_notify方法知道什么时候这些任务全部执行完成了。</p>
<figure class="highlight livescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> queue : dispatch_queue_t = dispatch_queue_create(<span class="string">"com.y.queue"</span>, DISPATCH_QUEUE_CONCURRENT)</span><br><span class="line"><span class="keyword">var</span> group : dispatch_group_t = dispatch_group_create();</span><br><span class="line">dispatch_group_async<span class="function"><span class="params">(group, queue)</span> { <span class="params">()</span> -></span> Void <span class="keyword">in</span></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"0"</span>);</span><br><span class="line">}</span><br><span class="line">dispatch_group_async<span class="function"><span class="params">(group, queue)</span> { <span class="params">()</span> -></span> Void <span class="keyword">in</span></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"1"</span>);</span><br><span class="line">}</span><br><span class="line">dispatch_group_async<span class="function"><span class="params">(group, queue)</span> { <span class="params">()</span> -></span> Void <span class="keyword">in</span></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"2"</span>);</span><br><span class="line">}</span><br><span class="line">dispatch_group_notify<span class="function"><span class="params">(group, dispatch_get_main_queue())</span> { <span class="params">()</span> -></span> Void <span class="keyword">in</span></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"组里的所有任务都执行完了"</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h4 id="dispatch_suspend_&_dispatch_resume">dispatch_suspend & dispatch_resume</h4><p>队列挂起和恢复。</p>
<figure class="highlight livescript"><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">dispatch_suspend(queue);</span><br><span class="line">sleep(<span class="number">3</span>)</span><br><span class="line">dispatch_async<span class="function"><span class="params">(dispatch_get_main_queue())</span> { <span class="params">()</span> -></span> Void <span class="keyword">in</span></span><br><span class="line"> dispatch_resume(queue);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h4 id="其他">其他</h4><p>dispatch_barrier_async / dispatch<em>semaphore</em>* / dispatch_once 等。</p>
</div>
<div class="article-info article-info-index">
<div class="article-tag tagcloud">
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/GCD/">GCD</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/iOS/">iOS</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/学习笔记/">学习笔记</a></li></ul>
</div>
<div class="clearfix"></div>
</div>
</div>
</article>
<article id="post-常用Git命令清单" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2016/01/常用Git命令清单/" class="article-date">
<time datetime="2016-01-07T02:00:39.000Z" itemprop="datePublished">2016-01-07</time>
</a>
</div>
<div class="article-inner">
<input type="hidden" class="isFancy" />
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2016/01/常用Git命令清单/">常用Git命令清单</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<p>[<a href="http://www.ruanyifeng.com/blog/2015/12/git-cheat-sheet.html" target="_blank" rel="external">转载自阮一峰的网络日志</a>] </p>
<p>我每天使用 Git ,但是很多命令记不住。<br>一般来说,日常使用只要记住下图6个命令,就可以了。但是熟练使用,恐怕要记住60~100个命令。</p>
<p><img src="https://dn-yuankeqiang.qbox.me/bg2015120901.png" alt="git"></p>
<p>下面是我整理的常用 Git 命令清单。几个专用名词的译名如下。</p>
<ul>
<li>Workspace:工作区</li>
<li>Index / Stage:暂存区</li>
<li>Repository:仓库区(或本地仓库)</li>
<li>Remote:远程仓库 </li>
</ul>
<h3 id="一、新建代码库">一、新建代码库</h3><pre><code><span class="comment"># 在当前目录新建一个Git代码库</span>
$ git init
<span class="comment"># 新建一个目录,将其初始化为Git代码库</span>
$ git init [project-name]
<span class="comment"># 下载一个项目和它的整个代码历史</span>
$ git <span class="keyword">clone</span> <span class="title">[url</span>]
</code></pre><h3 id="二、配置">二、配置</h3><p>Git的设置文件为.gitconfig,它可以在用户主目录下(全局配置),也可以在项目目录下(项目配置)。 </p>
<pre><code><span class="comment"># 显示当前的Git配置</span>
<span class="variable">$ </span>git config --list
<span class="comment"># 编辑Git配置文件</span>
<span class="variable">$ </span>git config -e [--global]
<span class="comment"># 设置提交代码时的用户信息</span>
<span class="variable">$ </span>git config [--global] user.name <span class="string">"[name]"</span>
<span class="variable">$ </span>git config [--global] user.email <span class="string">"[email address]"</span>
</code></pre><h3 id="三、增加/删除文件">三、增加/删除文件</h3><pre><code><span class="comment"># 添加指定文件到暂存区</span>
<span class="variable">$ </span>git add [file1] [file2] ...
<span class="comment"># 添加指定目录到暂存区,包括子目录</span>
<span class="variable">$ </span>git add [dir]
<span class="comment"># 添加当前目录的所有文件到暂存区</span>
<span class="variable">$ </span>git add .
<span class="comment"># 删除工作区文件,并且将这次删除放入暂存区</span>
<span class="variable">$ </span>git rm [file1] [file2] ...
<span class="comment"># 停止追踪指定文件,但该文件会保留在工作区</span>
<span class="variable">$ </span>git rm --cached [file]
<span class="comment"># 改名文件,并且将这个改名放入暂存区</span>
<span class="variable">$ </span>git mv [file-original] [file-renamed]
</code></pre><h3 id="四、代码提交">四、代码提交</h3><pre><code>
# 提交暂存区到仓库区
$ git <span class="operator"><span class="keyword">commit</span> -<span class="keyword">m</span> [message]
# 提交暂存区的指定文件到仓库区
$ git <span class="keyword">commit</span> [file1] [file2] ... -<span class="keyword">m</span> [message]
# 提交工作区自上次<span class="keyword">commit</span>之后的变化,直接到仓库区
$ git <span class="keyword">commit</span> -a
# 提交时显示所有diff信息
$ git <span class="keyword">commit</span> -v
# 使用一次新的<span class="keyword">commit</span>,替代上一次提交
# 如果代码没有任何新变化,则用来改写上一次<span class="keyword">commit</span>的提交信息
$ git <span class="keyword">commit</span> <span class="comment">--amend -m [message]</span>
# 重做上一次<span class="keyword">commit</span>,并包括指定文件的新变化
$ git <span class="keyword">commit</span> <span class="comment">--amend [file1] [file2] ...</span></span>
</code></pre><h3 id="五、分支">五、分支</h3><pre><code>
<span class="comment"># 列出所有本地分支</span>
<span class="variable">$ </span>git branch
<span class="comment"># 列出所有远程分支</span>
<span class="variable">$ </span>git branch -r
<span class="comment"># 列出所有本地分支和远程分支</span>
<span class="variable">$ </span>git branch -a
<span class="comment"># 新建一个分支,但依然停留在当前分支</span>
<span class="variable">$ </span>git branch [branch-name]
<span class="comment"># 新建一个分支,并切换到该分支</span>
<span class="variable">$ </span>git checkout -b [branch]
<span class="comment"># 新建一个分支,指向指定commit</span>
<span class="variable">$ </span>git branch [branch] [commit]
<span class="comment"># 新建一个分支,与指定的远程分支建立追踪关系</span>
<span class="variable">$ </span>git branch --track [branch] [remote-branch]
<span class="comment"># 切换到指定分支,并更新工作区</span>
<span class="variable">$ </span>git checkout [branch-name]
<span class="comment"># 建立追踪关系,在现有分支与指定的远程分支之间</span>
<span class="variable">$ </span>git branch --set-upstream [branch] [remote-branch]
<span class="comment"># 合并指定分支到当前分支</span>
<span class="variable">$ </span>git merge [branch]
<span class="comment"># 选择一个commit,合并进当前分支</span>
<span class="variable">$ </span>git cherry-pick [commit]
<span class="comment"># 删除分支</span>
<span class="variable">$ </span>git branch -d [branch-name]
<span class="comment"># 删除远程分支</span>
<span class="variable">$ </span>git push origin --delete [branch-name]
<span class="variable">$ </span>git branch -dr [remote/branch]
</code></pre><h3 id="六、标签">六、标签</h3><pre><code>
<span class="comment"># 列出所有tag</span>
$ git <span class="keyword">tag</span>
<span class="title"># 新建一个tag</span>在当前commit
$ git <span class="keyword">tag</span> <span class="title">[tag</span>]
<span class="comment"># 新建一个tag在指定commit</span>
$ git <span class="keyword">tag</span> <span class="title">[tag</span>] [commit]
<span class="comment"># 删除本地tag</span>
$ git <span class="keyword">tag</span> <span class="title">-d</span> [<span class="operator">tag</span>]
<span class="comment"># 删除远程tag</span>
$ git push origin :refs/tags/[tagName]
<span class="comment"># 查看tag信息</span>
$ git show [<span class="operator">tag</span>]
<span class="comment"># 提交指定tag</span>
$ git push [remote] [<span class="operator">tag</span>]
<span class="comment"># 提交所有tag</span>
$ git push [remote] --tags
<span class="comment"># 新建一个分支,指向某个tag</span>
$ git checkout -b [branch] [<span class="operator">tag</span>]
</code></pre><h3 id="七、查看信息">七、查看信息</h3><pre><code><span class="comment"># 显示有变更的文件</span>
$ git status
<span class="comment"># 显示当前分支的版本历史</span>
$ git <span class="command">log</span>
<span class="comment"># 显示commit历史,以及每次commit发生变更的文件</span>
$ git <span class="command">log</span> <span class="comment">--stat</span>
<span class="comment"># 显示某个commit之后的所有变动,每个commit占据一行</span>
$ git <span class="command">log</span> [tag] HEAD <span class="comment">--pretty=format:%s</span>
<span class="comment"># 显示某个commit之后的所有变动,其"提交说明"必须符合搜索条件</span>
$ git <span class="command">log</span> [tag] HEAD <span class="comment">--grep feature</span>
<span class="comment"># 显示某个文件的版本历史,包括文件改名</span>
$ git <span class="command">log</span> <span class="comment">--follow [file]</span>
$ git whatchanged [<span class="type">file</span>]
<span class="comment"># 显示指定文件相关的每一次diff</span>
$ git <span class="command">log</span> -p [<span class="type">file</span>]
<span class="comment"># 显示指定文件是什么人在什么时间修改过</span>
$ git blame [<span class="type">file</span>]
<span class="comment"># 显示暂存区和工作区的差异</span>
$ git diff
<span class="comment"># 显示暂存区和上一个commit的差异</span>
$ git diff <span class="comment">--cached [file]</span>
<span class="comment"># 显示工作区与当前分支最新commit之间的差异</span>
$ git diff HEAD
<span class="comment"># 显示两次提交之间的差异</span>
$ git diff [<span class="keyword">first</span>-branch]...[<span class="keyword">second</span>-branch]
<span class="comment"># 显示某次提交的元数据和内容变化</span>
$ git show [commit]
<span class="comment"># 显示某次提交发生变化的文件</span>
$ git show <span class="comment">--name-only [commit]</span>
<span class="comment"># 显示某次提交时,某个文件的内容</span>
$ git show [commit]:[filename]
<span class="comment"># 显示当前分支的最近几次提交</span>
$ git reflog
</code></pre><h3 id="八、远程同步">八、远程同步</h3><pre><code>
<span class="comment"># 下载远程仓库的所有变动</span>
<span class="variable">$ </span>git fetch [remote]
<span class="comment"># 显示所有远程仓库</span>
<span class="variable">$ </span>git remote -v
<span class="comment"># 显示某个远程仓库的信息</span>
<span class="variable">$ </span>git remote show [remote]
<span class="comment"># 增加一个新的远程仓库,并命名</span>
<span class="variable">$ </span>git remote add [shortname] [url]
<span class="comment"># 取回远程仓库的变化,并与本地分支合并</span>
<span class="variable">$ </span>git pull [remote] [branch]
<span class="comment"># 上传本地指定分支到远程仓库</span>
<span class="variable">$ </span>git push [remote] [branch]
<span class="comment"># 强行推送当前分支到远程仓库,即使有冲突</span>
<span class="variable">$ </span>git push [remote] --force
<span class="comment"># 推送所有分支到远程仓库</span>
<span class="variable">$ </span>git push [remote] --all
</code></pre><h3 id="九、撤销">九、撤销</h3><pre><code># 恢复暂存区的指定文件到工作区
$ git checkout [file]
# 恢复某个<span class="operator"><span class="keyword">commit</span>的指定文件到工作区
$ git checkout [<span class="keyword">commit</span>] [<span class="keyword">file</span>]
# 恢复上一个<span class="keyword">commit</span>的所有文件到工作区
$ git checkout .
# 重置暂存区的指定文件,与上一次<span class="keyword">commit</span>保持一致,但工作区不变
$ git <span class="keyword">reset</span> [<span class="keyword">file</span>]
# 重置暂存区与工作区,与上一次<span class="keyword">commit</span>保持一致
$ git <span class="keyword">reset</span> <span class="comment">--hard</span>
# 重置当前分支的指针为指定<span class="keyword">commit</span>,同时重置暂存区,但工作区不变
$ git <span class="keyword">reset</span> [<span class="keyword">commit</span>]
# 重置当前分支的<span class="keyword">HEAD</span>为指定<span class="keyword">commit</span>,同时重置暂存区和工作区,与指定<span class="keyword">commit</span>一致
$ git <span class="keyword">reset</span> <span class="comment">--hard [commit]</span>
# 重置当前<span class="keyword">HEAD</span>为指定<span class="keyword">commit</span>,但保持暂存区和工作区不变
$ git <span class="keyword">reset</span> <span class="comment">--keep [commit]</span>
# 新建一个<span class="keyword">commit</span>,用来撤销指定<span class="keyword">commit</span>
# 后者的所有变化都将被前者抵消,并且应用到当前分支
$ git revert [<span class="keyword">commit</span>]</span>
</code></pre><h3 id="十、其他">十、其他</h3><pre><code><span class="comment"># 生成一个可供发布的压缩包</span>
<span class="variable">$ </span>git archive
</code></pre><hr>
<p><img src="https://dn-yuankeqiang.qbox.me/Git%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4.jpg" alt="git"></p>
</div>
<div class="article-info article-info-index">
<div class="article-tag tagcloud">
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/git/">git</a></li></ul>
</div>
<div class="clearfix"></div>
</div>
</div>
</article>
<article id="post-Envoy任务执行器及自动化部署项目" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2015/12/Envoy任务执行器及自动化部署项目/" class="article-date">
<time datetime="2015-12-29T06:00:39.000Z" itemprop="datePublished">2015-12-29</time>
</a>
</div>
<div class="article-inner">
<input type="hidden" class="isFancy" />
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2015/12/Envoy任务执行器及自动化部署项目/">Envoy任务执行器及自动化部署项目</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<h3 id="简介">简介</h3><p>Laravel Envoy 提供了简洁、轻量的语法用于定义在远程服务器上可执行的通用任务。通过 Blade 风格的语法,你可以很容易地设置任务从而完成部署、执行 Artisan 命令或其他更多工作。</p>
<p>官网:<a href="https://github.com/laravel/envoy" target="_blank" rel="external">https://github.com/laravel/envoy</a></p>
<h3 id="安装">安装</h3><p>直接通过 Composer 的 global 命令来安装 Envoy:</p>
<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">$ composer global require <span class="string">"laravel/envoy=~1.0"</span></span><br></pre></td></tr></table></figure>
<p>务必将 ~/.composer/vendor/bin 目录加入到 PATH 环境变量中,这样才能在命令行中执行 envoy 命令时找到可执行文件。</p>
<figure class="highlight bash"><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">$ vim ~/.bash_profile</span><br><span class="line">$ <span class="built_in">export</span> PATH=<span class="string">"<span class="variable">$PATH</span>:~/.composer/vendor/bin"</span></span><br><span class="line">$ <span class="built_in">source</span> ~/.bash_profile</span><br></pre></td></tr></table></figure>
<h3 id="Envoy_基本使用">Envoy 基本使用</h3><h4 id="创建任务">创建任务</h4><p>Envoy通过执行 Envoy.blade.php文件来执行SSH任务。<br>在项目目录创建Envoy.blade.php,写入如下内容:</p>
<figure class="highlight php"><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">@servers([<span class="string">'web'</span> => <span class="string">'root@115.27.x.x'</span>])</span><br><span class="line">@task(<span class="string">'foo'</span>, [<span class="string">'on'</span> => <span class="string">'web'</span>])</span><br><span class="line"> ls -la</span><br><span class="line">@endtask</span><br></pre></td></tr></table></figure>
<p>如上所示,@servers数组定义了服务器地址,后续的任务声明中,你可以在 on 选项中直接引用。@task声明里定义了一个名称为foo的任务,用于执行ls -la。</p>
<h3 id="执行任务">执行任务</h3><p>使用run命令来执行任务</p>
<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">$ envoy run foo</span><br></pre></td></tr></table></figure>
<p>执行run命令后,控制台会要求输入服务器的密码。<br>输入密码后即可看到在服务器执行ls -la的结果。<br><img src="https://dn-yuankeqiang.qbox.me/E5BFE028-7948-4522-B225-CC5661CDE7E9.png" alt="sd"></p>
<p>如有需要,你还可以通过命令行向 Envoy 文件传递参数:</p>
<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">$ envoy run deploy --path=/home/www</span><br></pre></td></tr></table></figure>
<figure class="highlight crystal"><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="variable">@servers</span>([<span class="string">'web'</span> => <span class="string">'192.168.1.1'</span>])</span><br><span class="line"></span><br><span class="line"><span class="variable">@task</span>(<span class="string">'deploy'</span>, [<span class="string">'on'</span> => <span class="string">'web'</span>])</span><br><span class="line"> cd <span class="expansion">{{<span class="variable">$path</span>}}</span></span><br><span class="line"> ls</span><br><span class="line"><span class="variable">@endtask</span></span><br></pre></td></tr></table></figure>
<p>可以在 @setup 指令中声明变量,并在 Envoy 文件中执行普通的 PHP 代码:</p>
<figure class="highlight xquery"><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">@setup</span><br><span class="line"> <span class="variable">$now</span> = new DateTime();</span><br><span class="line"> <span class="variable">$environment</span> = isset(<span class="variable">$env</span>) ? <span class="variable">$env</span> : <span class="string">"testing"</span>;</span><br><span class="line">@endsetup</span><br></pre></td></tr></table></figure>
<p>还有多服务器并行执行:</p>
<figure class="highlight less"><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="variable">@servers</span>([<span class="string">'web-1'</span> => <span class="string">'192.168.1.1'</span>, <span class="string">'web-2'</span> => <span class="string">'192.168.1.2'</span>])</span><br><span class="line"><span class="variable">@task</span>(<span class="string">'deploy'</span>, [<span class="string">'on'</span> => [<span class="string">'web-1'</span>, <span class="string">'web-2'</span>],<span class="string">'parallel'</span> => true])</span><br><span class="line"><span class="variable">@endtask</span></span><br></pre></td></tr></table></figure>
<p>其他,@macro任务宏声明让你只用一条命令就能顺序执行一组任务, @after声明在任务执行完后处理,如发一封邮件通知等。</p>
<p>参考<a href="https://laravel.com/docs/5.1/envoy" target="_blank" rel="external">https://laravel.com/docs/5.1/envoy</a></p>
<h3 id="使用envoy部署项目">使用envoy部署项目</h3><p>在实际项目中我们常常使用git来作版本控制,开发完成后将代码提交到远程代码库。<br>在产品发布新版本时,可以在服务器直接git pull下来完成更新。我们将一系列任务通过envoy来执行,如:</p>
<figure class="highlight crystal"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">@servers</span>([<span class="string">'web'</span> => <span class="string">'115.27.x.x'</span>])</span><br><span class="line"><span class="variable">@task</span>(<span class="string">'deploy'</span>, [<span class="string">'on'</span> => <span class="string">'web'</span>])</span><br><span class="line"> cd /home/www/laravel</span><br><span class="line"> git pull origin <span class="expansion">{{ <span class="variable">$branch</span> }}</span></span><br><span class="line"> php artisan migrate</span><br><span class="line"><span class="variable">@endtask</span></span><br><span class="line"></span><br><span class="line"><span class="variable">@after</span></span><br><span class="line">echo <span class="string">"\r\n新版本发布完成!\r\n"</span>;</span><br><span class="line"><span class="variable">@endafter</span></span><br></pre></td></tr></table></figure>
<figure class="highlight applescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ envoy <span class="command">run</span> deploy <span class="comment">--branch=master</span></span><br></pre></td></tr></table></figure>
<hr>
<p>基于envoy的项目:<br>Envoyer : <a href="https://envoyer.io/" target="_blank" rel="external">https://envoyer.io/</a><br>envoy-deploy : <a href="https://github.com/papertank/envoy-deploy" target="_blank" rel="external">https://github.com/papertank/envoy-deploy</a></p>
</div>
<div class="article-info article-info-index">
<div class="article-tag tagcloud">
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Envoy/">Envoy</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Laravel/">Laravel</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/自动化/">自动化</a></li></ul>
</div>
<div class="clearfix"></div>
</div>
</div>
</article>
<article id="post-细说iOS消息推送" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2015/11/细说iOS消息推送/" class="article-date">
<time datetime="2015-11-26T09:00:39.000Z" itemprop="datePublished">2015-11-26</time>
</a>
</div>
<div class="article-inner">
<input type="hidden" class="isFancy" />
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2015/11/细说iOS消息推送/">细说iOS消息推送</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<blockquote>
<p>[转载自<a href="https://blog.leancloud.cn/1163/" target="_blank" rel="external">leancloud</a>]</p>
</blockquote>
<h3 id="APNs_的推送机制">APNs 的推送机制</h3><p>与 Android 上我们自己实现的推送服务不一样,Apple 对设备的控制非常严格,消息推送的流程必须要经过 APNs: </p>
<p><img src="https://dn-yuankeqiang.qbox.me/remote_notif_simple_2x.png" alt="swift"> </p>
<p>这里 Provider 是指某个应用的 Developer,当然如果开发者使用 LeanCloud 的服务,把发送消息的请求委托给我们,那么这里的 Provider 就是 LeanCloud 的推送服务程序了。上图可以分为三步: </p>
<ol>
<li>LeanCloud 推送服务程序把要发送的消息、目的设备的唯一标识打包,发给 APNs。 </li>
<li>APNs 在自身的已注册 Push 服务的应用列表中,查找有相应标识的设备,并把消息发送到设备。 </li>
<li>iOS 系统把发来的消息传递给相应的应用程序,并且按照设定弹出 Push 通知。 </li>
</ol>
<p>为了实现消息推送,有两点非常重要: </p>
<p>1、App 的推送证书<br>要能够完整实现一条消息推送,需要我们在 App ID 中打开 Push Notifications,需要我们准备好 Provisioning Profile 和 SSL 证书,并且一定要注意 Development 和 Distribution 环境是需要分开的。最后,把 SSL 证书导入到 LeanCloud 平台,就可以尝试远程消息推送了。具体的操作流程可以参考我们的使用指南:iOS 推送证书设置指南 。 </p>
<p>2、设备标识 DeviceToken<br>知道了谁要推送,或者说要推送给哪个应用之后,APNs 还需要知道推到哪台设备上,这就是设备标识的作用。获取设备标识的流程如下: </p>
<pre><code><span class="number">1.</span> 应用打开推送开关,用户要确认 TA 希望获得该应用的推送消息;
<span class="number">2.</span> 应用获得一个 DeviceToken;
<span class="number">3.</span> 应用将 DeviceToken 保存起来,这里就是通过 [AVInstallation saveInBackground] 将 DeviceToken 保存到 LeanCloud;
<span class="number">4.</span> 当某些特定事件发生,开发者委托 LeanCloud 来发送推送消息,这时候 LeanCloud 的推送服务器就会给 APNs 发送一则推送消息,APNs 最后消息送到用户设备。
</code></pre><p><a href="https://dn-yuankeqiang.qbox.me/registration_sequence_2x.png" target="_blank" rel="external">leancloud</a> </p>
<h3 id="推送相关的几个概念">推送相关的几个概念</h3><h4 id="消息类型">消息类型</h4><p>一条消息推送过来,可以有如下几种表现形式: </p>
<ul>
<li>显示一个 alert 或者 banner,展现具体内容。 </li>
<li>在应用 icon 上提示一个新到消息数。 </li>
<li>播放一段声音。 </li>
</ul>
<p>开发者可以在每次推送的时候设置,在推送达到用户设备时开发者也可以选择不同的提示方式。 </p>
<h4 id="本地消息通知">本地消息通知</h4><p>iOS 上有两种消息通知,一种是本地消息(Local Notification),一种是远程消息 (Push Notification,也叫 Remote Notification),设计这两种通知的目的都是为了提醒用户,现在有些什么新鲜的事情发生了,吸引用户重新打开应用。 </p>
<p>本地消息什么时候有用呢?譬如你正在做一个 To-do 的工具类应用,对于用户加入的每一个事项,都会有一个完成的时间点,用户可以要求这个 To-do 应用在事项过期之前的某一个时间点提醒一下 TA。为了达到这一目的,应用就可以调度一个本地通知,在时间点到了之后发出一个 Alert 消息或者其他提示。 </p>
<p>我们在处理推送消息的时候,也可以综合运用这两种方式。 </p>
<h4 id="代码里面如何实现推送">代码里面如何实现推送</h4><p>首先,我们要获取 DeviceToken。 </p>
<p>应用需要每次启动的时候都去注册远程通知——通过调用 UIApplication 的 registerForRemoteNotificationTypes: 方法,传递给它你希望支持的消息类型参数即可,例如: </p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // do some initiale working ... [application registerForRemoteNotificationTypes:UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound]; return YES; }</span><br></pre></td></tr></table></figure>
<p>如果注册成功,APNs 会返回给你设备的 token,iOS 系统会把它传递给 app delegate 代理 application:didRegisterForRemoteNotificationsWithDeviceToken: 方法,你应该在这个方法里面把 token 保存到 LeanCloud 后台,例如: </p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">- (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { NSLog(@"Receive DeviceToken: %@", deviceToken); AVInstallation *currentInstallation = [AVInstallation currentInstallation]; [currentInstallation setDeviceTokenFromData:deviceToken]; [currentInstallation saveInBackground]; }</span><br></pre></td></tr></table></figure>
<p>如果注册失败,application:didFailToRegisterForRemoteNotificationsWithError: 方法会被调用,通过 NSError 参数你可以看到具体的出错信息,例如: </p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"> - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { NSLog(@"注册失败,无法获取设备 ID, 具体错误: %@", error); }</span><br></pre></td></tr></table></figure>
<p>请注意,注册流程需要在应用每次启动时调用,这并不不会带来额外的负担,因为 iOS 操作系统在第一次获得了有效的 device token 之后,会本地缓存起来,以后应用再调用 registerForRemoteNotificationTypes: 的时候会立刻返回,并不会再进行网络请求。另外,应用层面不应该对 device token 进行缓存,因为 device token 也有可能变化——如果用户重装了操作系统,那么 APNs 再次给出的 device token 就会和之前的不一样,又或者是,用户 恢复了原来的备份到新的设备上,那么原来的 device token 也会失效。 </p>
<p>其次,我们要处理收到消息之后的回调。 </p>
<p>我们可以设想一下消息通知的几种使用场景: </p>
<p>1/ 在应用没有被启动的时候,接收到了消息通知。这时候操作系统会按照默认的方式来展现一个 alert 消息,在应用的 icon 上标记一个数字,甚至播放一段声音。<br>2/ 用户看到消息之后,点击了一下 action 按钮或者点击了应用图标。如果 action 按钮被点击了,系统会通过调用 application:didFinishLaunchingWithOptions: 这个代理方法来启动应用,并且会把 notification 的 payload 数据传递进去。如果应用图标被点击了,系统也一样会调用 application:didFinishLaunchingWithOptions: 这个代理方法来启动应用,唯一不同的是这时候启动参数里面不会有任何 notification 的信息。示例代码如下:</p>
<pre><code class="objective-c"> - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // do initializing works ... if (launchOptions) { // do something else ... [AVAnalytics trackAppOpenedWithLaunchOptions:launchOptions]; } [application registerForRemoteNotificationTypes:UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound]; return YES; }
</code></pre>
<p>3/ 如果远程消息发送过来的时候,应用正在运行,这时候会发生什么呢?应用代理的 application:didReceiveRemoteNotification: 方法会被调用,同时远程消息中的 payload 数据会作为参数传递进去。示例代码如下: </p>
<pre><code class="objective-c"> - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo { if (application.applicationState == UIApplicationStateActive) { // 转换成一个本地通知,显示到通知栏,你也可以直接显示出一个 alertView,只是那样稍显 aggressive:) UILocalNotification *localNotification = [[UILocalNotification alloc] init]; localNotification.userInfo = userInfo; localNotification.soundName = UILocalNotificationDefaultSoundName; localNotification.alertBody = [[userInfo objectForKey:@"aps"] objectForKey:@"alert"]; localNotification.fireDate = [NSDate date]; [[UIApplication sharedApplication] scheduleLocalNotification:localNotification]; } else { [AVAnalytics trackAppOpenedWithRemoteNotificationPayload:userInfo]; } }
</code></pre>
</div>
<div class="article-info article-info-index">
<div class="article-tag tagcloud">
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/iOS/">iOS</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/swift/">swift</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/推送/">推送</a></li></ul>
</div>
<div class="clearfix"></div>
</div>
</div>
</article>
<article id="post-swift实现UITableViewCell滑动删除" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2015/11/swift实现UITableViewCell滑动删除/" class="article-date">
<time datetime="2015-11-06T06:00:39.000Z" itemprop="datePublished">2015-11-06</time>
</a>
</div>
<div class="article-inner">
<input type="hidden" class="isFancy" />
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2015/11/swift实现UITableViewCell滑动删除/">swift实现UITableViewCell滑动删除</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<h3 id="UITableViewCell滑动删除">UITableViewCell滑动删除</h3><p>实现Cell的滑动删除,只需重写UITableViewDelegate代理中的如下2个方法。 </p>
<pre><code class="swift">
<span class="comment">// 进入编辑模式,按下编辑按钮后执行 </span>
<span class="func"><span class="keyword">func</span> <span class="title">tableView</span><span class="params">(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath)</span></span> {
<span class="comment">//删除数组中的数据</span>
<span class="keyword">self</span>.dataArray.removeAtIndex(indexPath.row);
<span class="comment">//删除单元格</span>
<span class="keyword">self</span>.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: <span class="type">UITableViewRowAnimation</span>.<span class="type">Top</span>);
}
<span class="comment">//修改文字 </span>
<span class="func"><span class="keyword">func</span> <span class="title">tableView</span><span class="params">(tableView: UITableView, titleForDeleteConfirmationButtonForRowAtIndexPath indexPath: NSIndexPath)</span></span> -> <span class="type">String</span>? {
<span class="keyword">return</span> <span class="string">"啊~不要"</span>;
}
</code></pre>
<p> <img src="https://dn-yuankeqiang.qbox.me/uitableviewcell11.gif" alt="swift"> </p>
<h3 id="UITableview_自定义Action">UITableview 自定义Action</h3><p>上面只实现了UITableViewCell的单个编辑Action,若要自定义Action,可使用如下的代理方法。 </p>
<pre><code class="swift">
<span class="func"><span class="keyword">func</span> <span class="title">tableView</span><span class="params">(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath)</span></span> -> [<span class="type">UITableViewRowAction</span>]? {
<span class="keyword">let</span> action1 = <span class="type">UITableViewRowAction</span>(style: <span class="type">UITableViewRowActionStyle</span>.<span class="type">Default</span>, title: <span class="string">"删除"</span>) { (action , index) -> <span class="type">Void</span> <span class="keyword">in</span>
<span class="built_in">print</span>(<span class="string">"点击了删除"</span>);
}
<span class="keyword">let</span> action2 = <span class="type">UITableViewRowAction</span>(style: <span class="type">UITableViewRowActionStyle</span>.<span class="type">Normal</span>, title: <span class="string">"取消关注"</span>) { (action , index) -> <span class="type">Void</span> <span class="keyword">in</span>
<span class="built_in">print</span>(<span class="string">"点击了取消关注"</span>);
}
<span class="keyword">return</span> [action1,action2];
}
</code></pre>
<p><img src="https://dn-yuankeqiang.qbox.me/uitableviewcell2.gif" alt="swift"> </p>
</div>
<div class="article-info article-info-index">
<div class="article-tag tagcloud">
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/iOS/">iOS</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/swift/">swift</a></li></ul>
</div>
<div class="clearfix"></div>
</div>
</div>
</article>
<article id="post-持续集成是什么" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2015/11/持续集成是什么/" class="article-date">
<time datetime="2015-11-02T06:46:39.000Z" itemprop="datePublished">2015-11-02</time>
</a>
</div>
<div class="article-inner">
<input type="hidden" class="isFancy" />
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2015/11/持续集成是什么/">持续集成是什么?</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<blockquote>
<p>[转载自<a href="http://www.ruanyifeng.com/blog/2015/09/continuous-integration.html" target="_blank" rel="external">阮一峰的网络日志</a>]</p>
</blockquote>
<p>互联网软件的开发和发布,已经形成了一套标准流程,最重要的组成部分就是持续集成(Continuous integration,简称CI)。 </p>
<p>本文简要介绍持续集成的概念和做法。 </p>
<p><img src="https://dn-yuankeqiang.qbox.me/bg2015092301.png" alt="test"> </p>
<h3 id="一、概念">一、概念</h3><p>持续集成指的是,频繁地(一天多次)将代码集成到主干。 </p>
<p>它的好处主要有两个。 </p>
<ol>
<li>快速发现错误。每完成一点更新,就集成到主干,可以快速发现错误,定位错误也比较容易。 </li>
<li>防止分支大幅偏离主干。如果不是经常集成,主干又在不断更新,会导致以后集成的难度变大,甚至难以集成。 </li>
</ol>
<p>持续集成的目的,就是让产品可以快速迭代,同时还能保持高质量。它的核心措施是,代码集成到主干之前,必须通过自动化测试。只要有一个测试用例失败,就不能集成。 </p>
<p>Martin Fowler说过,”持续集成并不能消除Bug,而是让它们非常容易发现和改正。” </p>
<p>与持续集成相关的,还有两个概念,分别是持续交付和持续部署。 </p>
<h3 id="二、持续交付">二、持续交付</h3><p>持续交付(Continuous delivery)指的是,频繁地将软件的新版本,交付给质量团队或者用户,以供评审。如果评审通过,代码就进入生产阶段。 </p>
<p>持续交付可以看作持续集成的下一步。它强调的是,不管怎么更新,软件是随时随地可以交付的。 </p>
<h3 id="三、持续部署">三、持续部署</h3><p>持续部署(continuous deployment)是持续交付的下一步,指的是代码通过评审以后,自动部署到生产环境。 </p>
<p>持续部署的目标是,代码在任何时刻都是可部署的,可以进入生产阶段。 </p>
<p>持续部署的前提是能自动化完成测试、构建、部署等步骤。它与持续交付的区别,可以参考下图。 </p>
<p><img src="https://dn-yuankeqiang.qbox.me/bg2015092302.jpg" alt="test"> </p>
<h3 id="四、流程">四、流程</h3><p>根据持续集成的设计,代码从提交到生产,整个过程有以下几步。 </p>
<p>4.1 提交<br>流程的第一步,是开发者向代码仓库提交代码。所有后面的步骤都始于本地代码的一次提交(commit)。 </p>
<p>4.2 测试(第一轮)<br>代码仓库对commit操作配置了钩子(hook),只要提交代码或者合并进主干,就会跑自动化测试。<br>测试有好几种。 </p>
<ul>
<li>单元测试:针对函数或模块的测试 </li>
<li>集成测试:针对整体产品的某个功能的测试,又称功能测试 </li>
<li>端对端测试:从用户界面直达数据库的全链路测试 </li>
</ul>
<p>第一轮至少要跑单元测试。 </p>
<p>4.3 构建<br>通过第一轮测试,代码就可以合并进主干,就算可以交付了。 </p>
<p>交付后,就先进行构建(build),再进入第二轮测试。所谓构建,指的是将源码转换为可以运行的实际代码,比如安装依赖,配置各种资源(样式表、JS脚本、图片)等等。 </p>
<p>常用的构建工具如下。 </p>
<ul>
<li><a href="http://jenkins-ci.org/" target="_blank" rel="external">Jenkins</a> </li>
<li><a href="https://travis-ci.com/" target="_blank" rel="external">Travis</a> </li>
<li><a href="https://www.codeship.io/" target="_blank" rel="external">Codeship</a> </li>
<li><a href="http://stridercd.com/" target="_blank" rel="external">Strider</a> </li>
</ul>
<p>Jenkins和Strider是开源软件,Travis和Codeship对于开源项目可以免费使用。它们都会将构建和测试,在一次运行中执行完成。 </p>
<p>4.4 测试(第二轮)<br>构建完成,就要进行第二轮测试。如果第一轮已经涵盖了所有测试内容,第二轮可以省略,当然,这时构建步骤也要移到第一轮测试前面。 </p>
<p>第二轮是全面测试,单元测试和集成测试都会跑,有条件的话,也要做端对端测试。所有测试以自动化为主,少数无法自动化的测试用例,就要人工跑。 </p>
<p>需要强调的是,新版本的每一个更新点都必须测试到。如果测试的覆盖率不高,进入后面的部署阶段后,很可能会出现严重的问题。 </p>
<p>4.5 部署<br>通过了第二轮测试,当前代码就是一个可以直接部署的版本(artifact)。将这个版本的所有文件打包( tar filename.tar * )存档,发到生产服务器。 </p>
<p>生产服务器将打包文件,解包成本地的一个目录,再将运行路径的符号链接(symlink)指向这个目录,然后重新启动应用。这方面的部署工具有Ansible,Chef,Puppet等。 </p>
<p>4.6 回滚<br>一旦当前版本发生问题,就要回滚到上一个版本的构建结果。最简单的做法就是修改一下符号链接,指向上一个版本的目录。 </p>
<h3 id="五、参考链接">五、参考链接</h3><p>Gergely Nemeth, <a href="https://blog.risingstack.com/continuous-deployment-of-node-js-applications/" target="_blank" rel="external">Continuous Deployment of Node.js Applications</a><br>Codeship, <a href="https://codeship.com/continuous-integration-essentials" target="_blank" rel="external">Continuous Integration Essentials</a> </p>
</div>
<div class="article-info article-info-index">
<div class="article-tag tagcloud">
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/持续集成/">持续集成</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/测试/">测试</a></li></ul>
</div>
<div class="clearfix"></div>
</div>
</div>
</article>