-
Notifications
You must be signed in to change notification settings - Fork 0
/
rss.xml
executable file
·1578 lines (1295 loc) · 98.5 KB
/
rss.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
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
<?xml version="1.0"?>
<rss version="2.0">
<channel>
<title>淘小僧</title>
<link>http://www.codesimplelife.com</link>
<pubDate>2014-02-10 02:48:28 +0800</pubDate>
<item>
<title>如果处理PNG图片,从而降低IPA包大小以及提高图片使用时的性能</title>
<link>http://www.codesimplelife.com/2014/02/14/compress_png_file_to_decrease_ipa_size.html</link>
<pubDate>2014-02-14 12:00:00 +0800</pubDate>
<description><h2>降低包大小</h2>
<p>1:使用图片压缩工具ImageOptm来对图片进行压缩,该工具会使用多种算法挑选压缩后大小最小的一张图输出。
2:可以考虑使用jpg图片
3:如果手工自己压缩图片,则最好不要关闭xcode工程里自带的png图片压缩选项,即targets-&gt; Build Settings-&gt;Compress PNG Files设置为NO,否则可能引起一些已经被手动压缩过的图片经过xcode处理后,再次变大了一些。注意这个选项只适用于png图片
4:如果一张图可以通过拉伸等处理得到最后的效果,可以考虑切图的时候以九宫格的方式切小图。</p>
<h2>提高图片使用性能:</h2>
<p>1:在图片质量可以接受的情况下,图片越小越好
2:如果不要使用透明度,则可以考虑图片不带有alpha通道。
3:图片的大小最好等于UI显示时的尺寸,否则图片缩放处理会消耗一定的性能(这里涉及了矩阵运算)
4:图片draw的时候,最好可以设置draw的时候图片不透明。
5:显示图片的时候最好像素对齐,能降低抗锯齿的计算。</p>
</description>
</item>
<item>
<title>python小计</title>
<link>http://www.codesimplelife.com/2013/12/12/record_python_tips.html</link>
<pubDate>2013-12-12 12:00:00 +0800</pubDate>
<description><h1>Python</h1>
<h2>记录</h2>
<p>os.walk(dir) 遍历文件夹里的文件
str.rfind(&ldquo;abc&rdquo;) 反向查找abc的位置,返回pos
str.replace(&rdquo;kk&rdquo;, &ldquo;aa&rdquo;) 替换kk为aa
cmp(a,b) 比较字符串a和b
os.path.basename(path) #返回文件名
os.path.exists(path) #路径存在则返回True,路径损坏返回False
os.path.isfile(path) #判断路径是否为文件
os.path.isdir(path) #判断路径是否为目录
os.path.islink(path) #判断路径是否为链接
os.path.splitext(path) #分割路径,返回路径名和文件扩展名的元组
&gt;&gt;&gt; os.path.splitext(&lsquo;file.1.2&rsquo;)
(&lsquo;file.1&rsquo;, &lsquo;.2&rsquo;)
os.remove(file) #删除文件
os.rename(fromFile, toFile)#移动文件</p>
</description>
</item>
<item>
<title>many tips</title>
<link>http://www.codesimplelife.com/2013/10/04/manytips.html</link>
<pubDate>2013-10-04 12:00:00 +0800</pubDate>
<description><h1>Tips</h1>
<ol>
<li>问:[centos]Can&rsquo;t ssh to server with root account because of gitosis?
答:Just remove /root/.ssh/authorized_keys</li>
</ol>
</description>
</item>
<item>
<title>Core Text</title>
<link>http://www.codesimplelife.com/2013/10/01/coretext.html</link>
<pubDate>2013-10-01 12:00:00 +0800</pubDate>
<description><h1>Core Text</h1>
<ol>
<li>Here you need to create a path which bounds the area where you will be drawing text. Core Text on the Mac supports different shapes like rectangles and circles, but for the moment iOS supports only rectangular shape for drawing with Core Text. In this simple example, you’ll use the entire view bounds as the rectangle where you will be drawing by creating a CGPath reference from self.bounds.</li>
</ol>
</description>
</item>
<item>
<title>IOS开发</title>
<link>http://www.codesimplelife.com/2013/09/21/iosDevTip.html</link>
<pubDate>2013-09-21 12:00:00 +0800</pubDate>
<description><p>app打开默认是混合音模式
开始录音的时候如果选择的模式是playandrecord,则也能支持播放。</p>
<p>ios7系统里跑ios6兼容模式,删除按钮的位置有变化</p>
</description>
</item>
<item>
<title>Nodejs中的async的使用</title>
<link>http://www.codesimplelife.com/2013/08/12/how_to_use_async_in_nodejs.html</link>
<pubDate>2013-08-12 12:00:00 +0800</pubDate>
<description><h1>Nodejs中的async的使用</h1>
<p>Nodejs中最最感觉不舒服的是因为全异步引起的代码层级变的更大而导致阅读更加麻烦。
所以解决因为nodejs的这个问题,我按照前人的提示选择了async的框架</p>
<p>其实这个框架里我更多的是使用async的auto方法
因为这个方法可以并行,串行,按照我写的规则去执行一些异步方法。</p>
<p>比如可以这样
async.auto({
task1:function(cb, paramData){
},
task2:function(cb, paramData){
},
task3:[&lsquo;task1&rsquo;,&lsquo;task2&rsquo;,function(cb, paramData){
}
task4:[&lsquo;task3&rsquo;,function(cb, paramData){
}
},function(err, result){
});</p>
<p>这个流程最后跑的是task1和task2并发执行,执行完后,task3执行,然后再task4执行,最终完成后执行最下面的function函数,如果中途的cb中的参数不为null,则直接退出上面的task流程,直接执行最后的function函数。</p>
</description>
</item>
<item>
<title>tips_when_install_node_modules</title>
<link>http://www.codesimplelife.com/2013/08/12/tips_when_install_node_modules.html</link>
<pubDate>2013-08-12 12:00:00 +0800</pubDate>
<description><h1>安装自己的node代码及安装node_modules的注意</h1>
<ol>
<li><p>package.json里的版本号似乎需要1.0.1这种类似的格式</p></li>
<li><p>将你的代码打包后,你需要在其他未安装的服务器上运行时,则你可能需要临时安装你的node相关的node_module,此时你可以不要将你原来的代码里的nodemoudle带过来,而是通过npm install安装node module模块,当然最好是要求你的node module里的模块的版本和你开发测试的版本一致。否则你可能会出现异常的情况。所以注意修改设置你的package.json里的相关依赖模块的版本号</p></li>
<li></li>
</ol>
</description>
</item>
<item>
<title>Core Data</title>
<link>http://www.codesimplelife.com/2013/08/12/core_data.html</link>
<pubDate>2013-07-11 12:00:00 +0800</pubDate>
<description><h1>Core Data</h1>
<p>1: Core Data
数据库操作当有insert操作做merge通知的时候会立即触发processPendingChange,但是如果没有insert,而只有delete和update的时候,则只会投放一个processPendingChange但不会立即执行,所以当一个save先update然后delete的时候,merge就出问题了。</p>
<p>工作:
今天依然是对首页等地方进行性能调优
改善的点是
1:计算时间,格式化显示的时候,ios相对比较耗时,此时如果对性能有一定要求的话,还是考虑用c的api实现一把,另外stringWithFormat在对性能有一定要求的地方也应该尽量考虑用C函数拼接好后再一次搞定。
经过函数的重写,该函数性能提升2.4倍</p>
<p>2:core data数据库fetch的时候,如果你只为了查询一个对象,而且这个对象之前已经在当前contxt上的话,那么fetch操作依然是一个耗时的操作,原因应该是由于core data没有暴露出来的key,那么你查询数据库中的条件理论上查询出来的都是一个数组,而且及时表是一个空表,core data的一次fetch依然会尝试查询数据库一把。也就造成了性能的直接下降。
但是据说如果你是用了objectid去查询的话就快一些,因为你的对象已经在内存中了,并且一个managedObject有个fault属性,如果你查询的objectId对应的对象的fault属性不是true的话,你的查询应该就不会倒数据库里在查询呢一次。</p>
<p>3:个人感觉如果需要使用core data,应该尽可能的不要直接完全依然coredata的managedObject对象,一是可以一定程度解耦,不然一旦更换core data的机制,你就代码里也改动很大了。</p>
<p>4:c函数里(t m) 结构体和和time_t可以通过mktime,gmtime和localtime去互相转化,注意localtime是当地时区的时间。获取的time_t都是从1970年开始到现在的时间。另外注意的是t m里tm_mon是从0开始算起的月份,所以正常情况下你需要进行+1处理表示正常的月份。
另外注意如果是考虑到多线程的话,不要使用localtime,这个是线程不安全的,应该使用localtime_r等接口。
struct tm localNow;
time_t rawtime = (time_t)offsetTimeInterval;
localtime_r(&amp;rawtime, &amp;localNow);
这样就填充好了一个包含年月日时分秒的数据结构了。</p>
<p>5:网上一些关于性能调优的要点记录:</p>
<ul>
<li>在Image Views中调整图片大小
如果要在<code>UIImageView</code>中显示一个来自bundle的图片,你应保证图片的大小和UIImageView的大小相同。在运行中缩放图片是很耗费资源的,特别是<code>UIImageView</code>嵌套在<code>UIScrollView</code>中的情况下。</li>
</ul>
<p>如果图片是从远端服务加载的你不能控制图片大小,比如在下载前调整到合适大小的话,你可以在下载完成后,最好是用background thread,缩放一次,然后在UIImageView中使用缩放后的图片。</p>
<p>非工作:
nodejs摸索出package.json为什么老是解析失败。。。
不知道为什么,version版本信息必须是0.0.1这种类型的,如果写成1.0则失败。。。</p>
</description>
</item>
<item>
<title>more about markdown</title>
<link>http://www.codesimplelife.com/2013/04/06/more-about-markdown.html</link>
<pubDate>2013-06-05 12:00:00 +0800</pubDate>
<description><h1>关于几个之前忽略的markdown的语法</h1>
<ol>
<li>行末连续两个空格然后回车,可以造成文本换行</li>
</ol>
<pre><code>abcdefg“空格空格”
hijklmn
</code></pre>
<p>abcdefg<br />
hijklmn</p>
<ol>
<li><p><a name="md-anchor" id="md-anchor">段落缩进</a></p>
<p>段落缩进演示:
&gt; 第一段
&gt;
&gt;&gt; 第二段
&gt;&gt;&gt; 第三段
&gt;&gt;
&gt;&gt; 第二段</p></li>
<li><p>四个空格缩进是代码块:</p></li>
<li><p>无序列表</p>
<ul>
<li>星号、减号、加号开始列表。
<ul>
<li>列表层级和缩进有关。
<ul>
<li>和具体符号无关。</li>
</ul></li>
</ul></li>
<li>返回一级列表。</li>
</ul></li>
<li><p>有序列表</p>
<ol>
<li><p>数字和点开始有序列表。</p>
<ol>
<li><p>注意子列表的缩进位置。</p>
<ol>
<li>三级列表。</li>
<li>编号会自动更正。</li>
</ol></li>
<li><p>二级列表,编号自动更正为2。</p></li>
</ol></li>
<li><p>返回一级列表。</p></li>
</ol></li>
<li><p>上标、下标</p>
<ul>
<li>Water: H<sub>2</sub>O</li>
<li>E = mc<sup>2</sup></li>
</ul></li>
<li><p>等宽字体
行内反引号嵌入代码,如: <code>git status</code> 。</p></li>
<li><p>内部跳转
<a name="md-anchor" id="md-anchor"></a></p>
<p>跳转至 <a href="#md-anchor">文内链接</a> 。</p></li>
</ol>
<p>摘抄自:<a href="http://www.worldhello.net/gotgithub/appendix/markups.html">链接</a></p>
</description>
</item>
<item>
<title>git使用及github</title>
<link>http://www.codesimplelife.com/2013/05/30/git_useage_and_github.html</link>
<pubDate>2013-05-30 12:00:00 +0800</pubDate>
<description><h1>git使用及github</h1>
<p>git 和github越来越多人使用,作为一个程序员,应该懂的怎么去用,而我就是个不怎么会用的人,所以我的主管在我刚开始上班的时候就要我去熟悉git那套东西。</p>
<p>当时我依然不是很懂,但最近开始迷糊的懂了,下面是我这段时间的心得。</p>
<h2>创建ssh从而能免登访问github。</h2>
<p>命令<code>ssh-keygen</code>去创建公钥私钥,如果你只希望访问一个服务器的话,比如github,那么你就放到默认的目录下,并且使用默认的私钥和公钥文件路径,即~/.ssh/id_rsa和~/.ssh/id_rsa.pub,其中id_rsa.pub是公钥文件。我们需要将这个文件里的数据复制并粘贴到github上的ssh key里。从而通过ssh就可以面等访问github了。</p>
<p>如果你有多个服务器需要访问,比如我们还需要访问gitlab,那么,这个时候,需要~/.ssh/config文件里的配置来区分服务器和ssh秘钥。</p>
<p>比如我自己的配置为</p>
<pre><code>Host github.com
HostName github.com
User [email protected]
IdentityFile /Users/Dick/.ssh/github_taoxiaoseng_rsa
Host gitlab.alibaba-inc.com
HostName gitlab.alibaba-inc.com
User [email protected]
IdentityFile /Users/Dick/.ssh/gitlab_rsa
</code></pre>
<p>也就是说访问github用文件github_taoxiaoseng_rsa,访问gitlab用gitlab_rsa。注意公钥也需要上传到相应的服务器里。</p>
<p>其实还有些网上也经常说,这里不细提了。比如这些东西</p>
<pre><code>git config --global user.name &quot;you name&quot;
git config --global user.email [email protected]
</code></pre>
<h2>如何创建git仓库</h2>
<p>其实每次你创建github里的仓库时,都会告诉你怎么去push,这里还是做下说明,讲我理解的说下。</p>
<pre><code>mkdir helloworld
cd helloworld
git init 创建一个空的数据仓库
touch readme.md
git add . 自动判断添加相应文件到git中
git commit -m &quot;commit first file&quot; 提交并且添加提交日志
git remote add origin [email protected]:TaoXiaoSeng/test.git 这个最关键,说的是在本地git中添加remote的git地址,并且以origin命名,这样以后我们就不需要每次都写git地址了。实际上他在.git目录里的config添加了一条记录,类似这样的。注意我这里特意加了两个,地址不一样。注意github区分大小写的仓库路径(我的用户名有大小写,如果不完全匹配则认为访问不到了。)。另外注意这里的branch,当你init的时候默认创建的就是master。
[remote &quot;origin&quot;]
url = [email protected]:taoxiaoseng/test.git
fetch = +refs/heads/*:refs/remotes/origin/*
[remote &quot;remote&quot;]
url = [email protected]:TaoXiaoSeng/test.git
fetch = +refs/heads/*:refs/remotes/remote/*git
[branch &quot;master&quot;]
remote = remote
merge = refs/heads/master
git push -u origin master 向远程origin对应的仓库里push本地master分支。这里用-u是类似使用upstream的意思?
</code></pre>
<h2>其他git命令</h2>
<p>git branch 罗列所有分支
git branch branchname 创建分支
git checkout branchname 切换分支
git branch -d branchname 假删除分支,如果是分支是当前正在运行的分支,则删除失败
git branch -D branchname 真删除分支</p>
<p>git ls-remote 查看远程版本库中的分支情况
git push remote :branchname 远程删除分支,注意如果分支是默认分支,则删除失败</p>
<h2>里程碑管理</h2>
<p>里程碑及tag,和分支管理类似,保存在.git/refs/tags路径下,引用可能指向一个提交,但也可能是其他类型。</p>
<ul>
<li>轻量级里程碑:用git tag <tagname> [<commit>]命令创建,引用直接指向一个提交对象<commit></li>
<li>带说明的里程碑:用git tag -a <tagname> [<commit>] 命令创建,并且在创建时需要提供创建里程碑的说明。Git会创建一个tag对象保存里程碑说明、里程碑的指向、创建里程碑的用户等信息,里程碑引用指向该Tag对象。</li>
<li>带签名的里程碑:用git tag -s <tagname> [<commit>] 命令创建。是在带说明的里程碑的基础上引入了PGP签名,保证了所创建的里程碑的完整性和不可拒绝性。</li>
</ul>
</description>
</item>
<item>
<title>涨姿势-NSCache</title>
<link>http://www.codesimplelife.com/2013/05/30/about_nscache.html</link>
<pubDate>2013-05-30 12:00:00 +0800</pubDate>
<description><h1>NSCache</h1>
<p>他的用法很简单,基本等效于NSMutableDictionary,只不过他不保证cache里的对象永远存在,也许在低内存警告时会释放掉部分对象,所以如果你的程序里如果有可以重复创建的对象需要保存时,可以考虑使用这个类。</p>
<p>以下是转载来的。
NSCache 是iOS4以后引入的一个方便的缓存某些object的类。它的使用方法与NSMutableDictionary很相似,但是他会在内存吃紧的时候自动释放某些object。而且不用考虑线程安全的问题。具体的可以参见官方文档的描述。以下是stackoverflow里面一个比较不错的应用例子:</p>
<p>You use it the same way you would use NSMutableDictionary. The difference is that whenNSCache detects excessive memory pressure (i.e. it&rsquo;s caching too many values) it will release some of those values to make room.</p>
<p>If you can recreate those values at runtime (by downloading from the Internet, by doing calculations, whatever) then NSCache may suit your needs. If the data cannot be recreated (e.g. it&rsquo;s user input, it is time-sensitive, etc.) then you should not store it in an NSCache because it will be destroyed there.</p>
<pre><code>You use it the same way you would use NSMutableDictionary. The difference is that whenNSCache detects excessive memory pressure (i.e. it's caching too many values) it will release some of those values to make room.
If you can recreate those values at runtime (by downloading from the Internet, by doing calculations, whatever) then NSCache may suit your needs. If the data cannot be recreated (e.g. it's user input, it is time-sensitive, etc.) then you should not store it in an NSCache because it will be destroyed there.
Example, not taking thread safety into account:
// Your cache should have a lifetime beyond the method or handful of methods
// that use it. For example, you could make it a field of your application
// delegate, or of your view controller, or something like that. Up to you.
NSCache *myCache = ...;
NSAssert(myCache != nil, @&quot;cache object is missing&quot;);
// Try to get the existing object out of the cache, if it's there.
Widget *myWidget = [myCache objectForKey: @&quot;Important Widget&quot;];
if (!myWidget) {
// It's not in the cache yet, or has been removed. We have to
// create it. Presumably, creation is an expensive operation,
// which is why we cache the results. If creation is cheap, we
// probably don't need to bother caching it. That's a design
// decision you'll have to make yourself.
myWidget = [[[Widget alloc] initExpensively] autorelease];
// Put it in the cache. It will stay there as long as the OS
// has room for it. It may be removed at any time, however,
// at which point we'll have to create it again on next use.
[myCache setObject: myWidget forKey: @&quot;Important Widget&quot;];
}
// myWidget should exist now either way. Use it here.
if (myWidget) {
[myWidget runOrWhatever];
}
</code></pre>
</description>
</item>
<item>
<title>CoreData编程指南_CoreData的并发编程_章节翻译</title>
<link>http://www.codesimplelife.com/2013/04/22/Concurrency_with_Core_Data.html</link>
<pubDate>2013-04-22 12:00:00 +0800</pubDate>
<description><h1>Core Data的并发(Concurrency with Core Data)</h1>
<p>存在着一些的场景,我们需要利用后台线程或者队列来执行Core Data的一些操作,因为他们能我们带来一些好处,尤其是你需要在Core Data在做一些比较消耗时间的任务时能保证应用程序的用户交互界面能及时响应。但是如果你并发使用Core Data时,那么你需要关心,考虑到对象图(object Groups)不会进入一个冲突的状态。</p>
<p>如果你选择使用并发编程来操作Core Data,你还需要考虑到应用程序的环境。对于大多数的情况,AppKit和UIKit是线程不安全的;另外,OS X Cocoa bindings和controllers也都是线程不安全的 &ndash; 如果你正在使用这些技术,多线程就会复杂了。</p>
<h2>使用Thread Confinement来支持并发(Use Thread Confinement to Support Concurrency)</h2>
<p>Core Data推荐的并发编程的模式是thread confinement(线程封闭,线程约束?):每个线程必须拥有属于它自身的,完全私有的managed object context。</p>
<p>这里有两种可能的方法来采用这种模式:</p>
<ol>
<li>给每个线程创建一个managed object context,并且共享一个persistent store coordinator</li>
<li>给每隔线程创建一个managed object context和persistent store coordinator。(这种方法提供更加强大的并发,但是同样会更加的复杂(尤其是你需要在不同的context之间进行交互)和消耗更多的内存使用)</li>
</ol>
<p>你必须在它使用的线程中创建相应的managed context。如果你使用的NSOperation,你需要注意它的init方法是在被调用(caller)的线程里执行的。所以你不能够在queue的init方法里创建managed object context,否则你创建的context会关联到这个caller调用者的线程。与之相对应的,你应该在main方法(针对serial queue串行队列)或者start方法(针对concurent queue并发队列)里创建context。</p>
<p>使用thread confinement(线程封闭,线程约束?),你不应该在线程间传递managed objects或者managed object contexts。如果你需要跨线程传递一个managed object,你可以通过以下方法:</p>
<ul>
<li>传递它的object ID(objectID)然后在接收线程里的managed object context里使用objectWithID:或者existingObjectWithID:error:去获取。相对应的managed objects必须已经保存(save)&ndash;你不能够传递一个新创建的还没有保存过的managed object的ID给另外一个context上。</li>
<li>在接收context上通过执行fetch来获取得到需要的managed object。</li>
</ul>
<p>这些方法会在接收context上创建一个本地版本(local version)的managed object。</p>
<p>你能够通过NSFetchRequest的相关方法来更加方便高效的跨平台去操作数据。例如一个配置一个fetch请求来返回一些列的object IDs,当然也可以包括row data(同时更新cache的row数据)&ndash; 这对于你想在一个后台线程里传递一些object IDs到另外的线程里是很有用的。</p>
<p>通常情况下你没有必要去lock managed objects或者managed object contexts。然后,如果你使用单一的被多个context共享的persistent store coordinator,并且想要在它上面执行一些操作(比如你想要添加一个新的store),或者你想要集合多个operation像一个事务一样一起在one context执行。你应该lock锁住persistent store coordinator。</p>
<h2>在其他线程里使用notification通知来跟踪变动</h2>
<p>你在一个context上对一个managed object的改动是不能自动的通知传播到另外一个不同的context上相关联对应的managed object,除非你主动重新查询(refetch)或者<strong><em>re-fault(?)</em></strong>这个object。如果你需要在某个线程上跟踪另外一个线程上的改动,这里有两种方法达到这个目的,两个都是通过notifications通知的方式。为了能更好的解释说明的目的,我们现在假设有两个线程A和B,假设你现在想要将B上的改动传送通知给A。</p>
<p>一般来说,你在线程A上注册managed object context的save通知,NSManagedObjectContextDidSaveNotification。当你接收到这个通知的时候,这个通知里的userinfo(dictionary类型)包含了在B线程里被插入,删除,更新的managed objects的数组信息。由于这些managed objects是被关联到不同的线程上,因此你不应该直接去访问这些对象。与之对应的,你应该将notification作为的一个参数来传递给<a href="http://developer.apple.com/library/ios/#documentation/cocoa/Reference/CoreDataFramework/Classes/NSManagedObjectContext_Class/NSManagedObjectContext.html#//apple_ref/occ/instm/NSManagedObjectContext/mergeChangesFromContextDidSaveNotification:">mergeChangesFromContextDidSaveNotification:</a>方法(这个方法是你在用来发送相关数据给线程A)。通过使用这个方法,context才能够更加安全的合并改动。</p>
<p>注意变动的通知(change notification)是发送到NSManagedObjectContext的<a href="http://developer.apple.com/library/ios/#documentation/cocoa/Reference/CoreDataFramework/Classes/NSManagedObjectContext_Class/NSManagedObjectContext.html#//apple_ref/occ/instm/NSManagedObjectContext/processPendingChanges">processPendingChanges</a>方法。这个方法在主线程是被绑定到应用程序的事件循环里的,这样在主线程上,processPendingChanges就能让用户在context上的每个用户事件后被自动调用。但这不适用于后台进程 &ndash; 什么时候这个方法被调用是决定于平台(platform)和发布版本(release version),所以你不应该依赖与特定的时间。如果第二个context不是在主线程上,你应该自己在适当时刻来调用processPendingChanges方法。(你应该创建你自己概念上的后台线程上工作“循环”(周期?cycle) &ndash; 比如在一系列的操作之后)。</p>
<h2>后台查询来支持UI响应(Fetch in the Background for UI Responsiveness)</h2>
<p>executeFetchRequest:error:方法本质上会根据硬件和工作负载来对它的行为进行一定调整。如果需要,Core Data将会创建额外的私有线程来优化查询性能。你不需要为了优化提高查询的速度的目的而特意的为此创建后台线程。当然这仍然有可能被适当使用,不管怎么说,通过后台线程或queue去查询来尽量避免你的应用程序被用户交互阻塞住。这意味着,如果一个查询很复杂,或者会返回大量的数据,那么你能通过这样的方式来控制,等到这些数据获取到的时候再展示出这些数据。</p>
<p>为了遵从thread confinement(线程封闭?线程约束?)的设计模式,你使用两个managed object context来关联到一个persistent store coordinator。你从一个后台线程的managed object context上来查询数据,然后传递相应的查询得到的object IDs到另外一个线程里。在第二个线程里(一般来说指的是应用程序的主线程,因为你可以得到这些数据后将他们在这个线程里展示出来),你用第二个context来fault这些传递过来的objectIDs对应的那些objects(你可以使用objectWithID:来实例化这些对象)。(这项技术仅仅对于你使用SQLite store的时候才有效,因为binary和xml stores的存储介质里的数据会在被打开的时候就都被读取到内存里了)</p>
<h2>在后台线程做保存操作是容易出错的(Saving in a Background Thread is Error-prone)</h2>
<p>异步的队列和线程都不会阻止应用程序被关闭,也就是说应用程序是不会等异步线程的退出结束之后才关闭。(具体点说就是,所有的NSThread-based线程都是“detached”(独立的?分离的?附加的?)&ndash; 更加详细的可以查看pthread的文档 &ndash; 而一个进程只会等到所有的not-detached线程退出后才退出)如果你在一个后台线程里执行一个保存操作,那么,这个操作可能在它完成保存操作前被杀死。如果你需要在一个后台线程里做保存操作,你必须写一些额外的代码,类似让主线程阻止应用程序在所有的保存操作完成前退出。</p>
<h2>如果你不用Thread Confinement(If You Don’t Use Thread Containment)</h2>
<p>如果你选线不使用程兼容的模式 &ndash; 这就是说,如果你尝试在多线程间传递managed objects或者contexts等类似的操作 &ndash; 你就必须非常消息的去锁数据(locking),这样的后果就是你可能失去了使用多线程带来的好处。你还需要关注一下问题:</p>
<ul>
<li>每次你使用相关联的managed object context去操作或者访问managed objects。Core Data没有提供一个读安全(save)但是修改有危险(dangerous)的解决方案 &ndash; 而是任何操作都是危险(dangerous)的,因为任何操作都会有为了性能考虑的缓存,都可以触发faulting。</li>
<li>managed objects自身就不是线程安全的。如果你想要跨线程去操作一个managed object,你必须锁住(lock)他的context(参考查看<a href="http://developer.apple.com/library/ios/documentation/cocoa/Reference/Foundation/Protocols/NSLocking_Protocol/Reference/Reference.html#//apple_ref/occ/intf/NSLocking">NSLocking</a>)</li>
</ul>
<p>如果你在多线程间共享一个managed object context或者一个persistent store coordinator,你必须确保任何被调用的方法都是以线程安全的定义的。如果需要locking,你应该使用NSLocking方法来锁住managed object context和persistent store coordinator,而不是用你自己定义的mutexes(互斥方法)。这些NSLocking方法还帮助提供了上下文的信息给应用程序的框架 &ndash; 换句话说,就是除了提供了mutex互斥的目的,它(NSLocking)还帮助定义了一系列Core Data操作的上下文的工作环境。</p>
<p>一般来说,你可以使用tryLock或者lock来锁context或者coordinator。如果你这样做了,那么框架就会保证在这之后的操作也都是线程安全。比如说,如果你为每个线程创建了一个context,但是所有的context都是指向同一个persistent store coordinator,那么Core Data会以线程安全的方式来负责访问coordinator(NSManagedObjectContext的<a href="http://developer.apple.com/library/ios/#documentation/cocoa/Reference/CoreDataFramework/Classes/NSManagedObjectContext_Class/NSManagedObjectContext.html#//apple_ref/occ/instm/NSManagedObjectContext/lock">lock</a>和unlock方法支持递归))</p>
<p>如果你使用lock锁住了一个context(或者使用tryLock成功锁住),那么直到你调用unlock去解锁为止,你必须保持对这个context的强引用。如果你不保持强引用的话,那么在一个多线程的环境里,你可能导致死锁。</p>
</description>
</item>
<item>
<title>关于CoreData中删除消息崩溃的教训</title>
<link>http://www.codesimplelife.com/2013/04/10/%E5%85%B3%E4%BA%8Ecoredata%E4%B8%AD%E5%88%A0%E9%99%A4%E6%B6%88%E6%81%AF%E5%B4%A9%E6%BA%83%E7%9A%84%E6%95%99%E8%AE%AD.html</link>
<pubDate>2013-04-10 12:00:00 +0800</pubDate>
<description><h1>关于CoreData中删除记录导致崩溃的教训</h1>
<p>由于之前没有深刻的使用过CoreData(虽然这个技术出来好多年了),只是简单的写了点demo学了下大概如何使用,以及看了一些说明简单了解了整个流程。但是由于项目需要深度使用(至少在我看来CoreData能很好降低耦合),所以工作中还是不得不碰到一些问题。比如这次就碰到。。。</p>
<h2>前因</h2>
<p>因为项目组需要提供SDK给外部使用,所以在做重构,而且做的调整很大很大,由于时间紧张,可能大家都是优先完成代码函数的重构,然后估摸着也很少对重构后的代码功能做测试,只是简单编译了下,然后简单的跑了下程序,看主要聊天功能没问题,就直接check in了。。。引起的后果是代码里可能有一些隐藏的bug。</p>
<p>我因为要修改session和message等很多DB相关的代码重写(虽然对CoreData了解不是那么深,但是根据封装好的接口去重写函数还是没什么大的关系的),所以必然涉及到很多创建会话,删除会话,删除消息,插入消息的功能。重构了部分功能代码后,测试过程中发现删除message,甚至到后面删除session(因为删除session内部涉及到了删除message)都会出现数据库保存操作时错误导致后面崩溃。</p>
<h2>现象</h2>
<p>当有单人聊天时,如果会话中含有消息,那么只删除一条消息也会导致崩溃,清空会话的消息也崩溃。
跟踪代码后,总是死在coredata save的时候出错。导致后面跟踪到错误,主动报错崩溃。</p>
<h2>查bug过程</h2>
<p>多次比对了自己修改前后的代码逻辑,也查看了之前修改前的版本也存在此问题,感觉不应该是我的代码引起的,所以开始往前翻svn log记录,慢慢的找到一个同事merge了一个比较大的功能进来,并且修改比较大,改动了数据库表信息。所以关注的点开始转向数据表,前前后后花了好几个小时,也添加删除了一些代码去测试,最终还是没有找到真正的原因。这个时候觉得还是要回去翻翻官方文档比较靠谱,因为一下子也不知道到底是哪块出问题,也不知道直接找哪块比较靠谱点。晚上回来后开始翻翻core data programming guide,最后找到了一段关于Relationship Delete Rules的。这里摘抄如下</p>
<pre><code>Relationship Delete Rules
A relationship's delete rule specifies what should happen if an attempt is made to delete the source object. Note the phrasing in the previous sentence—&quot;if an attempt is made...&quot;. If a relationship's delete rule is set to Deny, it is possible that the source object will not be deleted. Consider again a department's employees relationship, and the effect that the different delete rules have.
Deny
If there is at least one object at the relationship destination, then the source object cannot be deleted.
For example, if you want to remove a department, you must ensure that all the employees in that department are first transferred elsewhere (or fired!) otherwise the department cannot be deleted.
Nullify
Set the inverse relationship for objects at the destination to null.
For example, if you delete a department, set the department for all the current members to null. This only makes sense if the department relationship for an employee is optional, or if you ensure that you set a new department for each of the employees before the next save operation.
Cascade
Delete the objects at the destination of the relationship.
For example, if you delete a department, fire all the employees in that department at the same time.
No Action
Do nothing to the object at the destination of the relationship.
For example, if you delete a department, leave all the employees as they are, even if they still believe they belong to that department.
</code></pre>
<p>Deny:要求只要destination relationship有一个没有删除,那么source object就不能被删除。</p>
<p>nullify:但我们的表里不是用这个规则,用的是下面的nullify。这里很明确的说明了Set the inverse relationship for objects at the destination to null(我的理解是当你删除一个对象A时,那么他的relationship对象的指向A的relationship值会被设置为null)。</p>
<p>Cascade:级联删除,这个就不多说了</p>
<p>No Action: 删除A,不影响B对A的relationship,B中的relationship原来是什么值就还是什么值。</p>
<p>他给的example非常的明白。一个部门里有好多employee,所以一个部门和employee的关系是一对多的relationship,同时一个employee和部门的关系是一对一的relationship(架设一对一,当然一对多也可能),那么当你要删除一个部门的对象是,那么那个employee就没有部门了,那么<strong><em>他与部门的relationship就要设置为null</em></strong>或者你给他再找个新部门设置进去。如果你不给他找新部门,这就要求employee对部门的relationship的属性是optional,也就是说employee可以对部门的relationship值为null。</p>
<p>理解了这个之后,再看了看数据表中关于message对session的optional关系,确实没有设置为optional.修改后,编译运行,测试通过。到此终于解决了这个问题。</p>
<p>看样子回头还是要好好的看下coredata官方文档,否则一些细节出问题,真的很不好查找。</p>
</description>
</item>
<item>
<title>如何在XCode中以不同的颜色分级显示不同级别的log信息</title>
<link>http://www.codesimplelife.com/2013/04/08/XCode%E5%88%86%E7%BA%A7%E6%98%BE%E7%A4%BAlog%E4%BF%A1%E6%81%AF.html</link>
<pubDate>2013-04-08 12:00:00 +0800</pubDate>
<description><p>转发:
原文出处:http://maxwin.me/blog/?p=124</p>
<h2>安装:</h2>
<p>XLog – Xcode log分级显示插件</p>
<p>下载链接http://maxwin.me/blog/wp-content/uploads/<sup>2012</sup>&frasl;<sub>05</sub>/XLog.zip
提供功能:</p>
<ol>
<li><p>支持log分级,Debug/Info/Warn/Error</p></li>
<li><p>支持自定义log颜色</p></li>
</ol>
<p>安装可查看readme文件,其实就是./install.sh将插件XLog.bundle复制到~/Library/Application\ Support/Developer/Shared/Xcode/Plug-ins/目录下</p>
<h2>使用</h2>
<p>引入工程中的XLogUtil.h和.m文件,也可看下列代码.
调用类似这样的代码XLog_v(@&ldquo;start!&rdquo;);即可。(仔细查看.h文件,里面还提供其他的分级方法,如XLog_i更多的自己查看)。</p>
<pre><code>//
// XLog.h
// TestMXLog
//
// Created by WenDong Zhang on 5/9/12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import &lt;Foundation/Foundation.h&gt;
#define XLOG_ESC_CH @&quot;\033&quot;
#define XLOG_LEVEL_DEBUG @&quot;DEBUG&quot;
#define XLOG_LEVEL_INFO @&quot;INFO&quot;
#define XLOG_LEVEL_WARN @&quot;WARN&quot;
#define XLOG_LEVEL_ERROR @&quot;ERROR&quot;
// colors for log level, change it as your wish
#define XLOG_COLOR_RED XLOG_ESC_CH @&quot;#FF0000&quot;
#define XLOG_COLOR_GREEN XLOG_ESC_CH @&quot;#00FF00&quot;
#define XLOG_COLOR_BROWN XLOG_ESC_CH @&quot;#FFFF00&quot;
// hard code, use 00000m for reset flag
#define XLOG_COLOR_RESET XLOG_ESC_CH @&quot;#00000m&quot;
#if defined (__cplusplus)
extern &quot;C&quot; {
#endif
void _XLog_print(NSString *tag, NSString *colorStr, const char *fileName, const char *funcName, unsigned line, NSString *log);
void _XLog_getFileName(const char *path, char *name);
BOOL _XLog_isEnable();
#if defined (__cplusplus)
}
#endif
#define XLog_log(tag, color, ...) _XLog_print(tag, color, __FILE__, __FUNCTION__, __LINE__, [NSString stringWithFormat:__VA_ARGS__])
#define XLog_d(...) XLog_log(XLOG_LEVEL_DEBUG, XLOG_COLOR_GREEN, __VA_ARGS__)
#define XLog_i(...) XLog_log(XLOG_LEVEL_INFO, XLOG_COLOR_RESET, __VA_ARGS__)
#define XLog_v(...) XLog_log(XLOG_LEVEL_WARN, XLOG_COLOR_BROWN, __VA_ARGS__)
#define XLog_e(...) XLog_log(XLOG_LEVEL_ERROR, XLOG_COLOR_RED, __VA_ARGS__)
</code></pre>
<pre><code>//
// XLog.m
// TestMXLog
//
// Created by WenDong Zhang on 5/9/12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import &quot;XLogUtil.h&quot;
//static int isXLogEnable = -1; // -1: not set, 0: disable, 1: enable
void _XLog_print(NSString *tag, NSString *colorStr, const char *fileName, const char *funcName, unsigned line, NSString *log)
{
const char *tagStr = [tag UTF8String];
// show filename without path
char *file = (char *)malloc(sizeof(char) * strlen(fileName));
_XLog_getFileName(fileName, file);
if (_XLog_isEnable()) {
printf(&quot;%s&quot;, [colorStr UTF8String]); // log color
printf(&quot;%s[%s]&quot;, [XLOG_ESC_CH UTF8String], tagStr); // start tag
}
printf(&quot;%s &quot;, [[[NSDate date] description] UTF8String]); // time
printf(&quot;%s %s:l%u) &quot;, file, funcName, line); // fileName
printf(&quot;%s&quot;, [log UTF8String]); // log
if (_XLog_isEnable()) {
printf(&quot;%s[/%s]&quot;, [XLOG_ESC_CH UTF8String], tagStr); // end tag
printf(&quot;%s&quot;, [XLOG_COLOR_RESET UTF8String]); // reset color
}
printf(&quot;\n&quot;);
free(file);
}
BOOL _XLog_isEnable()
{
return YES;
// if (isXLogEnable == -1) { // init
// char *xlogEnv = getenv(&quot;XLOG_FLAG&quot;);
// if (xlogEnv &amp;&amp; !strcmp(xlogEnv, &quot;YES&quot;)) {
// isXLogEnable = 1;
// } else {
// isXLogEnable = 0;
// }
// }
//
// if (isXLogEnable == 0) {
// return NO;
// }
// return YES;
}
void _XLog_getFileName(const char *path, char *name)
{
int l = strlen(path);
while (l-- &gt;= 0 &amp;&amp; path[l] != '/') {}
strcpy(name, path + (l &gt;= 0 ? l + 1 : 0));
}
</code></pre>
</description>
</item>
<item>
<title>关于如何使用appledoc生成XCode能读取的文档的完整版</title>
<link>http://www.codesimplelife.com/2013/04/06/create-docset-used-by-xcode-the-tool-appledoc[V1.0].html</link>
<pubDate>2013-04-06 12:00:00 +0800</pubDate>
<description><p>#关于生成文档的事项</p>
<p>可以借助appledoc去生成文档,并且可以通过xcode自带的文档查看器去跳转查看。</p>
<p>##安装步骤:</p>
<ol>
<li><p>安装appledoc,简单的方法就是</p>
<ol>
<li>)下载源码 git clone git://github.com/tomaz/appledoc.git</li>
<li><p>)安装appledoc,并且安装到/usr/bin等目录下,以便直接appledoc直接运行。</p>
<pre><code>sudo sh install-appledoc.sh -b /usr/bin -t ~/Library/Application\ Support/appledoc
</code></pre></li>
</ol></li>
</ol>
<p>##生成文档
通过下面的命令去生成文档
<code>
appledoc --project-name WXMessengerDoc --project-company &quot;alibaba-inc&quot; --company-id com.alibaba-inc.WXMessenger [--output ./helloworld] [includeFilePathOrFile] [--ignore excludeFilePathOrFile] .
</code></p>
<p>这个命令只扫描当前目录下所有文件(另外包含includeFilePathOrFile和去除excludeFilePathOrFile) 并生成到某个目录下(默认是当前目录)</p>
<p>该命令结束后会添加到系统的文档目录下</p>
<p>If the path is directory, it&rsquo;s recursively parsed for all source files. If path is file, it&rsquo;s parsed as source file</p>
<p>#####更多关于命令的说明是这样的:
<a href="http://gentlebytes.com/appledoc-docs-examples-basic/">http://gentlebytes.com/appledoc-docs-examples-basic/</a></p>
<p>2:写代码的时候注意添加注释,我测试下生成的规则后对注释的理解。</p>
<pre><code>头文件中注释使用
/*
我随便写的,反正这里不会被抽取作为文档的一部分,这里随便写点帮助别人如何使用该API的注释就好了,反正安装了docset后,你都可以直接option+左键点出文档相关了,这里说太多参数,函数作用,返回值的东西也没什么用,而且还占地方
*/
- (id)initWithAvatar:(UIImage *)avatarImage;
.m或者.mm文件里添加注释如果下,该部分会被appledoc提取生成文档
/** 你可以从这开始,不过也没什么必要,反正这里和下面那行一样的功能,这行不写可能更加好看点
这里很特别,这里是添加Discussion的地方,这样我们可以看到这个函数有什么问题啊,注意事项啊,反正他放到了文档里的Discussion里,你自己看着办
@brief 麻烦告诉我这个函数干什么用的
@param avatarImage 介绍下avatarImage是什么东西吧。
@return 这里是返回结果的描述,什么情况返回什么样的结果啊?
@exception name description
*/
- (id)initWithAvatar:(UIImage *)avatarImage{
。。。
}
</code></pre>
<p>上面的方式是头文件和.m文件分开注释
但是对于protocol来说,没有实现文件了,所以还是需要直接添加注释的,这里测试的代码如下</p>
<pre><code>/**
这个协议的描述
*/
@protocol PHAvatarViewDelegate &lt;NSObject&gt;
@optional
/**
@brief 这个didShowAvatar delegate的作用
*/
-(void)didShowAvatar:(NSString*)avatarName animated:(BOOL)animated;
@required
/**
@brief willShowAvatar delegate的作用
*/
-(void)willShowAvatar:(NSString*)avatarName animated:(BOOL)animated;
@end
</code></pre>
<p>通过上面的方式就能生成如下的效果了(protocol类似,这里不贴了),详细的注释和文档效果图见最下面</p>
<p>##说明</p>
<p>这里有几点说明下:(下面说的文档注释指会被提取出生成文档的,普通注释不会被提取)</p>
<p>1:如果文档注释写到.m等文件里也是可行的,这样可以减少头文件的注释大小,头文件只添加一部分普通注释就好了。但是protocol之类的就不得不在声明处添加文档注释了。</p>
<p>2:如果在.m .mm等文件里添加文档注释,那么生成的文档可能有个比较怪异,比如</p>
<pre><code> Declared In
PHAvatarView.m
</code></pre>
<p>所以这个是写在.m文件里的弊端。</p>
<p>3:要生成文档的添加注释的规则方法如下:</p>
<pre><code>1)多行注释是从下面这种样式/**注释*/注释里抓的,注意前面需要是/**(实际上/*!也支持),对于/*注释*/这样的注释是不会被提取的。单行注释通过///去抓取,//是不会抓取的
/**
注释
*/
2)简介需要标签@brief,而discussion一定要写在最前面。
3)对于同一个方法不能出现两个两个地方的文档注释,否则.h文件里的注释会覆盖了.m文件
4)扫描生成注释的时候可以忽略某些文件夹或者某些类型文件。
5)标签的格式一般为类似@param &lt;name&gt; &lt;description&gt;
6)可以通过///@name spcename去添加一段需要在文档中标示一组方法的作用,具体看下面的文档效果。
</code></pre>
<h2>集成安装到XCode中直接安装</h2>
<p>还可以通过在xcode工程里添加一个target来运行该target生成文档
创建一个target,添加一个run script
内容类似下面
<code>
/usr/bin/appledoc \
--project-name &quot;Messenger&quot; \
--project-company &quot;Gentle Bytes&quot; \
--company-id com.alibaba-inc.WXMessenger \
--ignore UnitTest \
.
exit 0
</code>
说明:这里之前安装appledoc是安装到了/usr/bin下,运行该target后自动生成相关的文档并安装到系统文档目录(/Users/youUserName/Library/Developer/Shared/Documentation/DocSets)下。
####参见:
<a href="http://www.infinite-loop.dk/wp/wp-content/uploads/2011/06/Build-Documentation-Script.png">http://www.infinite-loop.dk/wp/wp-content/uploads/2011/06/Build-Documentation-Script.png</a></p>
<p>#附:
##参考资料:
###更多参考
<a href="http://gentlebytes.com/appledoc">http://gentlebytes.com/appledoc</a></p>
<p>github地址: <a href="https://github.com/tomaz/appledoc">https://github.com/tomaz/appledoc</a></p>
<p><a href="http://www.infinite-loop.dk/blog/2011/06/providing-custom-documentation-in-xcode/">http://www.infinite-loop.dk/blog/2011/06/providing-custom-documentation-in-xcode/</a></p>
<p>下面是官方的appledoc源码和生成的文档,可以参考比对。
<a href="https://github.com/tomaz/appledoc/blob/master/">https://github.com/tomaz/appledoc/blob/master/</a>
<a href="http://gentlebytes.com/media/appledoc/examples/v2/latest/Classes/GBGenerator.html#tasks">http://gentlebytes.com/media/appledoc/examples/v2/latest/Classes/GBGenerator.html#tasks</a></p>
<p>###附头文件和实现文件的注释和最后的文档效果:</p>
<p>###&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;-头文件&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;</p>
<pre><code>/**
这个类的作用
*/
@interface PHAvatarView : UIView
{
}
///-----------------------------------------------------------------
/// @name Initialization
///-----------------------------------------------------------------
/*
* 我随便写的,反正这里不会被抽取作为文档的一部分,这里随便写点帮助别人如何使用该API的注释就好了,反正安装了docset后,你都可以直接option+左键点出文档相关了,这里说太多参数,函数作用,返回值的东西也没什么用,而且还占地方
*
*
*/
//我随便写,这里不会被提取
- (id)initWithAvatar:(UIImage *)avatarImage;
@end
/**
这个协议的描述
*/
@protocol PHAvatarViewDelegate &lt;NSObject&gt;
@optional
/**
@brief 这个didShowAvatar delegate的作用
*/
-(void)didShowAvatar:(NSString*)avatarName animated:(BOOL)animated;
@required
/**
@brief willShowAvatar delegate的作用
*/
-(void)willShowAvatar:(NSString*)avatarName animated:(BOOL)animated;
@end
</code></pre>
<p>###&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash; 实现文件 &mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;</p>
<pre><code>@implementation PHAvatarView
/** 你可以从这开始,不过也没什么必要,反正这里和下面那行一样的功能,这行不写可能更加好看点
这里很特别,这里是添加Discussion的地方,这样我们可以看到这个函数有什么问题啊,注意事项啊,反正他放到了文档里的Discussion里,你自己看着办
@brief 麻烦告诉我这个函数干什么用的
@param avatarImage 介绍下avatarImage是什么东西吧
@return 这里是返回结果的描述,什么情况返回什么样的结果啊?
@exception aaa 异常提示
*/
- (id)initWithAvatar:(UIImage *)avatarImage{
if (self = [super initWithFrame:CGRectMake(0, 0, 50, 50)]) {
UIImageView *avatarImageView = [[[UIImageView alloc] initWithImage:avatarImage] autorelease];
[avatarImageView sizeToFit];
avatarImageView.layer.cornerRadius = avatarImage.size.width/2.0;
avatarImageView.clipsToBounds = YES;
avatarImageView.frame = CGRectMake(2, 0, avatarImageView.frame.size.width, avatarImageView.frame.size.height);
[self addSubview:avatarImageView];
UIImageView *maskView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:@&quot;invites-user-avatar-edge.png&quot;]] autorelease];
[maskView sizeToFit];
[self addSubview:maskView];
}
return self;
}
@end
</code></pre>
<p>###&mdash;&mdash;&mdash;&mdash;&mdash;&ndash; 文档效果 &mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;
<img src="http://yunpan.alibaba-inc.com/share/scan.do?info=8H2tK3INh&pInfo=8H2tK3INh&app_name="></p>
</description>
</item>
<item>
<title>关于如何使用appledoc生成XCode能读取的文档的命令</title>
<link>http://www.codesimplelife.com/2013/04/06/create-docset-used-by-xcode-the-tool-appledoc.html</link>
<pubDate>2013-04-06 12:00:00 +0800</pubDate>
<description><p>messenger文档生成命令</p>
<p>appledoc &ndash;project-name WXMessengerDoc &ndash;project-company &ldquo;alibaba-inc&rdquo; &ndash;company-id com.alibaba-inc.WXMessenger &ndash;output ./WXMessengerDoc &ndash;ignore ./Three20 &ndash;ignore ./ASIHttpRequest &ndash;ignore ./script &ndash;ignore ./tools &ndash;ignore ./docs &ndash;ignore ./Messenger/JSON &ndash;ignore ./Messenger/JSONKit.h &ndash;ignore ./Messenger/JSONKit.m &ndash;ignore ./Messenger/PopverView &ndash;ignore ./Messenger/RichText &ndash;ignore /Messenger/SDWebData &ndash;ignore ./Messenger/SFHFKeychainUtils &ndash;ignore ./Messenger/TouchJSON &ndash;ignore ./Messenger/weak_ref &ndash;ignore ./Messenger/zh-Hans.lproj &ndash;ignore ./Messenger/Frameworks .</p>
</description>
</item>
<item>
<title>你的第一篇博客</title>
<link>http://www.codesimplelife.com/2013/first-page.html</link>
<pubDate>2012-04-05 12:00:00 +0800</pubDate>
<description><h1>感谢你使用Gor编写博客</h1>
<h2>本文位于 posts/first-blog.md , 你可以任何删掉,修改这个文件</h2>
<p>文件开头是当前文章的元数据</p>
<ol>
<li>date为自动生成, 当然,你可以修改,这是你的自由</li>
<li>permalink 可以是固定地址,也可以由gor为你自动生成</li>
<li>categories 就是分类, 可以多个</li>
<li>tags 同理,多个标签也是很常见的</li>
</ol>
<p>请确保文件使用UTF8 without BOM编码</p>
<h2>你可以通过执行下面的语句来新建一篇博客:</h2>
<pre><code>gor post 文章标题
</code></pre>
<h2>编译你的博客,并预览之</h2>
<pre><code>gor compile #编译
gor http
</code></pre>
<p>然后打开你的浏览器,访问 <a href="http://127.0.0.1:8080">http://127.0.0.1:8080</a> 来预览</p>
<h2>你将使用Markdown来编写博客</h2>
<p><a href="http://wowubuntu.com/markdown/">Markdown 语法中文版</a> 能让你快速入门其语法</p>
<p>相信<a href="http://markdownpad.com">MarkdownPad</a>或<a href="http://code.google.com/p/liteide/">liteide</a>会是你的编写博客的好帮手</p>
<h2>如果你打算部署到github的pages上</h2>
<ol>
<li>申请github帐户</li>
<li>新建一个库 username.github.com 即你的用户名命名的地址</li>
<li>将compiled目录,作为根路径,提交上去github.com上</li>
<li>稍等几分钟, 你即可通过 <a href="http://username.github.com">http://username.github.com</a> 访问到</li>
</ol>
<h2>附上git教程 <a href="http://gitbook.liuhui998.com/">GitBook中文版</a></h2>
<p>一般来说,你只需要几个简单的git命令就足以应付大部分需求(仅示例)</p>
<pre><code>git clone git://github.com/wendal/wendal.net.git
git add -A
git commit -m &quot;...&quot;
git pull
git push
</code></pre>
<h2>用gor编写博客将会是一件很开心的事,如果有任何意见或建议,欢迎到 <a href="http://github.com/wendal/gor">gor的官网</a> 提交issue</h2>
<h1>祝你使用愉快</h1>
</description>
</item>
<item>
<title>FTP协议完全详解</title>
<link>http://www.codesimplelife.com/2013/04/22/detail_analyse_about_ftp.html</link>
<pubDate>2010-02-24 12:00:00 +0800</pubDate>
<description><h1>FTP协议完全详解</h1>
<pre><code>http://blog.csdn.net/yxyhack/archive/2007/10/15/1826256.aspx
FTP协议完全详解
1. 介绍
FTP的目标是提高文件的共享性,提供非直接使用远程计算机,使存储介质对用户透明和可靠高效地传送数据。虽然我们也可以手工使用它,但是它的主要作用是供程序使用的。在阅读本文之前最好能够阅读TCP协议标准规范和Telnet协议标准规范。
2. 概览
在本节中我们将讨论一些表面上的问题,有些名词的定义请参阅TCP和Telnet参考文献。我们先介绍一下(1)字节大小,在FTP中字节大小有两个:逻辑字节大小和用于传输的字节大小。后者通常是8位,而前者可不一定是多少了。传输字节不必等于逻辑字节大小,也不必对数据结构进行解释。(2)控制连接是建立在USER-PIT和SERVER-PI之间用于交换命令与应答的通信链路。(3)数据连接是传输数据的全双工连接。传输数据可以发生在服务器DTP和用户DTP之间也可以发生在两个服务器DTP之间。(4)DTP:数据传输过程(DTP)建立和管理数据连接,DTP可以是主动的也可以是被动的。(5)EOR代表记录尾。(6)NTV代表网络虚拟终端,它的定义与在Telnet协议中的定义一致。(7)NVFS代表网络虚拟文件系统。(8)FTP可以传输非连续的文件,这些文件的一部分称为页。(9)PI代表协议解释器。(10)服务器DTP代表一种传输过程,它通常处于“主动”状态,它和侦听端口建立数据连接,它还可以为传输和存储设置参数,并根据PI的指令传输数据。当然,DTP也可以转入“被动”状态。(11)服务器FTP进程,它是和用户FTP进程一起工作的,它由PI和DTP组成。至于用户FTP进程则是由PI,DTP和用户接口组成的。下图是FTP服务示意图:
注意:数据连接是双向的,它不用整个时间都存在。上图中用户PI开始控制连接,控制连接与Telnet协议很象。在开始阶段,标准FTP命令由用户PI产生并通过控制连接传送到服务器进程。服务器PI向用户PI返回标准应答。FTP命令指定数据连接参数和文件系统操作。用户DTP在特定数据端口侦听,服务器开始数据连接并以指定的参数开始数据传输。数据端口不必在开始FTP命令的机器上,但用户或用户FTP进程必须确定它在指定的数据端口上侦听。这个数据连接是全双工的。
在另外一种情况下,用户或许希望在两个主机间传送文件,不是两个本地主机。用户在两台主机间建立控制连接,然后规划数据连接。用这种方式,控制信息由用户PI获得,但是数据在服务器DTP之间传送。下面就是一个例子:
协议要求数据传输在处理时打开控制连接。在完成FTP服务后由用户中止控制连接,而服务器具体操作。如果在未接收命令时关闭了控制连接,服务器也会关闭数据传输。FTP和Telnet很有联系,FTP使用Telnet协议进行控制连接,可有两种方法达到目的:用户PI或服务器PI可以在自己的过程中实现Telnet协议的功能;第二种方法是利用系统中现有的Telnet模块。实现上,FTP对Telnet协议的依赖也不多,即使重新实现,代码量也不大。
3. 数据传输功能
数据连接只传输数据,控制连接传送命令和响应。几个命令是关于在主机间传输数据的,数据传输基本上独立于物理结构的,但是如果在压缩传输模式下流式传输与文件结构有关,文件的属性与表示类型有关。
3.1. 数据表示与保存
数据是在主机间的存储设置间传送的。因为两个系统的数据存储方式不同,因此需要对它进行转换,在传送文本时会有对ASCII表示的问题,在进行二进制传送的时候,会有不同系统对字节长度规定不同的问题,有的系统是7位,有的系统可能是32位,这也需要进行转换。需要提供数据表示与传输模型函数,但是FTP提供这方面的功能不多,超过FTP提供功能的那一部分要用户自己实现。
3.1.1. 数据类型
数据表示是由用户指定的表示类型,它可以是隐含的,也可以是用户指定的。请一定注意:逻辑字节长度与物理字节长度是不同的。
3.1.1.1. ASCII类型
这是所有FTP必须实现的默认类型,用于传送文本文件,当在主机间使用EBCDIC传送时更方便,则不使用ASCII类型。发送方将内部表示转换为NVT-ASCII格式,接收方则进行相反的过程接收数据。根据NVT标准,要在行结束处使用&lt;CRLF&gt;序列。NVT-ASCII是8位的。ASCII和EBCDIC的格式参数在下面讨论。
3.1.1.2. EBCDIC类型
它是作为ASCII的另一种方法在主机间传送数据的数据类型。EBCDIC和ASCII很象,仅在类型的功能描述上有一些差别。行结束符使用很少。
3.1.1.3. 图象类型
在此类型下传送的数据被看作连续的位,发送方将数据打包到8位传输字节中传送。因为结构的需要要对传送数据进行填充,填充字节全部为0,填充必须在文件结构时使用,而且要标记出以便接收方过滤掉。它用于传送二进制数据和有效地传送和存储文件,因此所有FTP也必须实现。
3.1.1.4. 本地类型
也可以以十进制指定逻辑字节大小。如果物理字节大小和逻辑字节大小不同,直接将物理数据打包为逻辑字节,不用什么填充。接收方根据逻辑字节大小进行和本机的存储特点进行转换。传输必须是可重复的,也就是说,相同的文件相同的参数,那内容必须是一样的。
3.1.1.5. 格式控制
ASCII和EBCDIC有一个可选参数,它说明文件垂直格式控制,下面的数据表示类型在FTP中有定义。字符文件可能有三种用途,打印,存储或留待以后处理。如果是用于打印,那主机必须知道垂直格式控制的表示,如果存储或等以后处理,也需要保留文件格式。如果在远程主机上处理完后传输回本地主机,要保证远程主机处理时没有麻烦。这都需要在ASCII和EBCDIC格式上加入新的参数。
3.1.1.5.1. NON PRINT
未指定第二个参数是它是默认值。它必须为所有FTP接受。如果传输的文件是用于打印的,则使用边界和间隔的默认值。通常它不用于打印目的,而用于保存文件或执行文件。
3.1.1.5.2. TELNET格式控制
文件包括ASCII/EBCDIC垂直格式控制,这些控制字符可以使打印正常进行。
3.1.1.5.3. CARRIAGE CONTROL (ASA)
文件包含ASA (FORTRAN)垂直格式控制字符。在以ASA标准形成的行中,第一个字符不打印,它用于决定打印前的走纸量。下面是定义了的ASA字符:
blank: 向下移动1行;0:向下移动2行;1:移动至下一页;+:不移动
打印机必须能够决定结构体的结束。如果文件本身有记录结构就没有问题,如果没有,&lt;CRLF&gt;用于区别打印行,但这些格式标记已经由ASA控制字符使用了。
3.1.2. 数据结构
除了有不同的数据类型外,FTP还允许有不同的文件结构,下面是三种文件结构:文件式结构:文件中没有内部结构,文件被看作是二进制流;记录结构:文件是由一系列记录组成的;页结构:文件是由不同的索引页组成的。
如果未使用STRU命令,文件结构是默认值。文件的结构会影响传输模型,存储和数据表示。文件本来的属性和保存它的主机有关,不同的机器会以自己的方式保存文件。在不同主机间传送文件时必须使主机能够识别相互的表示。有些主机上的文件是面向字节的,有些是面向记录的,在传送时就会出现问题。那就要在接收方进行内部转换。在进行转换的时候,需要区别记录的边界,在ASCII中使用&lt;CRLF&gt;,在EBCDIC中使用&lt;NL&gt;作为分隔符。采用这种实现方法的必须保证转换是可逆的。
3.1.2.1. 文件结构
如果未使用STRU命令,文件结构是默认值。文件结构中没有默认值,文件被看作是连续的字节串。
3.1.2.2. 记录结构
对于文本文件,记录结构必须是所有FTP实现必须有的。记录结构文件是由连续的记录构成的。
3.1.2.3. 页结构
文件是非连续时使用页结构。这种文件称为随机访问文件。这些文件中有时会的和文件整体或部分相关的信息出现。在FTP中,文件的一个部分称为页。为了提供不同的页大小和相关信息,每页都带页头发送。页头中有如下域:
头长度
包括此字节的页头长度,单位为字节,最小长度为4
页索引
指出此部分在原文件中的位置,它和传输编号不是一回事
数据长度
页数据中的逻辑字节数,最小值为0
页类型
页的类型有以下几种:0=未页,指示传输结构,包头必须为4,数据长度必须为0;1=通常页,没有控制信息的通常页文件的普通类型;2=描述子页,用于传送整体文件的描述信息;3=访问控制页,包括页级访问控制信息的页文件头域,包头长度必须为5
可选域
提供每页的控制信息
描述子 8位
字节计数 16位
描述子代码由在描述子字节中的位标记说明,下面是已经指定的四种代码及其意义:
代码