-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
1600 lines (587 loc) · 77.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 class="theme-next muse use-motion" lang="zh-Hans">
<head>
<meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
<meta name="theme-color" content="#222">
<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />
<link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />
<link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />
<link href="/css/main.css?v=5.1.4" rel="stylesheet" type="text/css" />
<link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png?v=5.1.4">
<link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png?v=5.1.4">
<link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png?v=5.1.4">
<link rel="mask-icon" href="/images/logo.svg?v=5.1.4" color="#222">
<meta name="keywords" content="Hexo, NexT" />
<meta name="description" content="追求优雅的代码风格">
<meta name="keywords" content="iOS开发技术博客">
<meta property="og:type" content="website">
<meta property="og:title" content="CoderMSY's Blog">
<meta property="og:url" content="http://yoursite.com/index.html">
<meta property="og:site_name" content="CoderMSY's Blog">
<meta property="og:description" content="追求优雅的代码风格">
<meta property="og:locale" content="zh-Hans">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="CoderMSY's Blog">
<meta name="twitter:description" content="追求优雅的代码风格">
<script type="text/javascript" id="hexo.configurations">
var NexT = window.NexT || {};
var CONFIG = {
root: '/',
scheme: 'Muse',
version: '5.1.4',
sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false},
fancybox: true,
tabs: true,
motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
duoshuo: {
userId: '0',
author: '博主'
},
algolia: {
applicationID: '',
apiKey: '',
indexName: '',
hits: {"per_page":10},
labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
}
};
</script>
<link rel="canonical" href="http://yoursite.com/"/>
<title>CoderMSY's Blog</title>
</head>
<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">
<div class="container sidebar-position-left
page-home">
<div class="headband"></div>
<header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
<div class="header-inner"><div class="site-brand-wrapper">
<div class="site-meta ">
<div class="custom-logo-site-title">
<a href="/" class="brand" rel="start">
<span class="logo-line-before"><i></i></span>
<span class="site-title">CoderMSY's Blog</span>
<span class="logo-line-after"><i></i></span>
</a>
</div>
<p class="site-subtitle">https://github.com/CoderMSY</p>
</div>
<div class="site-nav-toggle">
<button>
<span class="btn-bar"></span>
<span class="btn-bar"></span>
<span class="btn-bar"></span>
</button>
</div>
</div>
<nav class="site-nav">
<ul id="menu" class="menu">
<li class="menu-item menu-item-home">
<a href="/" rel="section">
<i class="menu-item-icon fa fa-fw fa-home"></i> <br />
首页
</a>
</li>
<li class="menu-item menu-item-archives">
<a href="/archives/" rel="section">
<i class="menu-item-icon fa fa-fw fa-archive"></i> <br />
归档
</a>
</li>
</ul>
</nav>
</div>
</header>
<main id="main" class="main">
<div class="main-inner">
<div class="content-wrap">
<div id="content" class="content">
<section id="posts" class="posts-expand">
<article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
<div class="post-block">
<link itemprop="mainEntityOfPage" href="http://yoursite.com/2018/04/13/iOS招聘/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="name" content="Tammy Miao">
<meta itemprop="description" content="">
<meta itemprop="image" content="/images/avatar.gif">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="CoderMSY's Blog">
</span>
<header class="post-header">
<h1 class="post-title" itemprop="name headline">
<a class="post-title-link" href="/2018/04/13/iOS招聘/" itemprop="url">快到“碗”里来</a></h1>
<div class="post-meta">
<span class="post-time">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建于" itemprop="dateCreated datePublished" datetime="2018-04-13T20:33:36+08:00">
2018-04-13
</time>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h3 id="iOS开发工程师-【招聘】"><a href="#iOS开发工程师-【招聘】" class="headerlink" title="iOS开发工程师 【招聘】"></a>iOS开发工程师 【招聘】</h3><p>薪资 9K-13k /杭州/经验1-3年/本科及以上/全职</p>
<h4 id="职位诱惑"><a href="#职位诱惑" class="headerlink" title="职位诱惑"></a>职位诱惑</h4><p>五险一金、双休、稳健上升型平台、妹子不多可以安心工作、地铁口</p>
<h4 id="职位描述"><a href="#职位描述" class="headerlink" title="职位描述"></a>职位描述</h4><p><strong>【岗位职责】</strong></p>
<ol>
<li>负责iOS客户端模块设计、编码和测试工作</li>
<li>参与产品需求讨论</li>
<li>负责公司现有项目框架的维护和拓展</li>
<li>有良好的编码习惯和规范</li>
<li>能独立承担项目和攻克重难点</li>
</ol>
<p><strong>【任职要求】</strong></p>
<ol>
<li>熟悉Objective-C编码;</li>
<li>1~3年iOS开发经验;</li>
<li>熟悉iOS多线程,数据库相关知识</li>
<li>熟练使用git版本控制及Cocoapods的私有库制作</li>
<li>有良好的架构思想,及项目模块化、组件化经验</li>
<li>熟悉MVVM架构模式</li>
</ol>
<p><strong>【加分项】</strong></p>
<ol>
<li>有自己的开源项目,简历上附上github地址</li>
<li>熟悉Socket,有IM开发经验</li>
<li>熟悉Runtime,Runloop</li>
</ol>
<h4 id="工作地址"><a href="#工作地址" class="headerlink" title="工作地址"></a>工作地址</h4><p>杭州下城区文晖路 现代置业大厦东楼506 (打铁关地铁口)</p>
<p>简历上最好附上自己过往开发的应用下载地址,我们一定会仔细阅读</p>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</div>
</article>
<article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
<div class="post-block">
<link itemprop="mainEntityOfPage" href="http://yoursite.com/2018/04/12/iOS简历/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="name" content="Tammy Miao">
<meta itemprop="description" content="">
<meta itemprop="image" content="/images/avatar.gif">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="CoderMSY's Blog">
</span>
<header class="post-header">
<h1 class="post-title" itemprop="name headline">
<a class="post-title-link" href="/2018/04/12/iOS简历/" itemprop="url">iOS简历</a></h1>
<div class="post-meta">
<span class="post-time">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建于" itemprop="dateCreated datePublished" datetime="2018-04-12T13:02:53+08:00">
2018-04-12
</time>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<p>@(iOS简历)[Objective-C, WebSocket, Cocoapods]</p>
<h4 id="联系方式"><a href="#联系方式" class="headerlink" title="联系方式"></a>联系方式</h4><ul>
<li><strong>Email:</strong><a href="mailto:codermsy@gmail.com" target="_blank" rel="noopener">codermsy@gmail.com</a></li>
<li><strong>QQ/微信号:</strong>2278046701<h4 id="个人信息"><a href="#个人信息" class="headerlink" title="个人信息"></a>个人信息</h4></li>
<li>缪时煜 | 男 | 1993</li>
<li>本科 浙江万里学院 计算机系 信息工程(学士)</li>
<li>工作年限:3年</li>
<li>技术博客:<a href="https://codermsy.github.io/" target="_blank" rel="noopener">https://codermsy.github.io/</a></li>
<li>Github:<a href="https://github.com/CoderMSY" target="_blank" rel="noopener">https://github.com/CoderMSY</a> </li>
<li>期望职位:iOS 开发工程师</li>
<li>期望薪资:15k,特别喜欢的公司可例外</li>
<li>期望城市:杭州<h4 id="工作经历"><a href="#工作经历" class="headerlink" title="工作经历"></a>工作经历</h4><h5 id="浙江鹭栖科技有限公司-(2017年5月-至今)"><a href="#浙江鹭栖科技有限公司-(2017年5月-至今)" class="headerlink" title="浙江鹭栖科技有限公司 (2017年5月~至今)"></a>浙江鹭栖科技有限公司 (2017年5月~至今)</h5><strong>审管平台app</strong> <code>企业应用</code><br>我负责整个项目的开发。利用cocopods对基础组件(常用的类别、宏、常量,以及各种处理工具)进行<strong>私有库封装</strong>,利用MVVM搭建项目框架,重写了申请模块、<strong>IM模块</strong>,基于工厂模式的数据存储。利用更多的业余时间去调研IM,以及跟主管反馈原来的IM设计上不合理,及原IM模块的耦合性,增大了修改的难度。公司领导也认可了我的做法。项目遇到最大的问题是重写老项目时,无法与新后台沟通相关接口数据,及IM消息收发逻辑,新同事都不清楚具体实现逻辑,尴尬,只能阅读原项目与相关技术文档,前期遇到相对复杂的消息指令,及webSocket连接问题,也是绕了一段弯路。至于其它新技术点,只要能找到方案都是不是难点。</li>
</ul>
<p><strong>下城政协</strong> <code>企业应用</code><br>负责整个项目开发。在规定时间内提前交接任务需求。利用<strong>MVVM</strong>搭建项目框架,使用主流第三方库,项目整体比较简单容易,封装了一个自定义日历组件,js和OC之间的交互,以及利用<code>Runtime</code>的特性,利用<code>Method Swizzling</code>进行用户习惯埋点统计,第三方平台数据分析及bug统计,学会了<strong>利用堆栈地址定位崩溃位置</strong>,并及时处理相关bug。</p>
<p><strong>下城OA</strong> <code>企业应用</code><br>负责项目维护、版本迭代和新需求开发。该项目是一个迭代了四年的项目,主体功能包括IM、公文模块、申请模块等,项目结构也是MVC,前期的不规范导致项目结构混乱,代码的冗余及重复性代码较多,耦合性比较高,之前也是屡次和领导提出重写项目,碍于时间和人数,以及重写后项目测试力度等多方面因数,重写迟迟未进行。</p>
<h5 id="浙江菲遇互联网科技有限公司-(2016年9月-2017年5月)"><a href="#浙江菲遇互联网科技有限公司-(2016年9月-2017年5月)" class="headerlink" title="浙江菲遇互联网科技有限公司 (2016年9月-2017年5月)"></a>浙江菲遇互联网科技有限公司 (2016年9月-2017年5月)</h5><p><a href="https://itunes.apple.com/cn/app/mm131-%E9%AB%98%E6%B8%85%E6%89%8B%E6%9C%BA%E5%A3%81%E7%BA%B8/id1330292979?mt=8" target="_blank" rel="noopener"><strong>mm131客户端</strong></a> <code>App Store</code><br>负责第一版本项目<strong>框架搭建</strong>,及主要界面开发,数据库缓存,权限管理,审核机制判断,广告接口设计。<br><strong>狼杀(在线语音狼人杀)</strong><code>手游(下架)</code><br>负责登录注册模块,个人中心模块,商城模块等游戏房间以外的全部UI及逻辑部分。项目主要是基于cocos2d-x,项目语言C++、OC。项目启动于16年11月,结束于17年4月,当时结束项目的原因是后续推广及后期开发人员投入较高。当前开发人员主要是我和主管,两天的学习C++和cocos2d-x框架我便上手,到后来熟练开发页面及动画消息。项目中能值得一提,开发出类似欢乐斗地主互送表情功能,应用场景为房间内互赠礼物。</p>
<p><strong>菲遇–社交</strong><br>参与菲遇客户端二期重构。学会使用<strong>gitLab</strong>参与团队开发,利用git<strong>主线分支</strong>机制、审核机制,提供工作效率,降低代码重复率。主要开发了个人中心模块,登录模块,动态圈列表交互,及动态圈列表性能优化,多张图片分享图片格式化合成处理(效果参考in-我的生活in记)。学会利用<strong>cocoapods</strong>封装<strong>私有库</strong>,<strong>业务模块化</strong>。</p>
<h5 id="杭州亿川科技有限公司(2015年9月-2016年9月)"><a href="#杭州亿川科技有限公司(2015年9月-2016年9月)" class="headerlink" title="杭州亿川科技有限公司(2015年9月-2016年9月)"></a>杭州亿川科技有限公司(2015年9月-2016年9月)</h5><p><a href="ttps://itunes.apple.com/cn/app/%E5%8D%97%E6%98%8C%E5%81%A5%E5%BA%B7/id1325408858?mt=8" target="_blank" rel="noopener"><strong>南昌健康</strong></a> <code>外包项目</code><br>参与项目开发及版本迭代。学会工厂化模式,异步线程储存数据,接入第三方地图,附近医院导航定位,对异常<strong>图片处理</strong>(图片压缩、图片剪辑等)。<strong>发布App Store</strong>版本,并交付给业主。<br><strong>Launchr</strong><br>参与项目开发及版本迭代。<strong>国际化</strong>、<strong>单工程多target</strong>输出,动态化表单结构的申请,通讯录模块开发。</p>
<h4 id="技能清单"><a href="#技能清单" class="headerlink" title="技能清单"></a>技能清单</h4><ul>
<li>熟悉基本数据类型,数据结构,指针及内存管理</li>
<li>熟练使用Objective-C,C进行编程,了解OC和C++混编机制</li>
<li>熟悉iOS Foundation框架和UIKit框架</li>
<li>熟悉MVC/MVVM/代理/单例/KVO等iOS常用的设计模式</li>
<li>熟悉GCD、NSThread多线程管理,SQLite数据缓存</li>
<li>了解runtime,AOP(面向切面编程)</li>
<li>熟练git分布式版本控制,熟悉gitLab工作流</li>
<li>注重代码风格、命名规范和控件封装,重视 Code Review</li>
<li>熟悉沙盒文件管理机制</li>
<li>熟练cocoapods私有库封装</li>
<li>了解基于cocos2d-X C++开发手游,了解C++内存管理机制;</li>
<li>独立开发过IM iOS端</li>
<li>了解iOS端一些前沿架构,如<a href="http://gracelancy.com/blog/2016/01/06/ape-ios-arch-design/#disqus_thread" target="_blank" rel="noopener">猿题库iOS架构</a>、<a href="https://github.com/music4kid/CDD" target="_blank" rel="noopener">Context Driven Design</a></li>
</ul>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</div>
</article>
<article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
<div class="post-block">
<link itemprop="mainEntityOfPage" href="http://yoursite.com/2018/04/08/KVO/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="name" content="Tammy Miao">
<meta itemprop="description" content="">
<meta itemprop="image" content="/images/avatar.gif">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="CoderMSY's Blog">
</span>
<header class="post-header">
<h1 class="post-title" itemprop="name headline">
<a class="post-title-link" href="/2018/04/08/KVO/" itemprop="url">KVO原理及实现</a></h1>
<div class="post-meta">
<span class="post-time">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建于" itemprop="dateCreated datePublished" datetime="2018-04-08T22:56:53+08:00">
2018-04-08
</time>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h1 id="Key-Value-Observing"><a href="#Key-Value-Observing" class="headerlink" title="Key-Value Observing"></a>Key-Value Observing</h1><p>@(KVO)[原理|实现]</p>
<p><strong>KVO</strong>是观察者设计模式的一种实现,另外一种是通知机制(Notification)。它定义了对象之间观察和通知状态改变的通用机制。例如,指定一个被观察对象(例如 A 类),当对象某个属性(例如 A 中的字符串 name)发生更改时,对象会获得通知,并作出相应处理;【且不需要给被观察的对象添加任何额外代码,就能使用 KVO 机制】</p>
<p>在MVC架构下的项目,KVO机制很适合显示model和view之前的通讯。</p>
<hr>
<p>[TOC]</p>
<h2 id="基本原理"><a href="#基本原理" class="headerlink" title="基本原理"></a>基本原理</h2><p>当观察某对象 A 时,KVO 机制动态创建一个对象A当前类的子类,并为这个新的子类重写了被观察属性 keyPath 的 setter 方法。setter 方法随后负责通知观察对象属性的改变状况。</p>
<h2 id="深入剖析"><a href="#深入剖析" class="headerlink" title="深入剖析"></a>深入剖析</h2><p>Apple 使用了 isa 混写(isa-swizzling)来实现 KVO 。当观察对象A时,KVO机制动态创建一个新的名为:NSKVONotifying_A 的新类,该类继承自对象A的本类,且 KVO 为 NSKVONotifying_A 重写观察属性的 setter 方法,setter 方法会负责在调用原 setter 方法之前和之后,通知所有观察对象属性值的更改情况。<br>(备注: isa 混写(isa-swizzling)isa:is a kind of ; swizzling:混合,搅合;)</p>
<p>①<strong>NSKVONotifying_A 类剖析:</strong>在这个过程,被观察对象的 isa 指针从指向原来的 A 类,被 KVO 机制修改为指向<strong>系统新创建的子类 NSKVONotifying_A 类,来实现当前类属性值改变的监听;</strong><br>所以当我们从应用层面上看来,完全没有意识到有新的类出现,这是系统“隐瞒”了对 KVO 的底层实现过程,让我们误以为还是原来的类。但是此时如果我们创建一个新的名为“NSKVONotifying_A”的类,就会发现系统运行到注册 KVO 的那段代码时程序就崩溃,因为系统在注册监听的时候动态创建了名为 NSKVONotifying_A 的中间类,并指向这个中间类了。<br>(<strong>isa</strong> 指针的作用:每个对象都有 isa 指针,指向该对象的类,它告诉 Runtime 系统这个对象的类是什么。所以对象注册为观察者时,isa 指针指向新子类,<strong>那么这个被观察的对象就神奇地变成新子类的对象(或实例)了</strong>。) 因而在该对象上对 setter 的调用就会调用已重写的 setter,从而激活键值通知机制。<br>—>我猜,这也是 KVO 回调机制,为什么都俗称KVO技术为黑魔法的原因之一吧:内部神秘、外观简洁。<br>②<strong>子类setter方法剖析:</strong>KVO 的键值观察通知依赖于 NSObject 的两个方法:willChangeValueForKey:和 didChangevlueForKey:,在存取数值的前后分别调用 2 个方法:<br>被观察属性发生<strong>改变之前</strong>,willChangeValueForKey:被调用,通知系统该 keyPath 的属性值即将变更;当<strong>改变发生后</strong>, didChangeValueForKey: 被调用,通知系统该 keyPath 的属性值已经变更;<strong>之后</strong>, observeValueForKey:ofObject:change:context: 也会被调用。且重写观察属性的 setter 方法这种继承方式的注入是在<strong>运行时</strong>而<strong>不是编译时</strong>实现的。<br>KVO 为子类的观察者属性重写调用存取方法的工作原理在代码中相当于:</p>
<figure class="highlight objectivec"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">-(<span class="keyword">void</span>)setName:(<span class="built_in">NSString</span> *)newName{ </span><br><span class="line">[<span class="keyword">self</span> willChangeValueForKey:<span class="string">@"name"</span>]; <span class="comment">//KVO 在调用存取方法之前总调用 </span></span><br><span class="line">[<span class="keyword">super</span> setValue:newName forKey:<span class="string">@"name"</span>]; <span class="comment">//调用父类的存取方法 </span></span><br><span class="line">[<span class="keyword">self</span> didChangeValueForKey:<span class="string">@"name"</span>]; <span class="comment">//KVO 在调用存取方法之后总调用</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="特点"><a href="#特点" class="headerlink" title="特点"></a>特点</h3><blockquote>
<p>观察者观察的是属性,只有遵循 KVO 变更属性值的方式才会执行 KVO 的回调方法,例如是否执行了 setter 方法、或者是否使用了 KVC 赋值。<br>如果赋值没有通过 setter 方法或者 KVC,而是直接修改属性对应的成员变量,例如:仅调用 <code>_name = @"newName"</code>,这时是不会触发 KVO 机制,更加不会调用回调方法的。<br>所以使用 KVO 机制的前提是遵循 KVO 的属性设置方式来变更属性值。</p>
</blockquote>
<h2 id="应用"><a href="#应用" class="headerlink" title="应用"></a>应用</h2><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">- (<span class="keyword">void</span>)addObserver:(<span class="built_in">NSObject</span> *)observer</span><br><span class="line">forKeyPath:(<span class="built_in">NSString</span> *)keyPath</span><br><span class="line">options:(<span class="built_in">NSKeyValueObservingOptions</span>)options</span><br><span class="line">context:(<span class="keyword">void</span> *)context</span><br></pre></td></tr></table></figure>
<ul>
<li><strong>observer</strong> :注册 <strong>KVO</strong> 通知的对象。观察者必须实现 <code>key-value observing</code> 方法 <code>observeValueForKeyPath:ofObject:change:context:</code>。</li>
<li><strong>keyPath</strong> :观察者的属性的 <code>keypath</code>,相对于接受者,值不能是 <code>nil</code>。</li>
<li><strong>options</strong> :<code>NSKeyValueObservingOptions</code> 的组合,它指定了观察通知中包含了什么,可以查看 “NSKeyValueObservingOptions”。</li>
<li><strong>context</strong>:在 <code>observeValueForKeyPath:ofObject:change:context:</code>传给 <code>observer</code> 参数的随机数据</li>
</ul>
<p>让这个API不堪入目的事实就是最后两个参数经常是 0 和 NULL。</p>
<p><code>options</code> 代表 <code>NSKeyValueObservingOptions</code> 的位掩码,需要注意 <code>NSKeyValueObservingOptionNew</code> & <code>NSKeyValueObservingOptionOld</code> ,因为这些是你经常要用到的,可以跳过 <code>NSKeyValueObservingOptionInitial</code> & <code>NSKeyValueObservingOptionPrior:</code></p>
<h3 id="NSKeyValueObservingOptions"><a href="#NSKeyValueObservingOptions" class="headerlink" title="NSKeyValueObservingOptions"></a>NSKeyValueObservingOptions</h3><ul>
<li><code>NSKeyValueObservingOptionNew</code>: 表明变化的字典应该提供新的属性值,如何可以的话。</li>
<li><code>NSKeyValueObservingOptionOld</code>: 表明变化的字典应该包含旧的属性值,如何可以的话。</li>
<li><code>NSKeyValueObservingOptionInitial</code>: 如果被指定,一个通知会立刻发送到观察者,甚至在观察者注册方法之前就返回,改变的字典需要包含一个 <code>NSKeyValueChangeNewKey</code> 入口,如果 <code>NSKeyValueObservingOptionNew</code> 也被指定的话,但从来不会包含一个<code>NSKeyValueChangeOldKey</code> 入口。(在一个 initial notification 里,观察者的当前属性可能是旧的,但对观察者来说是新的),你可以使用这个选项代替显式的调用,同时,代码也会被观察者的 <code>observeValueForKeyPath:ofObject:change:context:</code> 方法调用,当这个选项被用于 <code>addObserver:forKeyPath:options:context:</code>,一个通知将会发送到每个被观察者添加进去的索引对象中。</li>
<li><code>NSKeyValueObservingOptionPrior</code>:是否各自的通知应该在每个改变前后发送到观察者,而不是在改变之后发送一个单独的通知。一个通知中的可变数组在改变发生之前发送经常包含一个 <code>NSKeyValueChangeNotificationIsPriorKey</code> 入口且它的值是 @YES,但从来不会包含一个 <code>NSKeyValueChangeNewKey</code> 入口。当这个选项被指定,在改变之后发送的通知中的变化的字典包含了一个与在选项没有被指定的情况下应该包含的同一个入口,当观察者自己的键值观察需要它的时候,你可以使用这个选项来调用 -willChange… 方法中的一个来观察它自己的某个属性,那个属性的值依赖于被观察的对象的属性。(在那种情况,调用 -willChange… 来对收到的一个<code>observeValueForKeyPath:ofObject:change:context:</code> 消息做出反应可能就太晚了)</li>
</ul>
<h4 id="1-注册对象myKVO为被观察者"><a href="#1-注册对象myKVO为被观察者" class="headerlink" title="1.注册对象myKVO为被观察者:"></a>1.注册对象myKVO为被观察者:</h4><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[<span class="keyword">self</span>.kvoModel addObserver:<span class="keyword">self</span> forKeyPath:<span class="built_in">NSStringFromSelector</span>(<span class="keyword">@selector</span>(kvoNum)) options:<span class="built_in">NSKeyValueObservingOptionOld</span> | <span class="built_in">NSKeyValueObservingOptionNew</span> context:<span class="literal">nil</span>];</span><br></pre></td></tr></table></figure>
<h4 id="2-只要object的keyPath属性发生变化,就会调用此回调方法,进行相应的处理:UI更新:"><a href="#2-只要object的keyPath属性发生变化,就会调用此回调方法,进行相应的处理:UI更新:" class="headerlink" title="2.只要object的keyPath属性发生变化,就会调用此回调方法,进行相应的处理:UI更新:"></a>2.只要object的keyPath属性发生变化,就会调用此回调方法,进行相应的处理:UI更新:</h4><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//通过改变KVOModel的kvoNum值</span></span><br><span class="line">- (<span class="keyword">IBAction</span>)addBtnClicked:(<span class="built_in">UIButton</span> *)sender {</span><br><span class="line"></span><br><span class="line"><span class="keyword">self</span>.kvoModel.kvoNum = <span class="keyword">self</span>.kvoModel.kvoNum + <span class="number">1</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">- (<span class="keyword">void</span>)observeValueForKeyPath:(<span class="built_in">NSString</span> *)keyPath ofObject:(<span class="keyword">id</span>)object change:(<span class="built_in">NSDictionary</span><<span class="built_in">NSKeyValueChangeKey</span>,<span class="keyword">id</span>> *)change context:(<span class="keyword">void</span> *)context {</span><br><span class="line"><span class="keyword">if</span> (object == <span class="keyword">self</span>.kvoModel) {</span><br><span class="line"><span class="keyword">if</span> ([keyPath isEqualToString:<span class="built_in">NSStringFromSelector</span>(<span class="keyword">@selector</span>(kvoNum))]) {</span><br><span class="line"><span class="keyword">self</span>.oldLab.text = [<span class="built_in">NSString</span> stringWithFormat:<span class="string">@"旧值为:%@"</span>,[change valueForKey:<span class="string">@"old"</span>]];</span><br><span class="line"><span class="keyword">self</span>.freshLab.text = [<span class="built_in">NSString</span> stringWithFormat:<span class="string">@"新值为:%@"</span>,[change valueForKey:<span class="string">@"new"</span>]];</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h4 id="3-移除KVO"><a href="#3-移除KVO" class="headerlink" title="3.移除KVO"></a>3.移除KVO</h4><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[<span class="keyword">self</span> removeObserver:<span class="keyword">self</span> forKeyPath:<span class="built_in">NSStringFromSelector</span>(<span class="keyword">@selector</span>(kvoNum)) context:<span class="literal">nil</span>];</span><br></pre></td></tr></table></figure>
<hr>
<blockquote>
<p>参考链接<br><a href="https://www.jianshu.com/p/e59bb8f59302" target="_blank" rel="noopener">简书</a><br><a href="http://nshipster.cn/key-value-observing/" target="_blank" rel="noopener">Mattt Thompson</a></p>
</blockquote>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</div>
</article>
<article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
<div class="post-block">
<link itemprop="mainEntityOfPage" href="http://yoursite.com/2018/04/02/IM-技术选型及相关逻辑/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="name" content="Tammy Miao">
<meta itemprop="description" content="">
<meta itemprop="image" content="/images/avatar.gif">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="CoderMSY's Blog">
</span>
<header class="post-header">
<h1 class="post-title" itemprop="name headline">
<a class="post-title-link" href="/2018/04/02/IM-技术选型及相关逻辑/" itemprop="url">IM 技术选型及相关逻辑</a></h1>
<div class="post-meta">
<span class="post-time">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建于" itemprop="dateCreated datePublished" datetime="2018-04-02T13:28:53+08:00">
2018-04-02
</time>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h3 id="待续中"><a href="#待续中" class="headerlink" title="待续中"></a>待续中</h3>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</div>
</article>
<article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
<div class="post-block">
<link itemprop="mainEntityOfPage" href="http://yoursite.com/2018/01/05/CoCoaLumberJack日志介绍/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="name" content="Tammy Miao">
<meta itemprop="description" content="">
<meta itemprop="image" content="/images/avatar.gif">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="CoderMSY's Blog">
</span>
<header class="post-header">
<h1 class="post-title" itemprop="name headline">
<a class="post-title-link" href="/2018/01/05/CoCoaLumberJack日志介绍/" itemprop="url">CoCoaLumberJack日志</a></h1>
<div class="post-meta">
<span class="post-time">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建于" itemprop="dateCreated datePublished" datetime="2018-01-05T16:30:55+08:00">
2018-01-05
</time>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<p><a href="https://www.jianshu.com/p/ea1e6b210b27" target="_blank" rel="noopener">https://www.jianshu.com/p/ea1e6b210b27</a></p>
<h3 id="1-CoCoaLumberJack的日志级别有如下几种:"><a href="#1-CoCoaLumberJack的日志级别有如下几种:" class="headerlink" title="1.CoCoaLumberJack的日志级别有如下几种:"></a>1.CoCoaLumberJack的日志级别有如下几种:</h3><ul>
<li>LOG_LEVEL_ERROR:如果设置为LOG_LEVEL_ERROR,仅仅能看到Error相关的日志输出。</li>
<li>LOG_LEVEL_WARN:如果设置为LOG_LEVEL_WARN,能看到Error、Warn相关的日志输出。</li>
<li>LOG_LEVEL_INFO:如果设置为LOG_LEVEL_INFO,能够看到Error、Warn、Info相关的日志输出。</li>
<li>LOG_LEVEL_DEBUG:如果设置为LOG_LEVEL_DEBUG,能够看到Error/Warn/Info/Debug相关的日志输出。</li>
<li>LOG_LEVEL_VERBOSE:如果设置为LOG_FLAG_VERBOSE,能够看到所有级别的日志输出。</li>
<li>LOG_LEVEL_OFF:不输出日志。</li>
</ul>
<p>DDLogLevel 定义了全局的 log 等级,DDLogFlag 是我们打 log 时设定的 log 等级,CocoaLumberjack 会比较两者,如果 flag 低于 level,则不会打 log:</p>
<h3 id="2-如何使用"><a href="#2-如何使用" class="headerlink" title="2.如何使用"></a>2.如何使用</h3><p>使用宏来处理,指定日志的记录级别<br>创建一个PCH文件,并添加如下代码:<br>//定义并导入CoCoaLumberJack框架<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">#define LOG_LEVEL_DEF ddLogLevel</span><br><span class="line">#import <CocoaLumberjack.h></span><br><span class="line"></span><br><span class="line">//通过DEBUG模式设置全局日志等级,DEBUG时为Verbose,所有日志信息都可以打印,否则Error,只打印</span><br><span class="line">#ifdef DEBUG</span><br><span class="line">static const DDLogLevel ddLogLevel = DDLogLevelVerbose;</span><br><span class="line">#else</span><br><span class="line">static const DDLogLevel ddLogLevel = DDLogLevelError;</span><br><span class="line">#endif</span><br><span class="line"></span><br><span class="line">#endif /* PrefixHeader_pch */</span><br><span class="line">``` </span><br><span class="line"></span><br><span class="line">在代码中就可以使用DDLogError/DDLogWarn/DDLogDebug/DDLogVerbose来无缝替代NSLog,例如:</span><br><span class="line"></span><br><span class="line">DDLogError(@"[Error]:%@", @"输出错误信息");//输出错误信息</span><br><span class="line">DDLogWarn(@"[Warn]:%@", @"输出警告信息");//输出警告信息</span><br><span class="line">DDLogInfo(@"[Info]:%@", @"输出描述信息");//输出描述信息</span><br><span class="line">DDLogDebug(@"[Debug]:%@", @"输出调试信息");//输出调试信息</span><br><span class="line">DDLogVerbose(@"[Verbose]:%@", @"输出详细信息");//输出详细信息</span><br><span class="line"></span><br><span class="line">在PCH文件中,再次宏定义一下,例如DDLogError:</span><br><span class="line"></span><br><span class="line">``` objective-c</span><br><span class="line">#ifdef DEBUG</span><br><span class="line">#define DLog(format, ...) DDLogError((@"[文件名:%s]" "[函数名:%s]" "[行号:%d]" format), __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__);</span><br><span class="line">#else</span><br><span class="line">#define DLog(...);</span><br><span class="line">#endif</span><br><span class="line"></span><br><span class="line">DLog(@"User selected file:%@ withSize:%d", @"filePath", 123);</span><br><span class="line">[fg214,57,30;2016-05-17 01:17:22:443 MD5[28301:3795644] [文件名:/Users/lg/Desktop/Log/Log/ViewController.m][函数名:-[ViewController viewDidLoad]][行号:31]User selected file:filePath withSize:123</span><br></pre></td></tr></table></figure></p>
<h3 id="3-CocoaLumberjack框架主要文件分类"><a href="#3-CocoaLumberjack框架主要文件分类" class="headerlink" title="3.CocoaLumberjack框架主要文件分类"></a>3.CocoaLumberjack框架主要文件分类</h3><p>需要添加的主要文件有四个:</p>
<ul>
<li>1.@DDLog(整个框架的基础)</li>
<li>2.@DDASLLogger(发送日志语句到苹果的日志系统,以便它们显示在Console.app上)//个人建议没有必要</li>
<li>3.@DDTTYLoyger(发送日志语句到Xcode控制台,如果可用)</li>
<li>4.@DDFIleLoger(把日志语句发送至文件)</li>
</ul>
<p>DDLog是强制性的,其余的都是可选的,这取决于你打算如何使用这个框架。例如,如果你不打算纪录到一个文件,你可以跳过DDFileLogger,或者你想跳过ASL以便更快的文件记录,你可以跳过DDASLLoger。</p>
<h3 id="4-需要注意的是:"><a href="#4-需要注意的是:" class="headerlink" title="4.需要注意的是:"></a>4.需要注意的是:</h3><p>在使用DDLogError或者DLog之前,首先得在application:didFinishLaunchingWithOptions:方法中配置这个日志框架:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">//开始时,你需要下面两行代码:</span><br><span class="line">[DDLog addLogger:[DDASLLogger sharedInstance]]; </span><br><span class="line">[DDLog addLogger:[DDTTYLogger sharedInstance]]; </span><br><span class="line">//这将在你的日志框架中添加两个“logger”。也就是说你的日志语句将被发送到Console.app和Xcode控制 台(就像标准的NSLog)</span><br><span class="line"></span><br><span class="line">//这个框架的好处之一就是它的灵活性,如果你还想要你的日志语句写入到一个文件中,你可以添加和配置一个file logger:</span><br><span class="line">fileLogger = [[DDFileLogger alloc] init]; </span><br><span class="line">fileLogger.rollingFrequency = 60 * 60 * 24; // 24 hour rolling </span><br><span class="line">fileLogger.logFileManager.maximumNumberOfLogFiles = 7; </span><br><span class="line"></span><br><span class="line">[DDLog addLogger:fileLogger]; </span><br><span class="line"></span><br><span class="line">//上面的代码告诉应用程序要在系统上保持一周的日志文件。</span><br><span class="line">//如果不设置rollingFrequency和maximumNumberOfLogFiles,</span><br><span class="line">//则默认每天1个Log文件、存5天、单个文件最大1M、总计最大20M,否则自动清理最前面的记录。</span><br></pre></td></tr></table></figure></p>
<p>接下来上传,可以从DDLogFileManager protocol下面的两个协议中获取Log文件rolling时的通知,便可以在此时将Log文件上传:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">//Notifications from DDFileLogger</span><br><span class="line">- (void)didArchiveLogFile:(NSString *)logFilePath;</span><br><span class="line">- (void)didRollAndArchiveLogFile:(NSString *)logFilePath;</span><br></pre></td></tr></table></figure></p>
<p><strong>但是我并没有这样做,因为后来发现一个问题:</strong><br>而CocoaLumberJack只会记录下我们主动打印过的信息,而不会抓取、记录下苹果自带的crash信息,更不会在下一次程序启动时通知我们程序是否崩溃过。</p>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</div>
</article>
<article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
<div class="post-block">
<link itemprop="mainEntityOfPage" href="http://yoursite.com/2017/12/23/沙盒介绍/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="name" content="Tammy Miao">
<meta itemprop="description" content="">
<meta itemprop="image" content="/images/avatar.gif">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="CoderMSY's Blog">
</span>
<header class="post-header">
<h1 class="post-title" itemprop="name headline">
<a class="post-title-link" href="/2017/12/23/沙盒介绍/" itemprop="url">沙盒介绍</a></h1>
<div class="post-meta">
<span class="post-time">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建于" itemprop="dateCreated datePublished" datetime="2017-12-23T14:40:30+08:00">
2017-12-23
</time>
</span>