-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathatom.xml
More file actions
450 lines (310 loc) · 328 KB
/
atom.xml
File metadata and controls
450 lines (310 loc) · 328 KB
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
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>红纸</title>
<link href="/atom.xml" rel="self"/>
<link href="http://www.zhz.io/"/>
<updated>2018-07-05T11:59:04.665Z</updated>
<id>http://www.zhz.io/</id>
<author>
<name>HongZhi</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>那个LLDB你真的了解吗</title>
<link href="http://www.zhz.io/2018/07/05/%E9%82%A3%E4%B8%AALLDB%E4%BD%A0%E7%9C%9F%E7%9A%84%E4%BA%86%E8%A7%A3%E5%90%97/"/>
<id>http://www.zhz.io/2018/07/05/那个LLDB你真的了解吗/</id>
<published>2018-07-05T11:58:14.000Z</published>
<updated>2018-07-05T11:59:04.665Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>曾经有个笑话(身边的真相)。有次,我要回去写代码的时候呢,我无意间说了一句‘我们回去写BUG吧’。然后传为组内佳话。<br>BUG对于我们程序员来说是无所不在的,你想你的代码没有BUG,除非你没写代码。对于我们iOS or macOS 开发的同学们来说,苹果为我们准备了一个非常良好的Debug的生态环境(LLDB)这使得我们能够非常快速,有效的去排除,解决一些问题。</p><p>那么本文主要以本人平时的开发习惯/<a href="https://developer.apple.com/videos/play/wwdc2018/412/" target="_blank" rel="noopener">WWDC 2018 Session 412</a>/<a href="https://store.raywenderlich.com/products/advanced-apple-debugging-and-reverse-engineering" target="_blank" rel="noopener">Advaced Apple Debug</a>书中部分内容进行如下总结了一些LLDB的命令</p><a id="more"></a><h2 id="LLDB"><a href="#LLDB" class="headerlink" title="LLDB"></a>LLDB</h2><p>作为iOS开发者,我们经常回去设置一些断点,当程序运行到这一行的时候,我们就可以进入LLDB的控制台,去打印一些信息,来辅佐我们进行调试工作。但是我们平常只会去使用p/e命令吧?(至少我以前是这样的。)但是我们今天准备来系统的学习一下LLDB那些比较有作用的命令。在LLDB控制面板中打入help,我们可以看到一堆的命令。今天我准备拿出其中几个用的比较多的来进行说明。</p><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// Demo代码,下面的说明都会围绕这个代码进行描述</span></div><div class="line"><span class="class"><span class="keyword">@interface</span> <span class="title">HZViewController</span> ()</span></div><div class="line"></div><div class="line"><span class="keyword">@property</span> (<span class="keyword">nonatomic</span>, <span class="keyword">strong</span>) <span class="built_in">NSString</span> *string;</div><div class="line"><span class="keyword">@property</span> (<span class="keyword">nonatomic</span>, <span class="keyword">strong</span>) HZModel *model;</div><div class="line"></div><div class="line"></div><div class="line"><span class="keyword">@end</span></div><div class="line"></div><div class="line"><span class="class"><span class="keyword">@implementation</span> <span class="title">HZViewController</span></span></div><div class="line"></div><div class="line">- (<span class="keyword">void</span>)viewDidLoad</div><div class="line">{</div><div class="line"> [<span class="keyword">super</span> viewDidLoad];</div><div class="line"> <span class="keyword">self</span>.view.backgroundColor = [<span class="built_in">UIColor</span> whiteColor];</div><div class="line"> </div><div class="line">}</div><div class="line"><span class="keyword">@end</span></div></pre></td></tr></table></figure><h2 id="expression"><a href="#expression" class="headerlink" title="expression"></a>expression</h2><p>expression作为我们最常用的指令之一,他的作用<em>执行一个表达式,并将表达式的结果作为结果进行输出</em>,expression的完整语法<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">expression <cmd-options> -- <expr></div></pre></td></tr></table></figure></p><ul><li><cmd-options>: 命令参数选项,可以根据<em>help expression</em>进行查看他有哪些命令集合。</cmd-options></li><li><expr>: 表达式部分,也就是代码部分<blockquote><p>expression的简写为e,当你使用默认参数的时候可以不用打–这个多余的符号,但是当你使用参数的时候就必须加上。不过一般来说我们无需手动去进行设置。<br>OK,我们来实践一下。</p><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 最初String并没有被初始化(e也可以被当做是一个打印命令来打印信息。)</span></div><div class="line">(lldb) e <span class="keyword">self</span>.string</div><div class="line">(<span class="built_in">NSString</span> *) $<span class="number">6</span> = <span class="literal">nil</span></div><div class="line"><span class="comment">// 这个时候我们去执行一段表达式,去修改string的变量</span></div><div class="line">(lldb) e <span class="keyword">self</span>.string = <span class="string">@"TestString"</span></div><div class="line">(<span class="built_in">NSTaggedPointerString</span> *) $<span class="number">7</span> = <span class="number">0xbf4c3b53c7710e1d</span></div><div class="line"><span class="comment">// 我们去打印这个值</span></div><div class="line">(lldb) po <span class="keyword">self</span>.string</div><div class="line">TestString</div></pre></td></tr></table></figure></blockquote></expr></li></ul><h2 id="print"><a href="#print" class="headerlink" title="print"></a>print</h2><p>print相对来说很简单,其实他就是<em>expression – <expr></expr></em>就是一个简化版的e,其简写为p。<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">print</span> <expr></div></pre></td></tr></table></figure></p><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">(lldb) p <span class="keyword">self</span>.string = <span class="string">@"asd"</span></div><div class="line">(<span class="built_in">NSTaggedPointerString</span> *) $<span class="number">12</span> = <span class="number">0xbf623d41451e6fa4</span> <span class="string">@"\372\346Q\324#\366"</span></div><div class="line">(lldb) po <span class="keyword">self</span>.string</div><div class="line">asd</div></pre></td></tr></table></figure><h2 id="po"><a href="#po" class="headerlink" title="po"></a>po</h2><p>如果expression不加任何参数,那么他打印出来的信息都是指针的信息。这对于我们调试来说是非常不方便的。<br>所以expression有一个-O的参数,是用来打印函数对象的,并且他的描述即为,打印具有语言的描述性API。<br>po其实就是<em>expression -O <expr></expr></em>的简写,Xcode团队很人性化的加入了这个东西。当然上面的描述性API就是指的<em>description</em>和<em>debugDescription</em>了。<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//为上面的代码加入以下代码 HZViewController.m中</span></div><div class="line">- (<span class="built_in">NSString</span> *)debugDescription {</div><div class="line"> <span class="keyword">return</span> [<span class="built_in">NSString</span> stringWithFormat:<span class="string">@" 类名:%@ \n 属性列表:%@"</span>,<span class="built_in">NSStringFromClass</span>(HZViewController.class),HZViewController.hz_propertyKeys];</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">// LLDB调试输出</span></div><div class="line">(lldb) po <span class="keyword">self</span> <span class="comment">// 尝试使用po命令还输出self</span></div><div class="line"> 类名:HZViewController </div><div class="line"> 属性列表:(</div><div class="line"> string,</div><div class="line"> model</div><div class="line">)</div><div class="line">(lldb) p <span class="keyword">self</span> </div><div class="line">(HZViewController *) $<span class="number">1</span> = <span class="number">0x00007f934e50f4d0</span></div><div class="line">(lldb) e -O -- <span class="keyword">self</span> <span class="comment">// 对比于po是没有任何区别的</span></div><div class="line"> 类名:HZViewController </div><div class="line"> 属性列表:(</div><div class="line"> string,</div><div class="line"> model</div><div class="line">)</div></pre></td></tr></table></figure></p><h2 id="call"><a href="#call" class="headerlink" title="call"></a>call</h2><p>其实还有一个打印命令是call,对比于其他打印命令来说,他有一个优势在于,他能执行多行代码(但是我觉得比较鸡肋,我从来都没用过)。<br>如果单纯使用<em>call <expr></expr></em>的话他跟e/p无差异。但是你单独使用call,就可以键入多行函数,来执行一些定制化的需求。比如下面这个。<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">(lldb) po <span class="keyword">self</span>.view.backgroundColor</div><div class="line"> <span class="literal">nil</span></div><div class="line">(lldb) call <span class="comment">// 进入函数输入控制</span></div><div class="line">Enter expressions, then terminate with an empty line to evaluate:</div><div class="line"><span class="number">1</span> <span class="keyword">if</span> (<span class="keyword">self</span>.view.backgroundColor == <span class="literal">nil</span>) {</div><div class="line"><span class="number">2</span> <span class="keyword">self</span>.view.backgroundColor = [<span class="built_in">UIColor</span> whiteColor];</div><div class="line"><span class="number">3</span> }</div><div class="line">(lldb) po <span class="keyword">self</span>.view.backgroundColor</div><div class="line"><span class="built_in">UIExtendedGrayColorSpace</span> <span class="number">1</span> <span class="number">1</span></div><div class="line">(lldb) call <span class="keyword">self</span>.view.backgroundColor</div><div class="line">(<span class="built_in">UICachedDeviceWhiteColor</span> *) $<span class="number">10</span> = <span class="number">0x0000600001c46070</span></div><div class="line">(lldb) e <span class="keyword">self</span>.view.backgroundColor</div><div class="line">(<span class="built_in">UICachedDeviceWhiteColor</span> *) $<span class="number">11</span> = <span class="number">0x0000600001c46070</span></div></pre></td></tr></table></figure></p><h2 id="p-e-po-call区别"><a href="#p-e-po-call区别" class="headerlink" title="p/e/po/call区别"></a>p/e/po/call区别</h2><table><thead><tr><th>命令</th><th>主要区别</th><th>函数提示</th></tr></thead><tbody><tr><td>e</td><td>打印属性(基础类型:包含函数类型和变量值,对象类型:包含函数指针)</td><td>没有命令行提醒,完全属于盲打</td></tr><tr><td>p</td><td>同e</td><td>终端会根据当前堆栈,提示属性/函数信息</td></tr><tr><td>po</td><td>打印具有平台特性的描述性API(基础类型:打印变量值,对象: 根据描述API输出)</td><td>终端会根据当前堆栈,提示属性/函数信息</td></tr><tr><td>call</td><td>打印功能同e,但是还具备输入多行表达式的功能</td><td>没有命令行提醒,完全属于盲打</td></tr></tbody></table><h2 id="断点"><a href="#断点" class="headerlink" title="断点"></a>断点</h2><p>断点设置命令,可能会有人问了,我用Xcode,在函数边上点一下不就有断点了么,要这个命令有个锤子用?那只能说你作死作的还不够多。当你没有源码的时候,这个命令就成了你的救命稻草。举个🌰,我有个砸过壳的IPA包,纯二进制,你只能通过<em>class-dump</em>或者<em>IDA</em>去获取到一些函数信息,然后你就可以悄然的去用这个命令去设置一些断点,调试当前堆栈里的信息。整个APP都在你的掌控之中。OK,废话B多了,脑子要坏掉的,我们继续来看下这个命令。</p><blockquote><p>breakpoint的缩写为br,后面都会用缩写来进行描写</p></blockquote><h3 id="br-set"><a href="#br-set" class="headerlink" title="br set"></a>br set</h3><p>breakpoint set命令是用来设置断点的,LLDB提供了很多设置断点的方式。<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">breakpoint set <cmd-options></div></pre></td></tr></table></figure></p><p><em>使用 -n 来根据名称来设置一个断点</em><br>给所有的init方法设置上断点。<br><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">(lldb) br set -n init</div><div class="line">Breakpoint 3: 3657 locations. // 可以看到设置了3657的断点 2333333</div><div class="line">(lldb) br list</div><div class="line">Current breakpoints:</div><div class="line">3: name = 'init', locations = 3657, resolved = 3657, hit count = 0</div><div class="line"> 3.1: where = CoreGraphics`ttf::cvt::init(), address = 0x0000000101042ec6, resolved, hit count = 0 </div><div class="line"> 3.2: where = CoreGraphics`ttf::font::init(), address = 0x000000010148c9ca, resolved, hit count = 0 </div><div class="line"> 3.3: where = CoreGraphics`ttf::fpgm::init(), address = 0x0000000101063086, resolved, hit count = 0 </div><div class="line"> 3.4: where = CoreGraphics`ttf::glyf::init(), address = 0x00000001012bacea, resolved, hit count = 0</div></pre></td></tr></table></figure></p><p><em>使用 -f 来根据文件名去配合设置断点</em><br>我只想给HZViewController的init设置断点,其他断点都不要,因为前面设置的断点太多了。<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">(lldb) br set -f HZViewController.m -n init</div><div class="line">Breakpoint <span class="number">3</span>: where = HZTest_Example`-[HZViewController init] + <span class="number">20</span> at HZViewController.m:<span class="number">35</span>, address = <span class="number">0x000000010b6eeba4</span></div></pre></td></tr></table></figure></p><blockquote><p>Note: 文件的使用条件是,这个文件中必须包含有init函数。集成的话是不会被标记出来的。会提示<em>WARNING: Unable to resolve breakpoint to any actual locations.</em></p></blockquote><p><em>使用 -l 来根据文件来指定断点行数</em><br><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line">26:@property (nonatomic, strong) NSString *string;</div><div class="line">34:- (instancetype)init {</div><div class="line">35: if (self = [super init]) {</div><div class="line">36:</div><div class="line">37: }</div><div class="line">38: return self;</div><div class="line">39:}</div><div class="line">(lldb) br set -l 26 -f HZViewController.m</div><div class="line">Breakpoint 6: 2 locations.</div><div class="line">(lldb) br list</div><div class="line">Current breakpoints:</div><div class="line">6: file = 'HZViewController.m', line = 26, exact_match = 0, locations = 2, resolved = 2, hit count = 0</div><div class="line"> 6.1: where = HZTest_Example`-[HZViewController string] + 12 at HZViewController.m:26, address = 0x000000010f3c2f0c, resolved, hit count = 0 </div><div class="line"> 6.2: where = HZTest_Example`-[HZViewController setString:] + 49 at HZViewController.m:26, address = 0x000000010f3c2f51, resolved, hit count = 0 </div><div class="line">(lldb) br set -l 35 -f HZViewController.m</div><div class="line">Breakpoint 8: where = HZTest_Example`-[HZViewController init] + 20 at HZViewController.m:35, address = 0x000000010f3c2ba4</div><div class="line">(lldb) br list</div><div class="line">Current breakpoints:</div><div class="line">8: file = 'HZViewController.m', line = 35, exact_match = 0, locations = 1, resolved = 1, hit count = 0</div><div class="line"> 8.1: where = HZTest_Example`-[HZViewController init] + 20 at HZViewController.m:35, address = 0x000000010f3c2ba4, resolved, hit count = 0</div></pre></td></tr></table></figure></p><blockquote><p>Note:如果指定的地方是一个属性,那么就会指定其set和get方法,如果指定的地方是一个空白行的地方,则自动向下寻找第一个存在函数的指定行</p></blockquote><p>后面其实都是给前面的命令,新增一些功能而准备的。就不一个个去设置了。</p><ul><li><p><em>-c</em> 用来设置断点条件,比如说我们有时会希望想当一个条件为真的时候,才触发断点操作,那么我们就可以去使用这个东西,对应如果是使用UI去设置的话就是这个选项框</p><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">br set -n setString: -c string == <span class="string">@"TestString"</span></div></pre></td></tr></table></figure><p><img src="/resources/34E7B233E6285A6B512BFD507DA32887.jpg" alt="IMAGE"></p></li><li><em>-o</em> 来设置单次断点WWDC 2018 Session412中介绍的一点<br><em>-o</em>=<em>–one-shot true</em><br>配合br command add可以达到在某个断点后单次拦截某个函数,有兴趣看怎么操作的可以去看一下这位写的<a href="https://juejin.im/post/5b1cd870e51d4506dc0ac76c" target="_blank" rel="noopener">效率提升爆表的 Xcode 和 LLDB 调试技巧</a></li><li><em>-i</em> 设置执行忽略断点次数,对应的UI设置如图所示。<br><img src="/resources/88734A697D8FDFB43E853DA1F467D086.jpg" alt="IMAGE"></li></ul><table><thead><tr><th>函数</th><th>区别</th></tr></thead><tbody><tr><td>br set -f Test.m –line 12</td><td>给某个文件的12行函数加个断点</td></tr><tr><td>br set –name test</td><td>给所有test 函数加个断点</td></tr><tr><td>br set –method test</td><td>给所有test C++函数加个断点</td></tr><tr><td>br set –selector test</td><td>给所有test OC函数加个断点</td></tr><tr><td>br set –shlib Foundation.framework –name test</td><td>给Foundation库的test函数加个断点</td></tr></tbody></table><h3 id="br-command"><a href="#br-command" class="headerlink" title="br command"></a>br command</h3><p>从上面的set中,我们知道了如何去用代码去设置一个断点。当我们去执行了断点后,我们希望让他去自动的去帮我们去输出一些内容,而不是我们一个个po/e去打印。那么这个command就是为了这个而设计的。从可视化的方式设置的话,就如图所示了。<br><img src="/resources/CD4AB509BE03B60C485114A326132841.jpg" alt="IMAGE"></p><blockquote><p>Note: 其实Xcode提供了很多种类断点后执行的方式,比如执行一段脚本/发出声音/打印信息等等,我们这边只讲打印Debug信息的内容,跟你在LLDB中打印无异。</p></blockquote><p><em>br command list</em> 显示某个断点当前的所有可执行项<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">(lldb) br command list <span class="number">2</span></div><div class="line">Breakpoint <span class="number">2</span>:</div><div class="line"> po <span class="keyword">self</span></div></pre></td></tr></table></figure></p><p><em>br command add</em> 给断点添加一个可执行项<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">(lldb) br set -n testGlobalPoint -f HZViewController.m</div><div class="line">Breakpoint <span class="number">3</span>: where = HZTest_Example`-[HZViewController testGlobalPoint] + <span class="number">23</span> at HZViewController.m:<span class="number">52</span>, address = <span class="number">0x0000000105396d27</span></div><div class="line">(lldb) br command add -o <span class="string">"po self"</span> <span class="number">3</span></div><div class="line">(lldb) br command list <span class="number">2</span></div><div class="line">Breakpoint <span class="number">2</span>:</div><div class="line"> po <span class="keyword">self</span></div></pre></td></tr></table></figure></p><blockquote><p>Note: 如果你尝试给一个断点添加多个命令,-o是无法满足的,因为-o是–one-line的意思,他只能添加一行。后续的添加会覆盖前面的。<br>所以你可以直接使用br command add 3 (3是断点标识)<br>当然你可以去给断点加个脚本help br command add。可以看到Apple给我们举了一个添加脚本的🌰。虽然我并不知道这玩意儿有啥鸟用<br>小技巧LLDB中有个关键词是contiune,我们可以键入这个去跳过断点,所以我们输入多行的时候就可以这么玩了,这样会跳过断点,但是又会有合理的Log输出<br><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">(lldb) br command add 3</div><div class="line">Enter your debugger command(s). Type 'DONE' to end.</div><div class="line">> po self</div><div class="line">> continue</div><div class="line">> DONE</div></pre></td></tr></table></figure></p></blockquote><p><em>br command delete</em> 删除一个断点的可执行项<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">(lldb) br command delete <span class="number">3</span></div><div class="line">(lldb) br command list <span class="number">3</span></div><div class="line">Breakpoint <span class="number">3</span> does not have an associated command.</div></pre></td></tr></table></figure></p><h3 id="br-list"><a href="#br-list" class="headerlink" title="br list"></a>br list</h3><p>查看当前设置的断点的列表,enable/disable的都会显示在上面,包含运行期的代码断点和运行前的可视化断点<br>LLDB会默认先读取你在非运行期,设置的断点即在Xcode的函数左边的小箭头,当我们每次设置一次可视化的断点。就会去设置断点文件。<br>文件路径<em>/工程目录//Users/hongzhizhao/Desktop/HZTest/Example/HZTest.xcworkspace/xcuserdata/hongzhizhao.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist</em><br><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">(lldb) br list</div><div class="line">Current breakpoints:</div><div class="line">1: file = '/Users/xxxxx/Desktop/HZTest/Example/HZTest/HZViewController.m', line = 44, exact_match = 0, locations = 1, resolved = 1, hit count = 0</div><div class="line"></div><div class="line"> 1.1: where = HZTest_Example`-[HZViewController testGlobalPoint] + 23 at HZViewController.m:44, address = 0x0000000100d42d37, resolved, hit count = 0 </div><div class="line"></div><div class="line">2: file = '/Users/xxxx/Desktop/HZTest/Example/HZTest/HZViewController.m', line = 37, exact_match = 0, locations = 1, resolved = 1, hit count = 1</div><div class="line"></div><div class="line"> 2.1: where = HZTest_Example`-[HZViewController viewDidLoad] + 61 at HZViewController.m:37, address = 0x0000000100d42c6d, resolved, hit count = 1</div></pre></td></tr></table></figure></p><h3 id="br-delete"><a href="#br-delete" class="headerlink" title="br delete"></a>br delete</h3><p>顾名思义,删除断点。我们可以通过br list中去找到对应的nuber。(包含二级的也可以。)<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">breakpoint delete <span class="comment">#number#</span></div></pre></td></tr></table></figure></p><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">(lldb) br set -n init</div><div class="line">Breakpoint 6: 3657 locations.</div><div class="line">(lldb) br list</div><div class="line">Current breakpoints:</div><div class="line">6: name = 'init', locations = 3657, resolved = 3657, hit count = 0</div><div class="line"> 6.3656: where = EmailCore`-[ECEncodedWordEncoder init], address = 0x000000011e6caa14, resolved, hit count = 0 </div><div class="line"> 6.3657: where = CoreRecents`-[CRRecentContactsLibrary init], address = 0x000000011e6dec27, unresolved, hit count = 0 </div><div class="line">(lldb) br delete 6.3657</div><div class="line">0 breakpoints deleted; 1 breakpoint locations disabled.</div><div class="line">(lldb) br list</div><div class="line"> 6.3656: where = EmailCore`-[ECEncodedWordEncoder init], address = 0x000000011e6caa14, resolved, hit count = 0 </div><div class="line"> 6.3657: where = CoreRecents`-[CRRecentContactsLibrary init], address = 0x000000011e6dec27, unresolved, hit count = 0 Options: disabled</div></pre></td></tr></table></figure><h3 id="br-disable-enable"><a href="#br-disable-enable" class="headerlink" title="br disable/enable"></a>br disable/enable</h3><p>有些时候,我们可能根据具体场景设置了一些断点,但是临时开发的时候我们却不想让其进入断点。OK,我们可以去临时改写这个断点的状态为disable,当我们希望其开启的时候再设置为enable就可以了~,perfect?<br><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">(lldb) br list</div><div class="line">Current breakpoints:</div><div class="line">1: file = '/Users/hongzhizhao/Desktop/HZTest/Example/HZTest/HZViewController.m', line = 44, exact_match = 0, locations = 1, resolved = 1, hit count = 1</div><div class="line"> 1.1: where = HZTest_Example`-[HZViewController viewDidLoad] + 61 at HZViewController.m:44, address = 0x0000000105396c1d, resolved, hit count = 1 </div><div class="line">(lldb) br disable 1</div><div class="line">1 breakpoints disabled.</div><div class="line">(lldb) br list</div><div class="line">Current breakpoints:</div><div class="line">1: file = '/Users/hongzhizhao/Desktop/HZTest/Example/HZTest/HZViewController.m', line = 44, exact_match = 0, locations = 1 Options: disabled </div><div class="line"> 1.1: where = HZTest_Example`-[HZViewController viewDidLoad] + 61 at HZViewController.m:44, address = 0x0000000105396c1d, unresolved, hit count = 1</div></pre></td></tr></table></figure></p><h3 id="br-clear"><a href="#br-clear" class="headerlink" title="br clear"></a>br clear</h3><p>清理指定文件的指定行的断点<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">breakpoint clear -l <span class="meta">#lines# -f #filename#</span></div></pre></td></tr></table></figure></p><p>下面命令为</p><ul><li>查看当前list确实存在一个断点</li><li>使用clear命令清除name为5的断点</li><li>再次查看当前list,name为5确实被移除了<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">(lldb) br list</div><div class="line">Current breakpoints:</div><div class="line">5: file = '/Users/hongzhizhao/Desktop/HZTest/Example/HZTest/HZViewController.m', line = 48, exact_match = 0, locations = 1, resolved = 1, hit count = 0</div><div class="line"> 5.1: where = HZTest_Example`-[HZViewController testGlobalPoint] + 23 at HZViewController.m:48, address = 0x000000010da39d37, resolved, hit count = 0 </div><div class="line">(lldb) br clear -l 48 -f "HZViewController.m"</div><div class="line">1 breakpoints cleared:</div><div class="line">5: file = '/Users/hongzhizhao/Desktop/HZTest/Example/HZTest/HZViewController.m', line = 48, exact_match = 0, locations = 1, resolved = 1, hit count = 0</div><div class="line"></div><div class="line">(lldb) br list</div><div class="line">Current breakpoints:</div></pre></td></tr></table></figure></li></ul><h3 id="br-read-write"><a href="#br-read-write" class="headerlink" title="br read/write"></a>br read/write</h3><p>我们前面讲list的时候,我们讲到过一些存储断点相关的东西,<em>br write -f #file_path#</em>,-f标识断点文件的存储路径。<br>前面说了这么多的断点设置,其实都是运行期的设置,出了运行期,这些设置都是没有了的。所以read/write是一个持久保存的方案。<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">(lldb) br write -f ~/Desktop/a.log</div><div class="line">(lldb) br read -f ~/Desktop/a.log</div><div class="line">New breakpoints:</div><div class="line">Breakpoint <span class="number">4</span>: where = HZTest_Example`-[HZViewController viewDidLoad] + <span class="number">61</span> at HZViewController.m:<span class="number">44</span>, address = <span class="number">0x0000000105396c1d</span></div><div class="line">Breakpoint <span class="number">5</span>: where = HZTest_Example`-[HZAppDelegate application:didFinishLaunchingWithOptions:] + <span class="number">73</span> at HZAppDelegate.m:<span class="number">16</span>, address = <span class="number">0x0000000105396719</span></div><div class="line">Breakpoint <span class="number">6</span>: where = HZTest_Example`-[HZViewController testGlobalPoint] + <span class="number">23</span> at HZViewController.m:<span class="number">52</span>, address = <span class="number">0x0000000105396d27</span></div></pre></td></tr></table></figure></p><blockquote><p>Note: read并不能读取Xcode使用可视化设置的埋点,可能跟版本有关系吧,可视化埋的断点,是V2版本的。但是使用命令的并没有版本标识,可视化埋点是xml,使用命令保存的却是json文件。另外read并是一个会去替换的更新,而是一个读取后全部添加~</p></blockquote><h2 id="属性监听-watchpoint"><a href="#属性监听-watchpoint" class="headerlink" title="属性监听 watchpoint"></a>属性监听 watchpoint</h2><p>OK,我们看到了如何去设置一个函数的断点。但是有些时候,我们可能并不满足于函数,需要去监听一些属性的变化。那么有些人就会说,我直接监听set方法不就好了?那么就会有两种情况不会过set方法,1.直接调用的是<em>_</em> 2.这个属性压根就没有getter和setter方式是一个纯只指针的访问形式呢?你监听给我看看?<br>所以watchpoint就是这么一个神奇的东西,说白了他是监听一个内存地址的变化,所以他能够去捕获变量的变化。</p><blockquote><p>watchpoint 跟breakpoint的命令大致相同,只是一个操作函数,一个操作地址罢了</p></blockquote><h3 id="watchpoint-set"><a href="#watchpoint-set" class="headerlink" title="watchpoint set"></a>watchpoint set</h3><p><em>watchpoint set variable <var></var></em><br>跟设置函数一样的去设置一个断点<br><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">(lldb) watchpoint set variable self.window</div><div class="line">error: "self" is a pointer and . was used to attempt to access "window". Did you mean "self->window"?</div><div class="line">(lldb) watchpoint set variable self->_window</div><div class="line">Watchpoint created: Watchpoint 1: addr = 0x600000ccc110 size = 8 state = enabled type = w</div><div class="line"> watchpoint spec = 'self->_window'</div><div class="line"> new value: 0x00007fc8b780d600</div></pre></td></tr></table></figure></p><blockquote><p>由于watchpoint是一个内存块的监听策略,如果我们去写self.window这种表达式,实质是在设置[self setWindow:]方法,那么跟他的定义就大相径庭了。这边举个例子</p></blockquote><p><em>watchpoint set expression <expr></expr></em><br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">(lldb) watchpoint set expression <span class="number">0x00007fc8b780d600</span></div><div class="line">Watchpoint created: Watchpoint <span class="number">2</span>: addr = <span class="number">0x7fc8b780d600</span> size = <span class="number">8</span> state = enabled type = w</div><div class="line"> new value: <span class="number">4440870480</span></div></pre></td></tr></table></figure></p><p><em>-w统一参数</em><br>read/write/read_write<br>从字面意思上,我们就可以看出是监听的内容是读还是写还是读写一起监听。默认是写操作</p><blockquote><p>variable/expression的区别应用<br>variable必须是你进入了某次断点,才能够用使用当前栈中使用的属性去监听<br>expression 你可以通过一些逆向软件来计算偏移量+首地址的形式去得到最终的内存地址</p></blockquote><h3 id="watchpoint-list"><a href="#watchpoint-list" class="headerlink" title="watchpoint list"></a>watchpoint list</h3><p>这个我们就不多说了,查看当前的watchpoint列表<br><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">(lldb) watchpoint list</div><div class="line">Number of supported hardware watchpoints: 4</div><div class="line">Current watchpoints:</div><div class="line">Watchpoint 1: addr = 0x600000ccc110 size = 8 state = enabled type = w</div><div class="line"> watchpoint spec = 'self->_window'</div><div class="line"> new value: 0x00007fc8b780d600</div><div class="line">Watchpoint 2: addr = 0x7fc8b780d600 size = 8 state = enabled type = w</div><div class="line"> new value: 4440870480</div></pre></td></tr></table></figure></p><blockquote><p>这边我们看到使用expression和variable会得到两条结果</p></blockquote><h3 id="watchpoint-delete"><a href="#watchpoint-delete" class="headerlink" title="watchpoint delete"></a>watchpoint delete</h3><p>删除一个监测点<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">(lldb) watch delete <span class="number">1</span></div><div class="line"><span class="number">1</span> watchpoints deleted.</div></pre></td></tr></table></figure></p><h3 id="watchpoint-disable-enable"><a href="#watchpoint-disable-enable" class="headerlink" title="watchpoint disable/enable"></a>watchpoint disable/enable</h3><p>跟breakpoint一样,他也有一个临时关闭和打开的设置开关<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">(lldb) watchpoint disable <span class="number">3</span></div><div class="line"><span class="number">1</span> watchpoints disabled.</div><div class="line">(lldb) watchpoint enable <span class="number">3</span></div><div class="line"><span class="number">1</span> watchpoints enabled.</div></pre></td></tr></table></figure></p><h3 id="watchpoint-ignore"><a href="#watchpoint-ignore" class="headerlink" title="watchpoint ignore"></a>watchpoint ignore</h3><p>一个断点在真正被执行前需要被断点几次?<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">(lldb) watchpoint ignore -i <span class="number">3</span> <span class="number">3</span></div><div class="line"><span class="number">1</span> watchpoints ignored.</div></pre></td></tr></table></figure></p><h3 id="watchpoint-modify"><a href="#watchpoint-modify" class="headerlink" title="watchpoint modify"></a>watchpoint modify</h3><p>当条件为真的时候,采取真正执行断点<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">(lldb) watchpoint modify -c <span class="string">"self ==nil"</span> <span class="number">3</span></div><div class="line"><span class="number">1</span> watchpoints modified.</div></pre></td></tr></table></figure></p><h3 id="watchpoint-command"><a href="#watchpoint-command" class="headerlink" title="watchpoint command"></a>watchpoint command</h3><p>这个跟breakpoint command 没有任何区别,这边就不去说这个东西了。</p><h2 id="控制程序-thread"><a href="#控制程序-thread" class="headerlink" title="控制程序 thread"></a>控制程序 thread</h2><p>说完了如何去调试你的代码,但是有的时候这往往是不够的。我们希望于去控制我们的程序走向。例如一个函数的返回值,会改变程序的走向,那么我希望去改变他的返回值。这个时候我们就可以用到thread这个命令来辅佐我们。我们日常用的step调试,其实都是thread命令,只是Xcode帮我们可视化了罢了。</p><h3 id="thread-return"><a href="#thread-return" class="headerlink" title="thread return"></a>thread return</h3><p>return顾名思义,直接使函数return,return后面可以接表达式,来帮助我们返回一些有利于我们调试的东西<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">thread <span class="keyword">return</span> [<expr>]</div></pre></td></tr></table></figure></p><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">- (<span class="keyword">void</span>)testReturn {</div><div class="line"> <span class="built_in">NSString</span> *a = [<span class="keyword">self</span> testReturn] ? <span class="string">@"a"</span> : <span class="string">@"b"</span>;</div><div class="line">}</div><div class="line">- (<span class="built_in">BOOL</span>)testReturn {</div><div class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</div><div class="line">}</div><div class="line">(lldb) thread <span class="keyword">return</span> <span class="literal">false</span></div><div class="line">(lldb) po a</div><div class="line">b</div></pre></td></tr></table></figure><h3 id="thread-step-x"><a href="#thread-step-x" class="headerlink" title="thread step-x"></a>thread step-x</h3><p>我们平时的单步调试,其实都来自于这个命令<br><img src="/resources/8D06BDE637F8450D713B1DA6B5814F49.jpg" alt="IMAGE"><br>|命令|作用|<br>|–|–|<br>| thread countinue/c/countinue |等同于第一个按钮,标识程序继续运行|<br>| thread step-over/n/next | 等同于第二个按钮,单步运行 |<br>| thread step-in/s/step | 等同于第三个按钮,进入函数方法内部 源码级别|<br>| thread step-out/finish | 等同于第四个按钮,跳出当前函数 |</p><h3 id="thread-jump"><a href="#thread-jump" class="headerlink" title="thread jump"></a>thread jump</h3><p>OK,这玩意儿真的是一个黑魔法。看WWDC2018,断点移动的我一愣一愣的。来感受一下?<br><img src="/resources/64AC4E1386A9032ED73A4E7B68E4B08D.gif =1052x682" alt="未命名.gif"><br>其实这个东西很简单,老早就有了,只是我不知道罢了。<br><em>thread jump -l <linenum></linenum></em>上面的图是可视化的罢了,这个才是实质代码。-l 输入跳转的代码。</p><blockquote><p>Note:且用且珍惜,这东西有利有弊</p></blockquote><h3 id="其他东西"><a href="#其他东西" class="headerlink" title="其他东西"></a>其他东西</h3><ul><li>thread list 列出当前所有的线程</li><li>thread select 选择某个线程</li><li>therad until 传入一个行数,让程序执行到这行的时候暂停</li><li>thread info 输出当前线程的信息</li><li>thread backtrace 断点的函数调用堆栈,默认当前线程</li><li>thread plan 用来管理当前线程的执行计划</li></ul><blockquote><p>thread命令虽好,但是随意改变成语的走向,可能会引起一些不必要的麻烦</p></blockquote><h2 id="查看调用栈-frame"><a href="#查看调用栈-frame" class="headerlink" title="查看调用栈 frame"></a>查看调用栈 frame</h2><p>在我们调试过程中,我们经常能看到,控制台帮我们输出了当前的调用栈内容。其实他对应的就是frame命令。<br><img src="/resources/DEE2D7DF3DC22831BDEE012AE6FAE029.jpg" alt="IMAGE"><br>细心的同学可能会看到一个细节,红框1和红框2是对应的,红框2是用thread backtrace打印出来的。里面的frame其实就是我们这面的frame,我们可以通过<em>frame select <num></num></em>来使用代码切换当前运行的调用栈,跟你手动点现况1的没有任何区别<br><img src="/resources/1520BBE8DC414066748F4082CA2A4DDB.jpg" alt="IMAGE"><br><em>frame variable</em>用来打印当前调用栈内的所有属性<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">(lldb) frame variable</div><div class="line">(HZViewController *) <span class="keyword">self</span> = <span class="number">0x00007f8c6540ba90</span></div><div class="line">(SEL) _cmd = <span class="string">"viewDidLoad"</span></div><div class="line">(__NSCFConstantString *) a = <span class="number">0x000000010fffa128</span> <span class="string">@"b"</span></div></pre></td></tr></table></figure></p><p><em>frame info</em>打印当前栈的内容<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">(lldb) frame info</div><div class="line">frame <span class="meta">#0: 0x000000010fff7c64 HZTest_Example`-[HZViewController viewDidLoad](self=0x00007f8c6540ba90, _cmd=<span class="meta-string">"viewDidLoad"</span>) at HZViewController.m:52</span></div></pre></td></tr></table></figure></p><h2 id="help-apropos"><a href="#help-apropos" class="headerlink" title="help/apropos"></a>help/apropos</h2><p>当然说了这么多,很多时候我们肯定会忘掉的~啧啧啧,谁还能记住这么多呢,毕竟Xcode帮我们做好了可视化这么好的东西。<br>但是有些时候,我们却不得不去找这些命令,比如逆向的时候。2333333<br>help很简单,帮助我们去打印描述信息的。例如我想知道frame的相关信息,我们就可以这么操作,里面都帮我们写好了<br><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line">(lldb) help frame</div><div class="line"> Commands for selecting and examing the current thread's stack frames.</div><div class="line"></div><div class="line">Syntax: frame <subcommand> [<subcommand-options>]</div><div class="line"></div><div class="line">The following subcommands are supported:</div><div class="line"></div><div class="line"> info -- List information about the current stack frame in the current</div><div class="line"> thread.</div><div class="line"> select -- Select the current stack frame by index from within the</div><div class="line"> current thread (see 'thread backtrace'.)</div><div class="line"> variable -- Show variables for the current stack frame. Defaults to all</div><div class="line"> arguments and local variables in scope. Names of argument,</div><div class="line"> local, file static and file global variables can be</div><div class="line"> specified. Children of aggregate variables can be specified</div><div class="line"> such as 'var->child.x'.</div><div class="line"></div><div class="line">For more help on any particular subcommand, type 'help <command> <subcommand>'.</div></pre></td></tr></table></figure></p><p>apropos,是一个搜索命令,帮我们查找有哪些命令可以使用,例如下图这样,我们就能够答应出关于step的所有命令来帮助我们来使用<br><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">(lldb) apropos step</div><div class="line">The following commands may relate to 'step':</div><div class="line"> thread step-in -- Source level single step, stepping into calls. </div><div class="line"> Defaults to current thread unless specified.</div><div class="line"> thread step-inst -- Instruction level single step, stepping into calls. </div><div class="line"> Defaults to current thread unless specified.</div><div class="line"> thread step-inst-over -- Instruction level single step, stepping over calls. </div><div class="line"> Defaults to current thread unless specified.</div><div class="line"> thread step-out -- Finish executing the current stack frame and stop</div><div class="line"> after returning. Defaults to current thread unless</div><div class="line"> specified.</div><div class="line"> thread step-over -- Source level single step, stepping over calls. </div><div class="line"> Defaults to current thread unless specified.</div></pre></td></tr></table></figure></p><h2 id="Extension"><a href="#Extension" class="headerlink" title="Extension"></a>Extension</h2><h3 id="刷新UI"><a href="#刷新UI" class="headerlink" title="刷新UI"></a>刷新UI</h3><p>即使你改了UI的属性,因为程序暂停,你是无法刷新UI的,但是有个命令可以帮你,这样做你就可以及时的帮助你来改变UI的刷新,即使他在断点的状态<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">e <span class="built_in">CATransaction</span>.flush()</div></pre></td></tr></table></figure></p><h3 id="使用别人写好的脚本"><a href="#使用别人写好的脚本" class="headerlink" title="使用别人写好的脚本"></a>使用别人写好的脚本</h3><p>LLDB是支持使用别人写好的命令的,实质语法是使用python写的,这边就不多做介绍了。有兴趣可以去了解一下</p><h3 id="别名功能"><a href="#别名功能" class="headerlink" title="别名功能"></a>别名功能</h3><p>很多时候命令很长,我们记不住怎么办。比如那个刷新UI的命令。好长啊,在Session里面,这个人有个恶趣味用🚽来代替那个命令。<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">command alias 🚽 e <span class="built_in">CATransaction</span>.flush()</div></pre></td></tr></table></figure></p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="https://www.jianshu.com/p/268ecb3cf4c6" target="_blank" rel="noopener">iOS 常用的lldb命令</a><br><a href="https://juejin.im/entry/58c75b258ac24707201369c4" target="_blank" rel="noopener">使用 LLDB 调试 APP</a><br><a href="http://ios.jobbole.com/83393/" target="_blank" rel="noopener">熟练使用 LLDB,让你调试事半功倍</a></p>]]></content>
<summary type="html">
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>曾经有个笑话(身边的真相)。有次,我要回去写代码的时候呢,我无意间说了一句‘我们回去写BUG吧’。然后传为组内佳话。<br>BUG对于我们程序员来说是无所不在的,你想你的代码没有BUG,除非你没写代码。对于我们iOS or macOS 开发的同学们来说,苹果为我们准备了一个非常良好的Debug的生态环境(LLDB)这使得我们能够非常快速,有效的去排除,解决一些问题。</p>
<p>那么本文主要以本人平时的开发习惯/<a href="https://developer.apple.com/videos/play/wwdc2018/412/" target="_blank" rel="noopener">WWDC 2018 Session 412</a>/<a href="https://store.raywenderlich.com/products/advanced-apple-debugging-and-reverse-engineering" target="_blank" rel="noopener">Advaced Apple Debug</a>书中部分内容进行如下总结了一些LLDB的命令</p>
</summary>
</entry>
<entry>
<title>WWDC 2018 - 412 Advanced Debugging With Xcode and LLDB</title>
<link href="http://www.zhz.io/2018/06/10/WWDC-2018-412-Advanced-Debugging-With-Xcode-and-LLDB/"/>
<id>http://www.zhz.io/2018/06/10/WWDC-2018-412-Advanced-Debugging-With-Xcode-and-LLDB/</id>
<published>2018-06-10T02:19:44.000Z</published>
<updated>2018-06-11T01:28:40.775Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>刚做完了这个<a href="https://developer.apple.com/videos/play/wwdc2018/412/" target="_blank" rel="noopener">Advanced Debugging with Xcode and LLDB Session 412</a>的听力练习,来写个小总结(嗯,又可以愉快的开始装逼了)。<br>由于Swift写的不多,可能有几个小部分了解的并不是很全面</p><h2 id="增加了Swift-Debug的可靠性"><a href="#增加了Swift-Debug的可靠性" class="headerlink" title="增加了Swift Debug的可靠性"></a>增加了Swift Debug的可靠性</h2><h3 id="Swift第三方库导入方式不正确,导致的属性无法打印问题修正"><a href="#Swift第三方库导入方式不正确,导致的属性无法打印问题修正" class="headerlink" title="Swift第三方库导入方式不正确,导致的属性无法打印问题修正"></a>Swift第三方库导入方式不正确,导致的属性无法打印问题修正</h3><p>产生原因可以直接看这个<a href="https://juejin.im/post/5a52d7cdf265da3e4c07a004" target="_blank" rel="noopener">链接</a><br>实际产生的效果<br><img src="/resources/C377778880889F7F0B8897607C911E7A.jpg" alt="IMAGE"></p><h3 id="控制台Swift类型修正"><a href="#控制台Swift类型修正" class="headerlink" title="控制台Swift类型修正"></a>控制台Swift类型修正</h3><p><img src="/resources/CDA3BBFB82087B559010B162AAF25A8F.jpg" alt="IMAGE"><br><a id="more"></a></p><h2 id="Debug技巧"><a href="#Debug技巧" class="headerlink" title="Debug技巧"></a>Debug技巧</h2><p>之前看了会儿Advanced Apple Debugging这本书,其实好像上面很多都有写。</p><h3 id="多一个工作区的Debug-Tab给开发者来调试"><a href="#多一个工作区的Debug-Tab给开发者来调试" class="headerlink" title="多一个工作区的Debug Tab给开发者来调试"></a>多一个工作区的Debug Tab给开发者来调试</h3><p>当你进入Debug的时候会多出一个Tab来,当然这个并不是默认打开的,你可以去设置里面进行打开<br><img src="/resources/BBC36F839054F738B10C5FD527CFF6A0.jpg" alt="IMAGE"><br><img src="/resources/C56CA051961A7A531A13F4D930D5A098.jpg" alt="IMAGE"><br>Xcode提供了很多选项可供你选择,在Build/Test/Run等的场景开始或者是暂停的时候弹出新的Tab,可定制型还是挺高的。</p><h3 id="Expression交换函数结果"><a href="#Expression交换函数结果" class="headerlink" title="Expression交换函数结果"></a>Expression交换函数结果</h3><p>利用expression命令,简写为e,来修改属性的值<br><img src="/resources/285E4FC01E01462E29BBD1BE7DCBF7FD.jpg" alt="IMAGE"><br>当然如果你不想每次运行的时候都这么来敲一遍的话,你可以这么玩<br><img src="/resources/BE23B42AD0FC3BFF07F41C9EAF378E18.jpg" alt="IMAGE"><br>OK,甚至你忘记写某句代码了,但是编译又贼慢,你可以学习一下视频里的这个操作<br><img src="/resources/BC89285073DADF1D47C47A249F9898A9.jpg" alt="IMAGE"><br>图中他少设置了一个Delegate,但是他懒啊,直接用断点添加上,然后假装写一行代码在那(其实他并没有编译咯)。但是这样可以假装写了一个代理赋值。Amazing?</p><h3 id="breakpoint-set-–one-shot-true"><a href="#breakpoint-set-–one-shot-true" class="headerlink" title="breakpoint set –one-shot true"></a>breakpoint set –one-shot true</h3><p>OK,看到这个命令一开始是懵逼的,什么鬼东西。但是看到下图应该并不会很陌生,这个是对于UILabel的text字段的全局断点,但是系统的所有设置都会起作用。所以调试的时候还要开关断点啊什么的太麻烦了。所以可以利用上面的这个命令来进行一个修正优化。<br><img src="/resources/0C09C7A80D42093D1A8BACABE50080A8.jpg" alt="IMAGE"><br>我想要在if的判断后面拦截一次-[UILabel setText:]我就可以这么做了<br><img src="/resources/04B30B8BF791028055DA84C3E9FF1A4A.jpg" alt="IMAGE"><br><img src="/resources/4DC633FC7E0FAF3D67388A288A115420.jpg" alt="IMAGE"></p><blockquote><p>Note: 这边写了one shot 所以我在想有没有断两次的操作,但是在同一个断点设置两次command是没有起作用的。<br>–name只是这个命令的其中一个用法,还有其他用法大家自己探索</p></blockquote><h3 id="断点移动"><a href="#断点移动" class="headerlink" title="断点移动"></a>断点移动</h3><p>ヾ(。`Д´。)我擦这是什么操作?来段Gif来表达一下我的忧伤,恕我年少无知。<br><img src="/resources/64AC4E1386A9032ED73A4E7B68E4B08D.gif" alt="未命名.gif"><br>!!!运行中的断点竟然还能移动,关键是还能够回退执行。OK,回到正题,这应该是之前就有的吧,我只是不知道而已。视频中介绍了另外一种姿势,我们来学习一下。图中,这句jump的动画被泰国了,执行了手动执行的动画,并且为false。<br><img src="/resources/8561122F2D0DFE9BFF98E8ABF62EDFEA.jpg" alt="IMAGE"></p><h3 id="watch-point-属性的监听"><a href="#watch-point-属性的监听" class="headerlink" title="watch point 属性的监听"></a>watch point 属性的监听</h3><p>OK,这就像是运行期的KVO 中set炒作一样理解就OK了。<br>当我们在属性列表里面Watch了之后,就可以捕获到这个属性的变化了<br><img src="/resources/96CC650A97A1106370FBF1FC7D87AEB7.jpg" alt="IMAGE"><br><img src="/resources/E5F2CE69A63044C3CF887BBC228A8778.jpg" alt="IMAGE"></p><h3 id="老生常谈的Swift调用OC"><a href="#老生常谈的Swift调用OC" class="headerlink" title="老生常谈的Swift调用OC"></a>老生常谈的Swift调用OC</h3><p>在用Swift的时候,有些OC函数是无法在LLDB中使用的。like view.recursiveDescription。<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">expression -l objc -O -- <expr></div></pre></td></tr></table></figure></p><p><img src="/resources/5DFB3DE6902432B7376AB079104F19E2.jpg" alt="IMAGE"></p><h3 id="断点中,界面刷新"><a href="#断点中,界面刷新" class="headerlink" title="断点中,界面刷新"></a>断点中,界面刷新</h3><p>有时候我们进入了断点,在断点的时候,修改了Frame,但是因为被暂停UI是不会刷新的,所以我们可以手动去调用刷新<em>expression CATransaction.flush()</em>已达到界面刷新的效果</p><h3 id="别名"><a href="#别名" class="headerlink" title="别名"></a>别名</h3><p>这个没什么好讲的吧,给刚才的命令取一个简短的名字方便调用罢了<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">command alias poc expression -l objc -O --</div><div class="line">command alias 🚽 expression -l objc -- (void)[CATransaction flush]</div></pre></td></tr></table></figure></p><p>(用🚽来刷新也是骚的)</p><h3 id="用LLDB-Python库来自定义命令"><a href="#用LLDB-Python库来自定义命令" class="headerlink" title="用LLDB-Python库来自定义命令"></a>用LLDB-Python库来自定义命令</h3><p>这部分还是参考别人的文章看看吧<a href="http://southpeak.github.io/2015/01/25/tool-lldb/" target="_blank" rel="noopener">LLDB调试器使用简介</a>,反正以前就有了。</p><h3 id="Xib的约束拷贝"><a href="#Xib的约束拷贝" class="headerlink" title="Xib的约束拷贝"></a>Xib的约束拷贝</h3><p>断点的时候进入ViewDebug可以点击相关约束后进行拷贝。在命令行进行粘贴,这样你就能拿到约束对象的地址,进行调试。改完之后要记得刷新界面哦~<br><img src="/resources/C946596F700C273F1E9207A19E9CC749.jpg" alt="IMAGE"><br><img src="/resources/00A0909FA1EA8064E74D401361AFC5A7.jpg" alt="IMAGE"></p><h3 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h3><p>剩下的是dark mode和color的东西,并不是很有兴趣,自行观赏</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>这些都只是入门操作,<a href="https://store.raywenderlich.com/products/advanced-apple-debugging-and-reverse-engineering" target="_blank" rel="noopener">进阶入门</a>更多的这本书里面写的更详细一点吧。<br>这些都是提高自己工作效率的东西,但是关键还是在于理解和实战,只有配合好了才能够发挥出最大的效益。单单只有了解就只是个装饰品罢了。 </p>]]></content>
<summary type="html">
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>刚做完了这个<a href="https://developer.apple.com/videos/play/wwdc2018/412/" target="_blank" rel="noopener">Advanced Debugging with Xcode and LLDB Session 412</a>的听力练习,来写个小总结(嗯,又可以愉快的开始装逼了)。<br>由于Swift写的不多,可能有几个小部分了解的并不是很全面</p>
<h2 id="增加了Swift-Debug的可靠性"><a href="#增加了Swift-Debug的可靠性" class="headerlink" title="增加了Swift Debug的可靠性"></a>增加了Swift Debug的可靠性</h2><h3 id="Swift第三方库导入方式不正确,导致的属性无法打印问题修正"><a href="#Swift第三方库导入方式不正确,导致的属性无法打印问题修正" class="headerlink" title="Swift第三方库导入方式不正确,导致的属性无法打印问题修正"></a>Swift第三方库导入方式不正确,导致的属性无法打印问题修正</h3><p>产生原因可以直接看这个<a href="https://juejin.im/post/5a52d7cdf265da3e4c07a004" target="_blank" rel="noopener">链接</a><br>实际产生的效果<br><img src="/resources/C377778880889F7F0B8897607C911E7A.jpg" alt="IMAGE"></p>
<h3 id="控制台Swift类型修正"><a href="#控制台Swift类型修正" class="headerlink" title="控制台Swift类型修正"></a>控制台Swift类型修正</h3><p><img src="/resources/CDA3BBFB82087B559010B162AAF25A8F.jpg" alt="IMAGE"><br>
</summary>
<category term="WWDC" scheme="http://www.zhz.io/tags/WWDC/"/>
</entry>
<entry>
<title>记一次 Alfred Workflow中Confluence改造</title>
<link href="http://www.zhz.io/2018/05/18/%E8%AE%B0%E4%B8%80%E6%AC%A1-Alfred-Workflow%E4%B8%ADConfluence%E6%94%B9%E9%80%A0/"/>
<id>http://www.zhz.io/2018/05/18/记一次-Alfred-Workflow中Confluence改造/</id>
<published>2018-05-18T06:08:18.000Z</published>
<updated>2018-05-18T06:10:37.918Z</updated>
<content type="html"><![CDATA[<h2 id="起因"><a href="#起因" class="headerlink" title="起因"></a>起因</h2><p>Confluence对于企业来说是一个非常有用的文档管理工具,但是每次我在使用的时候,我都要去进行一遍文档空间的目录查找。<br>虽然提供了搜索功能,但是搜索的延时,使用便捷度,都存在一定的问题。(哦,对了我们的Confluence是比较低版本的,太老了,一次搜索50条)<br>那么,想了一下如何去解决这个问题呢,Mac中万能的Alfred Workflow就是用来解决一系列的工作效率的问题的。<br>所以我在尝试能不能用Alfred Workflow来进行文档标题的搜索。<br>OK做这个事情之前,先找下目前有没有已经存在的包拿来直接使用。<br>找到了这个<a href="https://github.com/skleinei/alfred-confluence" target="_blank" rel="noopener">Alfred Workflow</a>,但是按照他的设置却发现搜索不了结果。<br><a id="more"></a></p><h2 id="解决"><a href="#解决" class="headerlink" title="解决"></a>解决</h2><p>OK,我们都知道Alfred Workflow是用python写的,- -好吧,Python早忘光了,反正语言嘛,80%都一样的,就随便改改好了。<br>OK,在Debug信息中我们发现,大神写的脚本的链接直接报了404<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">14:28:40 alfred-confluence.py:99 DEBUG Quick Search URL: http://host/json/contentnamesearch.action/rest/quicknav/1/search?os_authType=basic&query=ios</div><div class="line">14:28:40 workflow.py:2243 ERROR HTTP Error 404: Not Found</div></pre></td></tr></table></figure></p><p>那么我们来找下公司的搜索按钮的URL来试试?<br><a href="http://host/dosearchsite.action?spaceSearch=false&queryString=" target="_blank" rel="noopener">http://host/dosearchsite.action?spaceSearch=false&queryString=</a><br>好像也有问题,因为他输出的是Html内容,我并不能去解析Html的东西,哇贼烦,看看有没有Rest API可以直接调用。<br>既然是Confluence,应该有API文档的吧,找到公司的Confluence版本,然后Google了一番,找到了这个<a href="https://docs.atlassian.com/DAC/rest/confluence/4.3.7.html" target="_blank" rel="noopener">链接</a>看到了图片中的这个,哇这个不就是我们想要的么。<br><img src="/resources/C11BDD5598E2284CFDA97A55806AC3EF.jpg" alt="IMAGE"><br><img src="/resources/C3533F3284F01E0B5AB879A8E173EEFD.jpg" alt="IMAGE"><br>OK,我们直接把链接给复制进去,改了下host。然后进行DEBUG,哇爆炸,还是不行。拿到的参数不是JSON,大神写的无法解析。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">14:36:21 alfred-confluence.py:99 DEBUG Quick Search URL: http://hots/rest/prototype/1/search?os_authType=basic&query=ios&spaceKey=xxxx&pageSize=10</div><div class="line">14:36:25 workflow.py:2243 ERROR No JSON object could be decoded</div></pre></td></tr></table></figure></p><p>OK,我们去直接用阅览器访问链接(因为阅览器有登录Cookie,所以可以直接进行访问),得到的数据是XML,哇爆炸,贼不想搞这东西。<br>但是没办法,还是要用的。<br>那么问题处在XML没办法解析,得不到最终的数据。那么就来解析XML吧,- -我个人喜欢JSON,直接去Google了一个XML转JSON的库,来做(主要是,试了好几个自己写的转换莫名其妙的有问题)。OK,安装上xml2Json库,很快我们就可以得到了Json串,然后Json转字典。OK我们就可以直接用KVC的方式把pageId和title给拿出来。然后把数据添加到Alfred的FeedBack之中。<br>OK,我们想要的数据就出来的。</p><h2 id="优化"><a href="#优化" class="headerlink" title="优化"></a>优化</h2><p>受限于公司Confluence的渣渣访问速度,只能针对于搜索条数和访问空间进行限制。<br>设置完之后的访问速度大概是1.5S左右,达到了个人理想的访问速度,还是比较能够接受的。</p>]]></content>
<summary type="html">
<h2 id="起因"><a href="#起因" class="headerlink" title="起因"></a>起因</h2><p>Confluence对于企业来说是一个非常有用的文档管理工具,但是每次我在使用的时候,我都要去进行一遍文档空间的目录查找。<br>虽然提供了搜索功能,但是搜索的延时,使用便捷度,都存在一定的问题。(哦,对了我们的Confluence是比较低版本的,太老了,一次搜索50条)<br>那么,想了一下如何去解决这个问题呢,Mac中万能的Alfred Workflow就是用来解决一系列的工作效率的问题的。<br>所以我在尝试能不能用Alfred Workflow来进行文档标题的搜索。<br>OK做这个事情之前,先找下目前有没有已经存在的包拿来直接使用。<br>找到了这个<a href="https://github.com/skleinei/alfred-confluence" target="_blank" rel="noopener">Alfred Workflow</a>,但是按照他的设置却发现搜索不了结果。<br>
</summary>
</entry>
<entry>
<title>OC 疯人院 - 引用计数</title>
<link href="http://www.zhz.io/2018/05/05/OC-%E7%96%AF%E4%BA%BA%E9%99%A2-%E5%BC%95%E7%94%A8%E8%AE%A1%E6%95%B0/"/>
<id>http://www.zhz.io/2018/05/05/OC-疯人院-引用计数/</id>
<published>2018-05-05T07:08:33.000Z</published>
<updated>2018-05-05T07:12:52.845Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>早在MRC时代,引用计数是由我们开发者自己控制的,但是到了ARC时代,编译器帮我们做了引用计数管理这一步。这边的描述,就是为了解密,引用计数是如何实现的。如果不知道什么是引用计数,请参考其他人的文章~</p><h2 id="前提"><a href="#前提" class="headerlink" title="前提"></a>前提</h2><p>本文的所有内容均已下述标准下进行的实验</p><ul><li><a href="https://github.com/RetVal/objc-runtime" target="_blank" rel="noopener">objc源码(可编译版本),tag号为723版本</a></li><li><a href="https://github.com/nianran/objc-runtime/tree/Feature/translation" target="_blank" rel="noopener">个人实验的路径,包含部分Demo,以及源码注解,欢迎指教</a></li><li>Xcode Version 9.3 (9E145)</li><li>Hopper Disassembler v4</li><li>以模拟器为准的话是x86_64,不同架构会有区别</li><li>本次实验内容,不涉及指针优化(TaggedPointer)<a id="more"></a></li></ul><h2 id="ISA"><a href="#ISA" class="headerlink" title="ISA"></a>ISA</h2><p>在了解引用计数前,我们来回顾一下ISA的isa_t的结构体中部分内容<br><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line">// 由于我们的实验是基于x86_64的这边就列举了x86_64的结构体</div><div class="line"> struct {</div><div class="line"> uintptr_t nonpointer : 1;</div><div class="line"> uintptr_t has_assoc : 1;</div><div class="line"> uintptr_t has_cxx_dtor : 1;</div><div class="line"> uintptr_t shiftcls : 44; // MACH_VM_MAX_ADDRESS 0x7fffffe00000</div><div class="line"> uintptr_t magic : 6;</div><div class="line"> uintptr_t weakly_referenced : 1;</div><div class="line"> uintptr_t deallocating : 1;</div><div class="line"> uintptr_t has_sidetable_rc : 1;</div><div class="line"> uintptr_t extra_rc : 8;</div><div class="line"># define RC_ONE (1ULL<<56)</div><div class="line"># define RC_HALF (1ULL<<7)</div><div class="line"> };</div></pre></td></tr></table></figure></p><p>其中,我们可以看到的是有两个字段,我们先来做一下简单的了解<em>extra_rc</em>和<em>has_sidetable_rc</em>,这两个变量就是用来存储引用计数的关键内容。<br><em>extra_rc</em> : 用8位来存储引用计数大小 ->256<br><em>has_sidetable_rc</em> : 如果<em>extra_rc</em>不够存储了,那么就会标记<em>has_sidetable_rc</em>为1,并且存储到另外一个Table表中</p><h2 id="retain"><a href="#retain" class="headerlink" title="retain"></a>retain</h2><h3 id="描述"><a href="#描述" class="headerlink" title="描述"></a>描述</h3><p>用于引用计数加1的操作的函数<br>retain函数内实际是调用rootRetain函数。</p><h3 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h3><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div></pre></td><td class="code"><pre><div class="line">ALWAYS_INLINE <span class="keyword">id</span> </div><div class="line">objc_object::rootRetain(<span class="keyword">bool</span> tryRetain, <span class="keyword">bool</span> handleOverflow)</div><div class="line">{</div><div class="line"></div><div class="line"> <span class="keyword">bool</span> sideTableLocked = <span class="literal">false</span>;</div><div class="line"> <span class="keyword">bool</span> transcribeToSideTable = <span class="literal">false</span>;</div><div class="line"></div><div class="line"> isa_t oldisa;</div><div class="line"> isa_t newisa;</div><div class="line"></div><div class="line"> <span class="keyword">do</span> {</div><div class="line"> transcribeToSideTable = <span class="literal">false</span>;</div><div class="line"> oldisa = LoadExclusive(&isa.bits);</div><div class="line"> newisa = oldisa;</div><div class="line"> <span class="comment">// carry Flag</span></div><div class="line"> uintptr_t carry;</div><div class="line"> <span class="comment">// 先存储在extra_rc字段中</span></div><div class="line"> <span class="comment">// carry标识超过了256之后会存储在其他地方 并且extra_rc减半 返回carry 为1</span></div><div class="line"> <span class="comment">// 这个代码没找到源码,网址是别人的研究成果</span></div><div class="line"> <span class="comment">// https://www.jianshu.com/p/18c3e88dfbf1</span></div><div class="line"> newisa.bits = addc(newisa.bits, RC_ONE, <span class="number">0</span>, &carry); <span class="comment">// extra_rc++</span></div><div class="line"> <span class="keyword">if</span> (slowpath(carry)) {</div><div class="line"> <span class="comment">// newisa.extra_rc++ overflowed</span></div><div class="line"> <span class="keyword">if</span> (!handleOverflow) {</div><div class="line"> ClearExclusive(&isa.bits);</div><div class="line"> <span class="comment">// carry为1的时候,重新进入rootRetain函数,进入SideTable的加入</span></div><div class="line"> <span class="keyword">return</span> rootRetain_overflow(tryRetain);</div><div class="line"> }</div><div class="line"> <span class="keyword">if</span> (!tryRetain && !sideTableLocked) sidetable_lock();</div><div class="line"> sideTableLocked = <span class="literal">true</span>;</div><div class="line"> transcribeToSideTable = <span class="literal">true</span>;</div><div class="line"> newisa.extra_rc = RC_HALF;</div><div class="line"> newisa.has_sidetable_rc = <span class="literal">true</span>;</div><div class="line"> }</div><div class="line"> <span class="comment">// 存储新的bits进入isa</span></div><div class="line"> } <span class="keyword">while</span> (slowpath(!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)));</div><div class="line"></div><div class="line"> <span class="keyword">if</span> (slowpath(transcribeToSideTable)) {</div><div class="line"> <span class="comment">// 存储一半的容量到SideTable中,x86下即为128</span></div><div class="line"> sidetable_addExtraRC_nolock(RC_HALF);</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">if</span> (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock();</div><div class="line"> <span class="keyword">return</span> (<span class="keyword">id</span>)<span class="keyword">this</span>;</div><div class="line">}</div><div class="line">NEVER_INLINE <span class="keyword">id</span> </div><div class="line">objc_object::rootRetain_overflow(<span class="keyword">bool</span> tryRetain)</div><div class="line">{</div><div class="line"> <span class="keyword">return</span> rootRetain(tryRetain, <span class="literal">true</span>);</div><div class="line">}</div></pre></td></tr></table></figure><h3 id="代码-1"><a href="#代码-1" class="headerlink" title="代码"></a>代码</h3><p>有点长~简化成流程图,放到这边了,代码就不贴了。</p><h3 id="流程图"><a href="#流程图" class="headerlink" title="流程图"></a>流程图</h3><p><img src="/resources/C8D636CA6528A23616F47E330C0BE509.png" alt="retain.png"></p><blockquote><p>流程图为了方便区分,分了两条线。</p></blockquote><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><ul><li>总的来说<em>extra_rc</em>和<em>has_sidetable_rc</em>加上SideTable来控制引用计数的</li><li><em>extra_rc</em>存储量比较小的计数标识,溢出则移出一部分交由另外两个值</li><li><em>has_sidetable_rc</em>只是标识是否有SideTable的存在,具体溢出的值还是存储在SideTable中的</li><li><em>SideTable</em>,用来存储溢出的引用计数的Map的,当然他还有其他作用,这边就只说明这个</li></ul><blockquote><p>系统是允许重写retain/release这些函数的,并且在调用真正的rootRetain之前会判断有没有实现自定义的retain函数<br>SideTable的具体实现部分没有具体源码就不深究了。每个对象都有自己的SideTable是独立的,只要了解至此我觉得已经够了</p></blockquote><h2 id="release"><a href="#release" class="headerlink" title="release"></a>release</h2><h3 id="描述-1"><a href="#描述-1" class="headerlink" title="描述"></a>描述</h3><p>用于引用计数减1的函数,当引用计数真正为0的时候,则调用真正的Release</p><h3 id="流程图-1"><a href="#流程图-1" class="headerlink" title="流程图"></a>流程图</h3><p><img src="/resources/32AEF7ED85A3955A807C73BAB2D37C72.png" alt="release.png"></p><h3 id="总结-1"><a href="#总结-1" class="headerlink" title="总结"></a>总结</h3><ul><li>引用计数先从<em>extra_rc</em>中进行减1操作</li><li>如果<em>extra_rc</em>为0,那么检查<em>SideTable</em>,如果<em>SideTable</em>中有数值,则取出<em>extra_rc</em>容量一半的数值,存入<em>extra_rc</em>,再做减法</li><li>当<em>extra_rc</em>和<em>SideTable</em>的引用计数都为0的时候,利用<em>msgSend</em>函数进行真正的dealloc函数的调用</li></ul><h2 id="引用计数总结"><a href="#引用计数总结" class="headerlink" title="引用计数总结"></a>引用计数总结</h2><p>引用计数的本质就是一个属性的加减以及一个Table的存储,并没有什么比较高深的东西。只当了解即可~</p>]]></content>
<summary type="html">
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>早在MRC时代,引用计数是由我们开发者自己控制的,但是到了ARC时代,编译器帮我们做了引用计数管理这一步。这边的描述,就是为了解密,引用计数是如何实现的。如果不知道什么是引用计数,请参考其他人的文章~</p>
<h2 id="前提"><a href="#前提" class="headerlink" title="前提"></a>前提</h2><p>本文的所有内容均已下述标准下进行的实验</p>
<ul>
<li><a href="https://github.com/RetVal/objc-runtime" target="_blank" rel="noopener">objc源码(可编译版本),tag号为723版本</a></li>
<li><a href="https://github.com/nianran/objc-runtime/tree/Feature/translation" target="_blank" rel="noopener">个人实验的路径,包含部分Demo,以及源码注解,欢迎指教</a></li>
<li>Xcode Version 9.3 (9E145)</li>
<li>Hopper Disassembler v4</li>
<li>以模拟器为准的话是x86_64,不同架构会有区别</li>
<li>本次实验内容,不涉及指针优化(TaggedPointer)
</summary>
<category term="OC 疯人院" scheme="http://www.zhz.io/tags/OC-%E7%96%AF%E4%BA%BA%E9%99%A2/"/>
</entry>
<entry>
<title>OC 疯人院 - Autorelease</title>
<link href="http://www.zhz.io/2018/05/05/OC-%E7%96%AF%E4%BA%BA%E9%99%A2-Autorelease/"/>
<id>http://www.zhz.io/2018/05/05/OC-疯人院-Autorelease/</id>
<published>2018-05-05T07:07:16.000Z</published>
<updated>2018-05-08T09:59:30.761Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>Autorelease->苹果在内存管理方面的优化策略,旨在方便开发者更加良好的管理对象的内存空间,做到OC对象无需开发者手动进行释放操作。<br>其是在OC从MRC转变为ARC中间形成的产物。</p><h2 id="前提"><a href="#前提" class="headerlink" title="前提"></a>前提</h2><p>本文的所有内容均已下述标准下进行的实验</p><ul><li><a href="https://github.com/RetVal/objc-runtime" target="_blank" rel="noopener">objc源码(可编译版本),tag号为723版本</a></li><li><a href="https://github.com/nianran/objc-runtime/tree/Feature/translation" target="_blank" rel="noopener">个人实验的路径,包含部分Demo,以及源码注解,欢迎指教</a></li><li>Xcode Version 9.3 (9E145)</li><li>Hopper Disassembler v4</li></ul><h2 id="从最熟悉的地方开始"><a href="#从最熟悉的地方开始" class="headerlink" title="从最熟悉的地方开始"></a>从最熟悉的地方开始</h2><p>main函数,敲过代码的都知道,很多语言都是从main函数开始的(写个Hello,world!,哈哈哈)<br>OK,那么我们从main函数开始,下面贴的就是main函数在编译前和编译后(反编译出来)的对比<br><a id="more"></a><br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 原代码</span></div><div class="line"><span class="keyword">int</span> main(<span class="keyword">int</span> argc, <span class="keyword">const</span> <span class="keyword">char</span> * argv[]) {</div><div class="line"> <span class="keyword">@autoreleasepool</span> {</div><div class="line"> testRetainCount();</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> <span class="number">0</span>;</div><div class="line">}</div><div class="line"><span class="comment">// Hopper 反编译后的代码</span></div><div class="line"><span class="keyword">int</span> _main(<span class="keyword">int</span> arg0, <span class="keyword">int</span> arg1) {</div><div class="line"> <span class="keyword">void</span> *context = objc_autoreleasePoolPush();</div><div class="line"> _testRetainCount();</div><div class="line"> objc_autoreleasePoolPop(context);</div><div class="line"> <span class="keyword">return</span> <span class="number">0</span>;</div><div class="line">}</div></pre></td></tr></table></figure></p><p>从中,我们可以看到@autoreleasepool{}这个东西实质上是被替换成了以前以后的两个函数</p><ul><li>objc_autoreleasePoolPush()</li><li>objc_autoreleasePoolPop(void *)<br>OK,在讲这两个函数之前,我们有必要先了解一下这么一个类AutoreleasePoolPage</li></ul><h2 id="AutoreleasePoolPage"><a href="#AutoreleasePoolPage" class="headerlink" title="AutoreleasePoolPage"></a>AutoreleasePoolPage</h2><h3 id="AutoreleasePoolPage类的简介"><a href="#AutoreleasePoolPage类的简介" class="headerlink" title="AutoreleasePoolPage类的简介"></a>AutoreleasePoolPage类的简介</h3><p>AutoreleasePoolPage(自动释放池页?这个翻译好奇怪,我们这边就约定为释放页/page吧)。<br>那么这个东西是什么呢,它其实是用来管理需要自动释放对象的一个<a href="https://zh.wikipedia.org/zh-hans/%E5%8F%8C%E5%90%91%E9%93%BE%E8%A1%A8" target="_blank" rel="noopener">双向链表</a><br>OK,我们盗个图,嘻嘻<a href="https://blog.sunnyxx.com/2014/10/15/behind-autorelease/" target="_blank" rel="noopener">来源</a><br><img src="http://ww2.sinaimg.cn/mw690/51530583gw1elj2ugt21wj20f109m3zl.jpg" alt="AutoreleasePoolPage类"><br>简单介绍一下元素</p><ul><li>next:作为一个游标,用来指定下一个会被添加进来的对象的起始地址</li><li>thread:当前page所在的线程(page和线程是一一对应的关系)</li><li>child/parent:双向链表的福字节点,没什么好说明的。</li><li>Size:一个page被宏定义的SIZE限制在了4096字节,所以超过4K的话,就会创建新的childpage来存储</li><li>depth:链表深度,根链表为0,后续持续+1</li></ul><p>OK,看完了图,我们来看下AutoreleasePoolPage的构造函数<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 构造函数</span></div><div class="line">AutoreleasePoolPage(AutoreleasePoolPage *newParent) </div><div class="line"> : magic(), next(begin()), thread(pthread_self()),</div><div class="line"> parent(newParent), child(<span class="literal">nil</span>), </div><div class="line"> depth(parent ? <span class="number">1</span>+parent->depth : <span class="number">0</span>), </div><div class="line"> hiwat(parent ? parent->hiwat : <span class="number">0</span>)</div><div class="line">{ </div><div class="line"> <span class="keyword">if</span> (parent) {</div><div class="line"> parent->check();</div><div class="line"> assert(!parent->child);</div><div class="line"> parent->unprotect();</div><div class="line"> parent->child = <span class="keyword">this</span>;</div><div class="line"> parent->protect();</div><div class="line"> }</div><div class="line"> protect();</div><div class="line">}</div><div class="line"><span class="comment">// begin函数</span></div><div class="line"><span class="keyword">id</span> * begin() {</div><div class="line"> <span class="keyword">return</span> (<span class="keyword">id</span> *) ((uint8_t *)<span class="keyword">this</span>+<span class="keyword">sizeof</span>(*<span class="keyword">this</span>));</div><div class="line">}</div></pre></td></tr></table></figure></p><blockquote><p>Note: 在C++中,冒号后面是赋值,next(begin())就是next=begin()</p></blockquote><p> 最后,在内存中的表现就会如下图所示,<a href="https://blog.sunnyxx.com/2014/10/15/behind-autorelease/" target="_blank" rel="noopener">来源</a><br><img src="http://ww2.sinaimg.cn/mw690/51530583gw1elj5gvphtqj20dy0cx756.jpg" alt="AutoreleasePoolPage内存中的表现形式"><br>结合代码和上图,我们可以知道,新的page的next指针被指向begin的位置(实例内存之后)</p><h3 id="hotPage"><a href="#hotPage" class="headerlink" title="hotPage()"></a>hotPage()</h3><p>什么是hotPage呢?<br>hotPage就是当前内存中最活跃的那一页,被用来存储当前线程快速存储的对象的时候使用的。<br>获取的方式TLS(Thread Local Storage),这块并不懂,也不想深究,简单明了的理解就是一个全局表,用key来换取value,value就是当前的活跃页。<br>如有兴趣了解TLS参考<a href="https://blog.sunnyxx.com/2014/10/15/behind-autorelease/" target="_blank" rel="noopener">大神博客</a></p><h2 id="对象是何时被加入到page中的呢?"><a href="#对象是何时被加入到page中的呢?" class="headerlink" title="对象是何时被加入到page中的呢?"></a>对象是何时被加入到page中的呢?</h2><h3 id="实验"><a href="#实验" class="headerlink" title="实验"></a>实验</h3><p>在开始讲对象是怎么被加入页中,我们从一个实验中来看<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line"></div><div class="line"><span class="keyword">void</span> * -[Person person](<span class="keyword">void</span> * <span class="keyword">self</span>, <span class="keyword">void</span> * _cmd) {</div><div class="line"> var_18 = [[Person alloc] init];</div><div class="line"> var_28 = [var_18 <span class="keyword">retain</span>];</div><div class="line"> objc_storeStrong(var_18, <span class="number">0x0</span>);</div><div class="line"> rax = [var_28 autorelease];</div><div class="line"> <span class="keyword">return</span> rax;</div><div class="line">}</div><div class="line"></div><div class="line">- (Person *)person {</div><div class="line"> Person *person = [[Person alloc] init];</div><div class="line"> <span class="keyword">return</span> person;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">void</span> -[Person person1](<span class="keyword">void</span> * <span class="keyword">self</span>, <span class="keyword">void</span> * _cmd) {</div><div class="line"> rdi = var_18;</div><div class="line"> [[Person alloc] init];</div><div class="line"> objc_storeStrong(rdi, <span class="number">0x0</span>);</div><div class="line"> <span class="keyword">return</span>;</div><div class="line">}</div><div class="line">- (<span class="keyword">void</span>)person1 {</div><div class="line"> Person *person = [[Person alloc] init];</div><div class="line">}</div></pre></td></tr></table></figure></p><h3 id="解释"><a href="#解释" class="headerlink" title="解释"></a>解释</h3><p>对比两个函数person和person1函数,从反编译器的代码中我们可以看出来,对于没有超出作用于范围函数的对象,是不会被加入到autoReleasePool中的,但是如果会超出了函数作用于范围的值,那么编译器会帮助你主动加上autoRelease这个函数的。<br>那么我们来看一下autoRelease的函数调用关系</p><ul><li>[self autorelease]<ul><li>((id)self)->rootAutorelease();<ul><li>rootAutorelease2();<ul><li>AutoreleasePoolPage::autorelease((id)this); // this就是oc中的self<ul><li>autoreleaseFast(obj) // obj就是传入的self<ul><li>page->add(obj)<figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">static</span> <span class="keyword">inline</span> <span class="keyword">id</span> *autoreleaseFast(<span class="keyword">id</span> obj)</div><div class="line">{</div><div class="line"> <span class="comment">// 线程安全的,找到当前页</span></div><div class="line"> <span class="comment">// hotPage:以全局的key(static pthread_key_t const key = AUTORELEASE_POOL_KEY;)存储的一份实例,动态更新双向页链表中最后一个节点</span></div><div class="line"> <span class="comment">// 都是同一个套路</span></div><div class="line"> <span class="comment">// 1. 存在页 跳转3</span></div><div class="line"> <span class="comment">// 2. 不存在页 创建页 跳转5</span></div><div class="line"> <span class="comment">// 3. 页是否满了 跳转5</span></div><div class="line"> <span class="comment">// 4. 添加obj 跳转6</span></div><div class="line"> <span class="comment">// 5. 设置hotPage 跳转4</span></div><div class="line"> <span class="comment">// 6. 结束</span></div><div class="line"> AutoreleasePoolPage *page = hotPage(); <span class="comment">// 找到当前活跃的PoolPage</span></div><div class="line"> <span class="keyword">if</span> (page && !page->full()) {</div><div class="line"> <span class="comment">// 存在页, 未满 -> 添加</span></div><div class="line"> <span class="keyword">return</span> page->add(obj);</div><div class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (page) {</div><div class="line"> <span class="comment">// 存在页, 已满 -> 新建后添加</span></div><div class="line"> <span class="keyword">return</span> autoreleaseFullPage(obj, page);</div><div class="line"> } <span class="keyword">else</span> {</div><div class="line"> <span class="comment">// 不存在页 -> 新建页后添加</span></div><div class="line"> <span class="keyword">return</span> autoreleaseNoPage(obj);</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></li></ul></li></ul></li></ul></li></ul></li></ul></li></ul><p>其实add操作就是将obj的指针加入page的next的位置,然后next位置上移<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">id</span> *add(<span class="keyword">id</span> obj)</div><div class="line">{</div><div class="line"> assert(!full());</div><div class="line"> unprotect();</div><div class="line"> <span class="keyword">id</span> *ret = next; <span class="comment">// faster than `return next-1` because of aliasing</span></div><div class="line"> *next++ = obj;</div><div class="line"> protect();</div><div class="line"> <span class="keyword">return</span> ret;</div><div class="line">}</div></pre></td></tr></table></figure></p><blockquote><p>objc_storeStrong增加引用计数的时候使用,但是这边是成员变量,并不会引起引用计数的增加</p></blockquote><h2 id="对象又是什么时候被释放的呢?"><a href="#对象又是什么时候被释放的呢?" class="headerlink" title="对象又是什么时候被释放的呢?"></a>对象又是什么时候被释放的呢?</h2><p>至此,我们已经了解到了,对象是如何被加入到page中的,接下来,我们要了解,对象的释放时机</p><h3 id="释放"><a href="#释放" class="headerlink" title="释放"></a>释放</h3><p>OK,那么我们回到文中最开始的那两个函数<br>objc_autoreleasePoolPush()<br>objc_autoreleasePoolPop(content)<br>每次调用objc_autoreleasePoolPush其实都是在给hotPage的栈顶添加一个哨兵对象,值为0,那么就会变成这个样子<br>(继续盗图<a href="https://blog.sunnyxx.com/2014/10/15/behind-autorelease/" target="_blank" rel="noopener">来源</a>)<br><img src="http://ww2.sinaimg.cn/large/51530583gw1elj5z7hawej20ji0dewff.jpg" alt="哨兵对象"><br>那么<em>objc_autoreleasePoolPush()</em>的返回值就是这个哨兵对象的地址,后续可以成为<em>objc_autoreleasePoolPop</em>的入参进行使用,于是乎就有如下结论</p><ul><li>根据传入的哨兵对象地址,找到对应的page页</li><li>根据page页和哨兵地址,将在哨兵对象添加时间后的对象全部调用release函数,并重置next指针</li></ul><h3 id="autoRelease和runloop的关系"><a href="#autoRelease和runloop的关系" class="headerlink" title="autoRelease和runloop的关系"></a>autoRelease和runloop的关系</h3><p>这块儿没有仔细的研究,抄一抄别人写的描述,<a href="https://blog.csdn.net/u011619283/article/details/53783650" target="_blank" rel="noopener">来源</a><br>我们总是看到有文章说程序启动后,苹果在主线程 RunLoop 里注册了两个 Observer:<br>第一个 Observer 监视的事件是 Entry(即将进入Loop),其回调内会调用 _objc_autoreleasePoolPush() 创建自动释放池。其 order 是-2147483647,优先级最高,保证创建释放池发生在其他所有回调之前。<br>第二个 Observer 监视了两个事件: BeforeWaiting(准备进入睡眠) 和 Exit(即将退出Loop),<br>BeforeWaiting(准备进入睡眠)时调用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的池并创建新池;<br>Exit(即将退出Loop) 时调用 _objc_autoreleasePoolPop() 来释放自动释放池。这个 Observer 的 order 是 2147483647,优先级最低,保证其释放池子发生在其他所有回调之后。<br>打印出MainRunLoop,可以看到MainRunLoop的 Common mode Items 中就有这两个观察者<br><img src="/resources/D35DF794164F53C79121DA4C53C3CA27.jpg" alt="IMAGE"></p><h2 id="杂项"><a href="#杂项" class="headerlink" title="杂项"></a>杂项</h2><p>保证线程访问安全?<br><a href="https://blog.csdn.net/roland_sun/article/details/33728955" target="_blank" rel="noopener">Linux中mprotect()函数的用法</a><br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">protect();</div><div class="line">unprotect();</div></pre></td></tr></table></figure></p><p>objc_storeStrong的研究<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">void</span> objc_storeStrong(<span class="keyword">id</span> *location, <span class="keyword">id</span> obj)</div><div class="line">{</div><div class="line"> <span class="keyword">id</span> prev = *location;</div><div class="line"> <span class="keyword">if</span> (obj == prev) {</div><div class="line"> <span class="keyword">return</span>;</div><div class="line"> }</div><div class="line"> objc_retain(obj);</div><div class="line"> *location = obj;</div><div class="line"> objc_release(prev);</div><div class="line">}</div><div class="line"><span class="comment">// 真正的set方法,是不会直接return掉的</span></div><div class="line">objc_storeStrong(<span class="number">0x0</span>, rax);</div><div class="line"></div><div class="line"><span class="comment">// 临时变量,会直接在内部return掉</span></div><div class="line">objc_storeStrong(var_8, <span class="number">0x0</span>);</div></pre></td></tr></table></figure></p><h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><ol><li><a href="http://blog.sunnyxx.com/2014/10/15/behind-autorelease/" target="_blank" rel="noopener">黑幕背后的Autorelease</a></li><li><a href="https://blog.csdn.net/u011619283/article/details/53783650" target="_blank" rel="noopener">RunLoop总结:RunLoop 与GCD 、Autorelease Pool之间的关系</a></li></ol><!--定义-->]]></content>
<summary type="html">
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>Autorelease-&gt;苹果在内存管理方面的优化策略,旨在方便开发者更加良好的管理对象的内存空间,做到OC对象无需开发者手动进行释放操作。<br>其是在OC从MRC转变为ARC中间形成的产物。</p>
<h2 id="前提"><a href="#前提" class="headerlink" title="前提"></a>前提</h2><p>本文的所有内容均已下述标准下进行的实验</p>
<ul>
<li><a href="https://github.com/RetVal/objc-runtime" target="_blank" rel="noopener">objc源码(可编译版本),tag号为723版本</a></li>
<li><a href="https://github.com/nianran/objc-runtime/tree/Feature/translation" target="_blank" rel="noopener">个人实验的路径,包含部分Demo,以及源码注解,欢迎指教</a></li>
<li>Xcode Version 9.3 (9E145)</li>
<li>Hopper Disassembler v4</li>
</ul>
<h2 id="从最熟悉的地方开始"><a href="#从最熟悉的地方开始" class="headerlink" title="从最熟悉的地方开始"></a>从最熟悉的地方开始</h2><p>main函数,敲过代码的都知道,很多语言都是从main函数开始的(写个Hello,world!,哈哈哈)<br>OK,那么我们从main函数开始,下面贴的就是main函数在编译前和编译后(反编译出来)的对比<br>
</summary>
<category term="OC 疯人院" scheme="http://www.zhz.io/tags/OC-%E7%96%AF%E4%BA%BA%E9%99%A2/"/>
</entry>
<entry>
<title>OC 疯人院 - ISA</title>
<link href="http://www.zhz.io/2018/05/05/OC-%E7%96%AF%E4%BA%BA%E9%99%A2-ISA/"/>
<id>http://www.zhz.io/2018/05/05/OC-疯人院-ISA/</id>
<published>2018-05-05T07:06:17.000Z</published>
<updated>2018-05-05T07:09:43.149Z</updated>
<content type="html"><![CDATA[<h2 id="介绍ISA"><a href="#介绍ISA" class="headerlink" title="介绍ISA"></a>介绍ISA</h2><p>Emmm,这个写到吐了,都不知道看第几遍Or写第几遍了,以前公司分享的,不能搬出来。<br>大家直接看大神的博客就可以了,后面的一些,我慢慢写,哈哈哈。<br><a href="https://github.com/Draveness/analyze/blob/master/contents/objc/%E4%BB%8E%20NSObject%20%E7%9A%84%E5%88%9D%E5%A7%8B%E5%8C%96%E4%BA%86%E8%A7%A3%20isa.md" target="_blank" rel="noopener">链接</a><br><a id="more"></a></p>]]></content>
<summary type="html">
<h2 id="介绍ISA"><a href="#介绍ISA" class="headerlink" title="介绍ISA"></a>介绍ISA</h2><p>Emmm,这个写到吐了,都不知道看第几遍Or写第几遍了,以前公司分享的,不能搬出来。<br>大家直接看大神的博客就可以了,后面的一些,我慢慢写,哈哈哈。<br><a href="https://github.com/Draveness/analyze/blob/master/contents/objc/%E4%BB%8E%20NSObject%20%E7%9A%84%E5%88%9D%E5%A7%8B%E5%8C%96%E4%BA%86%E8%A7%A3%20isa.md" target="_blank" rel="noopener">链接</a><br>
</summary>
<category term="OC 疯人院" scheme="http://www.zhz.io/tags/OC-%E7%96%AF%E4%BA%BA%E9%99%A2/"/>
</entry>
<entry>
<title>2017傻瓜式总结</title>
<link href="http://www.zhz.io/2018/01/06/2017%E5%82%BB%E7%93%9C%E5%BC%8F%E6%80%BB%E7%BB%93/"/>
<id>http://www.zhz.io/2018/01/06/2017傻瓜式总结/</id>
<published>2018-01-06T07:54:09.000Z</published>
<updated>2018-02-11T09:58:09.000Z</updated>
<content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>OK~工作一年了哈,博客一直没有管,组内事情太多(借口,太懒),虽然写了一堆的文章,但是基本上都是在组内发布了。对外么,部分受限于公司协议之类的,部分是暂时觉得水平有限(反正也没什么人看么)。</p><h2 id="回顾2016年立的Flag-amp-Bad"><a href="#回顾2016年立的Flag-amp-Bad" class="headerlink" title="回顾2016年立的Flag & Bad"></a>回顾2016年立的Flag & Bad</h2><ul><li>明确自己的人生目标<br>在这一点上,应该考虑了很久才对,但是一直没有给自己一个明确的答案。<br>但是应该有了一系列的目标。<ol><li>财务自由?不不不,精神自由。在40岁以后能够达到精神自由,不再受限于生活中的种种因素的干扰。毕竟精神自由取决于个人,我觉得我是一个比较感性的人,钱虽然是越多越好,但是钱么,我觉得够用就好。过上舒舒服服的小日子。但是在此之前就必须要好好努力才可以</li><li>在技术上有所突破,为后来人留下一些真正有用的东西,再者是提高一些知名度。2018,一个月至少一篇以上有含量的技术分享,为之努力吧,也只能这么说一说了。</li></ol></li><li>去一次日本<br>没去,其他地方倒是去了好几个,对我个人来说也是很棒的体验。</li><li>能够从容的应对各种事情<br>在这个方面,我觉得个人做的还可以,练就了’Pocker Face’,只不过还有待加强心理承受能力吧,主要还是靠这个,心理承受能力好了才行。不然应对一些事情的时候,会不知所措。</li></ul><a id="more"></a><h2 id="无痕埋点-amp-AOP"><a href="#无痕埋点-amp-AOP" class="headerlink" title="无痕埋点 & AOP"></a>无痕埋点 & AOP</h2><p>做好了,但是产品没有启用(怪我咯?)。17年年初的时候,一直都在做这方面的事情,研究各种大公司的方案,终于耗时3个月的修修整整,出了一个完整的方案,但是埋点组说维护成本太大了(项目启动时候,已经明确提出说维护成本会很大,大致分支成本出处,与最后一致。)。哎,不说这些伤心事,虽然没有启用,但是对于我个人而言还是有很大的收获的,例如AOP相关把Aspects完全给弄完了一遍,梳理了行业内的所有无痕埋点的方案。基本每个方案都进行了一个尝试,留下了很多产物,对于自己有了一个很大的帮助。从中的Aspects库后来改了改,也用到了AOP埋点上,也算是有所收获吧。</p><h2 id="组件化静态库切换"><a href="#组件化静态库切换" class="headerlink" title="组件化静态库切换"></a>组件化静态库切换</h2><p>由于组内的调整,原先的组件化静态库切换方案也逐渐开始有了一些变化,从原先的利用’s.prepare_command’命令,到现在的利用:http/:git的切换。利用Cocoapods的Config单例也达到了清理缓存的功能,达到业务人员无感知的情况下清理缓存,从而进行切换工作,但是这个方案可能会局限于tag的情况,在其他分支下的情况可能会有所不同。应该会需要另外的一些调整。毕竟现在做的静态库的SHA校验也没有做。</p><p>盘点一下已经做好的功能</p><ul><li>源码和静态库完全独立管理(后期如果没有源码权限的同学访问,会直接指向静态库)</li><li>podspec无需配置任何东西,一切都在上传tag版本的时候进行需要绑定</li><li>使用s.source替换原先的s.prepare_command的下载方式,让使用上更加原生pod化</li><li>做了一个Ruby Gem专门来打包静态库(原先的Shell被废弃掉了,不好维护)</li><li>打包好的静态库暴露出特殊源文件(哇这个遇到了好多坑,比如说dummny文件,日乐购)</li><li>完整的子项目打包静态库分包</li><li>异步打包静态库,达到单机子多个Runner在跑也不会出问题。(目标是多机子)</li></ul><p>盘点一下2018年组件化静态库切换要做的</p><ul><li>ZIP文件的SHA校验(这个会对分支打包有帮助,但是还没完全研究)</li><li>打包系统监控(主要公司的gitlab一直不升级,拖慢了这个)</li><li>GEM提交代码后,自动化进行测试。(17年写脚本的时候,最痛苦的就是测试,一个个测试过去,手动的。。。。:-))</li><li>分支打包静态库</li></ul><h2 id="投资方面"><a href="#投资方面" class="headerlink" title="投资方面"></a>投资方面</h2><p>emmm,说完了技术相关,来聊一聊投资相关吧,17年对于投资,有亏有赚。最主要的应该还是通过亏损和盈利调整好了心态。<br>16年的钱都在债券基金里面,17年基本都在51吧(保守型投资哈哈,保持年化8%左右哈哈。)。51因为有个朋友在,还是挺信任这家企业的。OK,这些事稳定的投资项目,期间我也稍微拿出几万在其他方面进行了尝试,起初大部分钱在债券基金的时候,有一部分的钱投入到了其他类型基金方向。因为不了解行情,瞎比乱投,导致后来亏损程度超过20%,股市基金深不可测,我决定暂时进行退出,多了解了解股市基金的规则之后再继续进入这个圈子内。<br>17年年末的时候,开始进入币圈。当时是10万左右软妹币的比特币。在local那边交易后,1.1万拿到了差不多0.1个左右的BTC,开始了我的抄币之旅。在经历多次买卖的亏损后,开始逐渐了解了市场行情,不贪就能获利,哈哈。顶峰的时候应该是到了1.6WRMB左右了吧。然后最近BTC开始疯狂大跌,调整了下心态。没有太多操作,放着就放着吧,我还是挺看好这一块的。虽然投的也不多。</p><h2 id="旅游"><a href="#旅游" class="headerlink" title="旅游"></a>旅游</h2><p>去了千岛湖、黄山、哈尔冰(2018年1月份去的,写总结之前去了,也算进去吧),虽然没有达到最开始去日本的目标,但是这几次行程对我来说都特别印象深刻。组织能力真的特别重要,还有适当的提前工作要做。你能想象220公里竟然需要开7个小时左右吗?他们那边的路不是按照100码来算的,有些地方是只能开到30码。瞬间就惊呆了。由于准备的不够充分,亚布力滑雪也没有去。这个倒是非常的遗憾吧。<br>精打细算。去旅游真的要精打细算,鲜明的栗子,我去哈尔冰4天花了3500。我的第一位朋友,哈尔滨/北京。7天的个人游,只花了4300。第二位朋友哈尔滨/漠河/北京,11天,5500,她跟我说她跟他老公的机票都是150块钱的,我瞬间惊呆了。</p><h3 id="对于2018年的展望"><a href="#对于2018年的展望" class="headerlink" title="对于2018年的展望"></a>对于2018年的展望</h3><p>2018,少立点FLAG吧,贵在坚持。</p><ul><li>坚持看书,看完12本书吧,一个月一本。</li><li>提升下英语水平。</li></ul><h2 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h2><p>NO PAINS, NO GAINS. Hello 2018.</p>]]></content>
<summary type="html">
<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>OK~工作一年了哈,博客一直没有管,组内事情太多(借口,太懒),虽然写了一堆的文章,但是基本上都是在组内发布了。对外么,部分受限于公司协议之类的,部分是暂时觉得水平有限(反正也没什么人看么)。</p>
<h2 id="回顾2016年立的Flag-amp-Bad"><a href="#回顾2016年立的Flag-amp-Bad" class="headerlink" title="回顾2016年立的Flag &amp; Bad"></a>回顾2016年立的Flag &amp; Bad</h2><ul>
<li>明确自己的人生目标<br>在这一点上,应该考虑了很久才对,但是一直没有给自己一个明确的答案。<br>但是应该有了一系列的目标。<ol>
<li>财务自由?不不不,精神自由。在40岁以后能够达到精神自由,不再受限于生活中的种种因素的干扰。毕竟精神自由取决于个人,我觉得我是一个比较感性的人,钱虽然是越多越好,但是钱么,我觉得够用就好。过上舒舒服服的小日子。但是在此之前就必须要好好努力才可以</li>
<li>在技术上有所突破,为后来人留下一些真正有用的东西,再者是提高一些知名度。2018,一个月至少一篇以上有含量的技术分享,为之努力吧,也只能这么说一说了。</li>
</ol>
</li>
<li>去一次日本<br>没去,其他地方倒是去了好几个,对我个人来说也是很棒的体验。</li>
<li>能够从容的应对各种事情<br>在这个方面,我觉得个人做的还可以,练就了’Pocker Face’,只不过还有待加强心理承受能力吧,主要还是靠这个,心理承受能力好了才行。不然应对一些事情的时候,会不知所措。</li>
</ul>
</summary>
<category term="总结" scheme="http://www.zhz.io/tags/%E6%80%BB%E7%BB%93/"/>
</entry>
<entry>
<title>RN晴明视频学习笔记 - 第四章</title>
<link href="http://www.zhz.io/2017/10/11/%E6%99%B4%E6%98%8E%E8%A7%86%E9%A2%91%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-%E7%AC%AC%E5%9B%9B%E7%AB%A0/"/>
<id>http://www.zhz.io/2017/10/11/晴明视频学习笔记-第四章/</id>
<published>2017-10-11T02:29:14.000Z</published>
<updated>2018-02-12T02:30:14.005Z</updated>
<content type="html"><![CDATA[<h2 id="组件的生命周期"><a href="#组件的生命周期" class="headerlink" title="组件的生命周期"></a>组件的生命周期</h2><h3 id="详细的生命周期文档"><a href="#详细的生命周期文档" class="headerlink" title="详细的生命周期文档"></a>详细的生命周期文档</h3><p><a href="https://www.race604.com/react-native-component-lifecycle/" target="_blank" rel="noopener">博客</a></p><h3 id="其他Tips"><a href="#其他Tips" class="headerlink" title="其他Tips"></a>其他Tips</h3><ul><li>render中只做与渲染有关的操作,只读取、不修改任何数据(临时变量除外)<ul><li>因为界面的更改是经常的,所以render是经常触发的</li><li>所以如果你有修改数据等操作,就会多次触发,使结果难以预料</li><li>比如你执行setState,那么setState又触发render,就会导致死循环</li></ul></li><li>随组件加载只执行一次的操作,放在WillMount或者DidMount中<ul><li>比如远程取首页数据(fetch),比如弹出提示框</li></ul></li><li>记得在WillUnmount中销毁定时器和一些订阅事件</li><li>props发生变化,使用WillReceiveProps来处理(比如将变动同步给state)</li></ul><a id="more"></a><h2 id="组件之间的相互通信"><a href="#组件之间的相互通信" class="headerlink" title="组件之间的相互通信"></a>组件之间的相互通信</h2><h3 id="存在的情况"><a href="#存在的情况" class="headerlink" title="存在的情况"></a>存在的情况</h3><ol><li>父组件向子组件进行调用,传递</li><li>子组件之间的相互通信</li><li>子组件向父组件进行通信</li><li>全局跨任意组件间通信</li></ol><h3 id="父组件向子组件进行调用,传递-子组件之间的相互通信"><a href="#父组件向子组件进行调用,传递-子组件之间的相互通信" class="headerlink" title="父组件向子组件进行调用,传递/子组件之间的相互通信"></a>父组件向子组件进行调用,传递/子组件之间的相互通信</h3><ol><li>父组件将自身的state作为子组件的属性props进行传递,父组件调用setState,于是子组件的props相应变化</li><li>通过使用进行调用子组件的方法<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 推荐写法</span></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">App</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>{</div><div class="line"> render() {</div><div class="line"> <span class="keyword">return</span> (</div><div class="line"> <CountDown ref={</div><div class="line"> instance => this.countDown = instance</div><div class="line"> } /></div><div class="line"> ) </div><div class="line"> }</div><div class="line"> componentDidMount() {</div><div class="line"> this.countDown.add(10086);</div><div class="line"> }</div><div class="line">}</div><div class="line">// 不推荐写法</div><div class="line">class App extends Component {</div><div class="line"> render() {</div><div class="line"> return (</div><div class="line"> <CountDown ref={</div><div class="line"> instance => this.countDown = instance</div><div class="line"> } /></div><div class="line"> ) </div><div class="line"> }</div><div class="line"> componentDidMount() {</div><div class="line"> this.countDown.add(10086);</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></li></ol><h3 id="子组件向父组件进行通信"><a href="#子组件向父组件进行通信" class="headerlink" title="子组件向父组件进行通信"></a>子组件向父组件进行通信</h3><ol><li>父组件将函数作为props传递给子组件,子组件在需要的时候进行调用,可以将数据作为函数参数进行回传<ul><li>类似于iOS中的block<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">CountDown</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>{</div><div class="line"> state = {</div><div class="line"> <span class="attr">count</span>: <span class="keyword">this</span>.props.time,</div><div class="line"> };</div><div class="line"> render() {</div><div class="line"> <span class="keyword">const</span> { count } = <span class="keyword">this</span>.state;</div><div class="line"> <span class="keyword">return</span> (</div><div class="line"> <Text>{count}</Text></div><div class="line"> )</div><div class="line"> }</div><div class="line"> add = <span class="function">(<span class="params">time</span>) =></span> {</div><div class="line"> <span class="keyword">this</span>.setState({</div><div class="line"> <span class="attr">count</span>: <span class="keyword">this</span>.state.count + time</div><div class="line"> })</div><div class="line"> }</div><div class="line"> componentDidMount() {</div><div class="line"> <span class="keyword">this</span>.timer = setInterval(<span class="function"><span class="params">()</span> =></span> {</div><div class="line"> <span class="keyword">const</span> { count } = <span class="keyword">this</span>.state;</div><div class="line"> <span class="keyword">if</span> (count === <span class="number">0</span>) {</div><div class="line"> <span class="keyword">this</span>.props.timeout && <span class="keyword">this</span>.props.timeout( <span class="number">1</span> );</div><div class="line"> <span class="keyword">return</span> clearInterval(<span class="keyword">this</span>.timer);</div><div class="line"> }</div><div class="line"> <span class="keyword">this</span>.setState({</div><div class="line"> <span class="attr">count</span>: count - <span class="number">1</span>,</div><div class="line"> });</div><div class="line"> }, <span class="number">1000</span>);</div><div class="line"> }</div><div class="line"> componentWillUnmount() {</div><div class="line"> clearInterval(<span class="keyword">this</span>.timer);</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">App</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>{</div><div class="line"> addTime = <span class="function"><span class="params">()</span> =></span> {</div><div class="line"> <span class="keyword">this</span>.countDown.add(<span class="number">5</span>)</div><div class="line"> }</div><div class="line"> timeOut = <span class="function">(<span class="params">params</span>) =></span> {</div><div class="line"> alert(<span class="string">'爸爸知道了'</span> + params)</div><div class="line"> }</div><div class="line"> render() {</div><div class="line"> <span class="keyword">return</span> (</div><div class="line"> <View></div><div class="line"> <TouchableOpacity onPress={this.addTime}></div><div class="line"> 延长十秒</div><div class="line"> </TouchableOpacity></div><div class="line"> <CountDown ref={ countdown => this.countDown = countdown} time = '3' timeout={this.timeOut} /></div><div class="line"> </View></div><div class="line"> )</div><div class="line"> }</div><div class="line"></div><div class="line">}</div><div class="line"></div><div class="line">AppRegistry.registerComponent('App', () => App)</div></pre></td></tr></table></figure></li></ul></li></ol><blockquote><p>Note</p><ol><li>跟iOS一样如果一个传入的函数(iOS为Block),加上了括号,那么即会变成函数调用</li><li>根据1,在动态列表中,想传入对应参数就比较困难,所以有以下方法</li></ol></blockquote><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">// 如何传入参数以区分不同的点击呢?</div><div class="line">arr.map(d => <Text key={d.id} onPress={this.handlePress}>{d.text}</Text>)</div><div class="line">// 错误使用,这么做会直接执行</div><div class="line">arr.map(d => <Text key={d.id} onPress={this.handlePress(d.id)}>{d.text}</Text>)</div><div class="line"></div><div class="line">// 正确使用</div><div class="line">arr.map(d => <Text key={d.id} onPress={() => this.handlePress(d.id)}>{d.text}</Text>)</div><div class="line">arr.map(d => <Text key={d.id} onPress={this.handlePress.bind(this, d.id)}>{d.text}</Text>)</div><div class="line">正确!箭头函数或bind都会生成新函数。传入参数以闭包的形式“封存”,留待调用。</div></pre></td></tr></table></figure><blockquote><p>这里有个坑,父类传到子类,因为函数被bind或者箭头函数重新进行了包装,所以在传参的时候,用在新的参数里面重新添加参数<br><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 实例代码</span></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">CountDown</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>{</div><div class="line"> state = {</div><div class="line"> <span class="attr">count</span>: <span class="keyword">this</span>.props.time,</div><div class="line"> };</div><div class="line"> render() {</div><div class="line"> <span class="keyword">const</span> { count } = <span class="keyword">this</span>.state;</div><div class="line"> <span class="keyword">return</span> (</div><div class="line"> <Text>{count}</Text></div><div class="line"> )</div><div class="line"> }</div><div class="line"> add = <span class="function">(<span class="params">time</span>) =></span> {</div><div class="line"> <span class="keyword">this</span>.setState({</div><div class="line"> <span class="attr">count</span>: <span class="keyword">this</span>.state.count + time</div><div class="line"> })</div><div class="line"> }</div><div class="line"> componentDidMount() {</div><div class="line"> <span class="keyword">this</span>.timer = setInterval(<span class="function"><span class="params">()</span> =></span> {</div><div class="line"> <span class="keyword">const</span> { count } = <span class="keyword">this</span>.state;</div><div class="line"> <span class="keyword">if</span> (count === <span class="number">0</span>) {</div><div class="line"> <span class="keyword">this</span>.props.timeout && <span class="keyword">this</span>.props.timeout(<span class="string">"123"</span>);</div><div class="line"> <span class="keyword">return</span> clearInterval(<span class="keyword">this</span>.timer);</div><div class="line"> }</div><div class="line"> <span class="keyword">this</span>.setState({</div><div class="line"> <span class="attr">count</span>: count - <span class="number">1</span>,</div><div class="line"> });</div><div class="line"> }, <span class="number">1000</span>);</div><div class="line"> }</div><div class="line"> componentWillUnmount() {</div><div class="line"> clearInterval(<span class="keyword">this</span>.timer);</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">App</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>{</div><div class="line"> addTime = <span class="function"><span class="params">()</span> =></span> {</div><div class="line"> <span class="keyword">this</span>.countDown.add(<span class="number">5</span>)</div><div class="line"> }</div><div class="line"> timeOut = <span class="function">(<span class="params">params1,params2</span>) =></span> {</div><div class="line"> alert(<span class="string">'爸爸知道了'</span> + params1 + params2)</div><div class="line"> }</div><div class="line"> state = {</div><div class="line"> <span class="attr">arr</span>: [<span class="number">5</span>,<span class="number">4</span>,<span class="number">3</span>]</div><div class="line"> }</div><div class="line"> render() {</div><div class="line"> <span class="keyword">return</span> (</div><div class="line"> <View></div><div class="line"> {</div><div class="line"> this.state.arr.map (i => {</div><div class="line"> return <CountDown key={i} time={i} timeout={(child) => this.timeOut(i,child)}/></div><div class="line"> })</div><div class="line"> }</div><div class="line"> </View></div><div class="line"> )</div><div class="line"> }</div><div class="line">}</div><div class="line">AppRegistry.registerComponent('App', () => App)</div></pre></td></tr></table></figure></p></blockquote><h3 id="双向通信"><a href="#双向通信" class="headerlink" title="双向通信"></a>双向通信</h3><ol><li>全局时间订阅系统(EventEmitter)</li><li>(Flux系)单向数据流框架<ul><li>flux</li><li>reflux</li><li>alt</li><li>redux</li></ul></li><li>双向数据流狂阶<ul><li>mobx</li></ul></li></ol>]]></content>
<summary type="html">
<h2 id="组件的生命周期"><a href="#组件的生命周期" class="headerlink" title="组件的生命周期"></a>组件的生命周期</h2><h3 id="详细的生命周期文档"><a href="#详细的生命周期文档" class="headerlink" title="详细的生命周期文档"></a>详细的生命周期文档</h3><p><a href="https://www.race604.com/react-native-component-lifecycle/" target="_blank" rel="noopener">博客</a></p>
<h3 id="其他Tips"><a href="#其他Tips" class="headerlink" title="其他Tips"></a>其他Tips</h3><ul>
<li>render中只做与渲染有关的操作,只读取、不修改任何数据(临时变量除外)<ul>
<li>因为界面的更改是经常的,所以render是经常触发的</li>
<li>所以如果你有修改数据等操作,就会多次触发,使结果难以预料</li>
<li>比如你执行setState,那么setState又触发render,就会导致死循环</li>
</ul>
</li>
<li>随组件加载只执行一次的操作,放在WillMount或者DidMount中<ul>
<li>比如远程取首页数据(fetch),比如弹出提示框</li>
</ul>
</li>
<li>记得在WillUnmount中销毁定时器和一些订阅事件</li>
<li>props发生变化,使用WillReceiveProps来处理(比如将变动同步给state)</li>
</ul>
</summary>
<category term="RN" scheme="http://www.zhz.io/tags/RN/"/>
</entry>
<entry>
<title>RN晴明视频学习笔记 - 第三章</title>
<link href="http://www.zhz.io/2017/09/12/RN%E6%99%B4%E6%98%8E%E8%A7%86%E9%A2%91%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-%E7%AC%AC%E4%B8%89%E7%AB%A0/"/>
<id>http://www.zhz.io/2017/09/12/RN晴明视频学习笔记-第三章/</id>
<published>2017-09-12T02:24:25.000Z</published>
<updated>2018-02-12T02:27:45.983Z</updated>
<content type="html"><![CDATA[<h2 id="ReactJS和ReactNative"><a href="#ReactJS和ReactNative" class="headerlink" title="ReactJS和ReactNative"></a>ReactJS和ReactNative</h2><h3 id="两者区别比较"><a href="#两者区别比较" class="headerlink" title="两者区别比较"></a>两者区别比较</h3><p><img src="/resources/44D051F354FFBF35A300E1F8C731FD9B.jpg" alt="IMAGE"></p><ul><li>最重要的区别莫过于是控件方面的区别,对应的分别是对应平台的组件<a id="more"></a></li></ul><p><img src="/resources/DDDD7F748054B8B26D999CBAED87BAC7.jpg" alt="IMAGE"></p><ul><li>ReactNative提供转化的桥梁</li></ul><h3 id="规范-规则"><a href="#规范-规则" class="headerlink" title="规范/规则"></a>规范/规则</h3><p>借鉴与XHTML</p><ol><li>开始和结束标签配对<br><组件>ooxx</组件></li><li>无内容的组件标签应写为自封闭形式<br><组件></组件> 应写为 <组件 /></li><li><p>可自定义属性,字符串值应使用双引号,其他值用{}括起来</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><Person age={<span class="number">30</span>} sex=“male” married={<span class="literal">true</span>} /></div></pre></td></tr></table></figure></li><li><p>布尔属性可省略值</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><Person married /> 等价于 <Person married={<span class="literal">true</span>} /></div></pre></td></tr></table></figure></li><li><p>Render(渲染)必须是单一节点<br><img src="/resources/EF0E754011E0D0BED63225A48E19617B.jpg" alt="IMAGE"></p></li><li><p>空值会被自动忽略掉</p><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><div /> </div><div class="line"><div></div> </div><div class="line"><div>{false}</div> </div><div class="line"><div>{null}</div> </div><div class="line"><div>{undefined}</div> </div><div class="line"><div>{true}</div></div><div class="line"></div><div class="line">// 技巧 显示和隐藏组件</div><div class="line"><View></div><div class="line">{ showWarning && <Text>FBI warning</Text> }</div><div class="line"></View></div></pre></td></tr></table></figure></li><li><p>组件必须已大写开头<br><img src="/resources/A7778B71E3034ACDAB3F3FD994D0BE2A.jpg" alt="IMAGE"></p></li><li>文本必须写在Text的组件内<br><img src="/resources/B526EFC8D8E70CE825608A0B338F7B4C.jpg" alt="IMAGE"></li><li>注释的写法<br><img src="/resources/AC476BD3FECD7F5E58F30C30C56577CF.jpg" alt="IMAGE"></li><li>只能嵌入表达式<br><img src="/resources/EDB686DFCD64EBEB3E6378E6931F1049.jpg" alt="IMAGE"></li></ol><h2 id="初识React组件化开发"><a href="#初识React组件化开发" class="headerlink" title="初识React组件化开发"></a>初识React组件化开发</h2><h3 id="在线模拟器"><a href="#在线模拟器" class="headerlink" title="在线模拟器"></a>在线模拟器</h3><p><a href="http://dabbott.github.io/react-native-web-player/" target="_blank" rel="noopener">练习基础的模拟器</a></p><h3 id="示例一"><a href="#示例一" class="headerlink" title="示例一"></a>示例一</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 导入开发所需要的库</span></div><div class="line"><span class="keyword">import</span> React, { Component } <span class="keyword">from</span> <span class="string">'react'</span></div><div class="line"><span class="keyword">import</span> {</div><div class="line"> AppRegistry,</div><div class="line"> StyleSheet,</div><div class="line"> Text,</div><div class="line"> View,</div><div class="line">} <span class="keyword">from</span> <span class="string">'react-native'</span></div><div class="line"></div><div class="line"><span class="comment">// Class类中会默认带有props,可以进行调用获取</span></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">GoodMorning</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>{</div><div class="line"> <span class="keyword">static</span> defaultProps = {</div><div class="line"> <span class="attr">name</span>: <span class="string">'somebody'</span></div><div class="line"> }</div><div class="line"> <span class="keyword">static</span> propTypes = {</div><div class="line"><span class="attr">name</span>: React.propTypes.string, <span class="comment">// 约定需要的类型(为字符串)</span></div><div class="line">}</div><div class="line"> render() {</div><div class="line"> <span class="keyword">return</span> (</div><div class="line"> <span class="comment">// 结构式语法</span></div><div class="line"> <Text>Good morning, {<span class="keyword">this</span>.props.name}!</Text></div><div class="line"> )</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">// Method中必须要手动去指定一下</span></div><div class="line"><span class="comment">// 如果的这里写了asd那么下面也要改成asd</span></div><div class="line"><span class="comment">// 如果这边有两个参数,默认是取第一个参数去取值</span></div><div class="line"><span class="keyword">const</span> GoodEvening = <span class="function">(<span class="params">props</span>) =></span> {</div><div class="line"> <span class="keyword">return</span> (</div><div class="line"> <Text>Good evening, {props.name}</Text></div><div class="line"> )</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">// 生命一个组件 App是名字</span></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">App</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>{</div><div class="line"> render() {</div><div class="line"> <span class="keyword">return</span> (</div><div class="line"> <View></div><div class="line"> <GoodMorning name="Sir" /></div><div class="line"> <GoodEvening name="Madam" /></div><div class="line"> </View ></div><div class="line"> )</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line">AppRegistry.registerComponent('App', () => App)</div></pre></td></tr></table></figure><ul><li>属性使用小结<ul><li>在类中,属性为默认配置,不需要去写,</li><li>在方法中,属性要自己去声明,默认取第一个参数当做是属性的调用类</li><li>在使用属性的时候,必须加入大括号,才可以进行调用</li></ul></li><li>defaultProps 是用来设置属性的默认值的。</li><li>propTypes 是用来约束设置的属性的类型的,这个只在开发阶段有效,发布阶段会被自动移除</li></ul><h3 id="变量作用于"><a href="#变量作用于" class="headerlink" title="变量作用于"></a>变量作用于</h3><ul><li>函数内的局部变量,只能函数内读写,函数运行完后销毁(闭包除外)</li><li>class内的成员变量,在单个class的实例内读写,实例销毁时一并销毁<ul><li>使用时不要忘记this.</li></ul></li><li>class内的静态成员变量,在所有class的实例内共享,不会自动销毁<ul><li>其他模块可通过此class访问(类public)</li></ul></li><li>class外的变量,在所有class的实例内共享(公有),不会自动销毁<ul><li>除非明确export,否则其他模块不可访问(类private)</li></ul></li><li>global全局变量,任何地方可读写(类浏览器的window),不会自动销毁<ul><li>global.test = 1;则之后的任何地方可alert(global.test)或alert(test)</li></ul></li></ul><h3 id="各种变量的写法"><a href="#各种变量的写法" class="headerlink" title="各种变量的写法"></a>各种变量的写法</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div></pre></td><td class="code"><pre><div class="line">global.d = <span class="number">0</span>;<span class="comment">// 全局变量</span></div><div class="line"><span class="keyword">const</span> c = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>];<span class="comment">// 在当前文件都可以访问</span></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">GoodMorning</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>{</div><div class="line"> </div><div class="line"> <span class="comment">// 成员变量</span></div><div class="line"> <span class="comment">// 推荐写法</span></div><div class="line"> a = <span class="number">1</span> <span class="comment">// 成员变量写法,注意没有var或者let</span></div><div class="line"> </div><div class="line"> <span class="comment">// 并不推荐的写法</span></div><div class="line"> <span class="keyword">constructor</span>(props) { <span class="comment">// 构造函数</span></div><div class="line"><span class="keyword">super</span>(props); <span class="comment">// 照抄即可,不可省略</span></div><div class="line"><span class="keyword">this</span>.a = <span class="number">1</span>; </div><div class="line">}</div><div class="line"></div><div class="line"> <span class="comment">// 静态变量 可以直接通过类名来访问,GoodMorning.defaultProps</span></div><div class="line"> <span class="keyword">static</span> b = <span class="number">2</span>;</div><div class="line"> render() {</div><div class="line"> <span class="keyword">return</span> (</div><div class="line"> <Text>Good morning, {this.props.name}!</Text></div><div class="line"> )</div><div class="line"> }</div><div class="line"> method1(){</div><div class="line"> <span class="comment">// 因为是实例的成员变量,所有加入this才可以使用</span></div><div class="line"> <span class="keyword">this</span>.a;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure><h3 id="示例二,使用map进行列表的循环创建"><a href="#示例二,使用map进行列表的循环创建" class="headerlink" title="示例二,使用map进行列表的循环创建"></a>示例二,使用map进行列表的循环创建</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">import</span> React, { Component } <span class="keyword">from</span> <span class="string">'react'</span></div><div class="line"><span class="keyword">import</span> {</div><div class="line"> AppRegistry,</div><div class="line"> StyleSheet,</div><div class="line"> Text,</div><div class="line"> View,</div><div class="line">} <span class="keyword">from</span> <span class="string">'react-native'</span></div><div class="line"></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">GoodMorning</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>{</div><div class="line"> render() {</div><div class="line"> <span class="keyword">return</span> (</div><div class="line"> <Text>Good morning, {this.props.name}!</Text></div><div class="line"> )</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">const</span> names = [<span class="string">'name1'</span>, <span class="string">'name2'</span>, <span class="string">'name3'</span>,<span class="string">'name4'</span>]</div><div class="line"><span class="keyword">const</span> GoodEvening = <span class="function">(<span class="params">props</span>) =></span> {</div><div class="line"> <span class="keyword">return</span> (</div><div class="line"> <Text>Good evening, {props.name}</Text></div><div class="line"> )</div><div class="line">}</div><div class="line"></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">App</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>{</div><div class="line"> render() {</div><div class="line"> <span class="keyword">return</span> (</div><div class="line"> <View></div><div class="line"> {</div><div class="line"> // 因为虚拟DOM的需要比较差异,使用key方便DOM进行差异的比较,key直接放在直接容器上面</div><div class="line"> names.map(name => <GoodMorning key={name} name={name} />)</div><div class="line"> </div><div class="line"> }</div><div class="line"> </View ></div><div class="line"> )</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line">AppRegistry.registerComponent('App', () => App)</div></pre></td></tr></table></figure><h3 id="示例三,点赞示例"><a href="#示例三,点赞示例" class="headerlink" title="示例三,点赞示例"></a>示例三,点赞示例</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">import</span> React, { Component } <span class="keyword">from</span> <span class="string">'react'</span></div><div class="line"><span class="keyword">import</span> {</div><div class="line"> AppRegistry,</div><div class="line"> StyleSheet,</div><div class="line"> Text,</div><div class="line"> View,</div><div class="line"> Image,</div><div class="line"> TouchableOpacity,</div><div class="line">} <span class="keyword">from</span> <span class="string">'react-native'</span></div><div class="line"></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">App</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>{</div><div class="line"> state = {</div><div class="line"> <span class="attr">likes</span>: <span class="number">0</span>,</div><div class="line"> };</div><div class="line"> onPress = <span class="function"><span class="params">()</span> =></span> {</div><div class="line"> <span class="keyword">const</span> { likes } = <span class="keyword">this</span>.state;</div><div class="line"> <span class="keyword">this</span>.setState({</div><div class="line"> <span class="attr">likes</span>: likes + <span class="number">1</span>,</div><div class="line"> });</div><div class="line"> };</div><div class="line"> render() {</div><div class="line"> <span class="keyword">return</span> (</div><div class="line"> <View style={styles.container}></div><div class="line"> <TouchableOpacity onPress={this.onPress}></div><div class="line"> <Image</div><div class="line"> style={styles.image}</div><div class="line"> source={{</div><div class="line"> uri: 'https://github.com/facebook/react-native/blob/master/Examples/UIExplorer/js/Thumbnails/like.png?raw=true',</div><div class="line"> }}</div><div class="line"> /></div><div class="line"> </TouchableOpacity></div><div class="line"> <Text>{this.state.likes}</Text></div><div class="line"> </View></div><div class="line"> )</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line">const styles = StyleSheet.create({</div><div class="line"> container: {</div><div class="line"> flex: 1,</div><div class="line"> justifyContent: 'center',</div><div class="line"> alignItems: 'center',</div><div class="line"> backgroundColor: '#F5FCFF',</div><div class="line"> },</div><div class="line"> image: {</div><div class="line"> width: 65,</div><div class="line"> height: 65,</div><div class="line"> },</div><div class="line">})</div><div class="line"></div><div class="line">AppRegistry.registerComponent('App', () => App)</div></pre></td></tr></table></figure><p>总结</p><ul><li>一切界面的变化都是因为state变化而引起的</li><li>但是state的修改必须通过setState进行修改</li><li>直接通过修改属性值是无效的,例如this.state.likes = 100;这样的直接赋值修改是无效的</li><li>setState是一个merge合并操作,只修改指定的属性,不影响其他属性</li><li><p>setState是一个异步操作,修改之后并不能马上修改生效。</p><ul><li>比如说在setState之后马上运行alert,进行数字的显示,这个时候是错的<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 错误示例</span></div><div class="line">onPress = <span class="function"><span class="params">()</span> =></span> {</div><div class="line"> <span class="keyword">const</span> { likes } = <span class="keyword">this</span>.state;</div><div class="line"> <span class="keyword">this</span>.setState({</div><div class="line"> <span class="attr">likes</span>: likes + <span class="number">1</span>,</div><div class="line"> });</div><div class="line"> alert(<span class="keyword">this</span>.state.like);</div><div class="line">};</div></pre></td></tr></table></figure></li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 正确示例</span></div><div class="line">onPress = <span class="function"><span class="params">()</span> =></span> {</div><div class="line"> <span class="keyword">const</span> { likes } = <span class="keyword">this</span>.state;</div><div class="line"> <span class="keyword">const</span> newLikes = likes + <span class="number">1</span>;</div><div class="line"> <span class="keyword">this</span>.setState({</div><div class="line"> <span class="attr">likes</span>: newLikes,</div><div class="line"> });</div><div class="line"> alert(newLikes);</div><div class="line">};</div></pre></td></tr></table></figure></li></ul><h3 id="为什么要引入State的概念"><a href="#为什么要引入State的概念" class="headerlink" title="为什么要引入State的概念"></a>为什么要引入State的概念</h3><p><img src="/resources/9F302D689DC97C4B7A739579EFE09BCA.jpg" alt="IMAGE"><br>如图所示,虚拟DOM的修改只是一个增量更新,比较得出前和后的差异,计算出diff,然后去重新渲染差异部分<br><img src="/resources/E105DA6EEC89A567AC63D8E805575EB1.jpg" alt="IMAGE"><br><img src="/resources/361B31527F3ECE475B3705A81659E72C.jpg" alt="IMAGE"><br>Key的使用方便了DOM计算出差异部分,对差异部分可以进行更方便的比较。</p><h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><h3 id="JSX"><a href="#JSX" class="headerlink" title="JSX"></a>JSX</h3><p><a href="https://facebook.github.io/react/docs/introducing-jsx.html" target="_blank" rel="noopener">Introducint JSX</a><br><a href="https://facebook.github.io/react/docs/jsx-in-depth.html" target="_blank" rel="noopener">JSX In Depth</a></p>]]></content>
<summary type="html">
<h2 id="ReactJS和ReactNative"><a href="#ReactJS和ReactNative" class="headerlink" title="ReactJS和ReactNative"></a>ReactJS和ReactNative</h2><h3 id="两者区别比较"><a href="#两者区别比较" class="headerlink" title="两者区别比较"></a>两者区别比较</h3><p><img src="/resources/44D051F354FFBF35A300E1F8C731FD9B.jpg" alt="IMAGE"></p>
<ul>
<li>最重要的区别莫过于是控件方面的区别,对应的分别是对应平台的组件
</summary>
<category term="RN" scheme="http://www.zhz.io/tags/RN/"/>
</entry>
<entry>
<title>RN晴明视频学习笔记 - 第二章</title>
<link href="http://www.zhz.io/2017/09/10/%E6%99%B4%E6%98%8E%E8%A7%86%E9%A2%91%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-%E7%AC%AC%E4%BA%8C%E7%AB%A0/"/>
<id>http://www.zhz.io/2017/09/10/晴明视频学习笔记-第二章/</id>
<published>2017-09-10T08:51:50.000Z</published>
<updated>2018-02-12T02:28:16.000Z</updated>
<content type="html"><![CDATA[<h2 id="原生-Hybird-RN"><a href="#原生-Hybird-RN" class="headerlink" title="原生/Hybird/RN"></a>原生/Hybird/RN</h2><h3 id="对比"><a href="#对比" class="headerlink" title="对比"></a>对比</h3><table><thead><tr><th>形式</th><th>介绍</th><th>优势</th><th>劣势</th></tr></thead><tbody><tr><td>原生</td><td>使用原生语言进行编写开发</td><td>性能好/有生态圈的优势点</td><td>各个平台有各自的语言,学习成本大,不兼容</td></tr><tr><td>Hybrid App混合开发</td><td>使用前段代码,在各个平台上有他的容器WebView,利用他进行渲染</td><td>跨平台,适合简单应用</td><td>性能不是很好 </td></tr><tr><td>RN</td><td>较于前面两者之间,使用JS开发,翻译成原生组件进行展示</td><td>学习成本低,天然跨平台,无审核热更新,具有优秀的社区能力(开源库)</td><td>性能</td><td></td></tr></tbody></table><h2 id="ECMAScript"><a href="#ECMAScript" class="headerlink" title="ECMAScript"></a>ECMAScript</h2><p>名词解释:ECMAScript是一种由Ecma国际(前身为欧洲计算机制造商协会,英文名称是European Computer Manufacturers Association)通过ECMA-262标准化的脚本程序设计语言。这种语言在万维网上应用广泛,它往往被称为JavaScript或JScript,但实际上后两者是ECMA-262标准的实现和扩展。</p><p>简单的说:ECMAScript是一种标准文本语法,JS是根据其进行设计的</p><a id="more"></a><h3 id="变量和常量生命"><a href="#变量和常量生命" class="headerlink" title="变量和常量生命"></a>变量和常量生命</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> greeting = <span class="string">'hi'</span>; <span class="comment">// 新式写法,推荐使用</span></div><div class="line"><span class="keyword">var</span> greeting1 = <span class="string">'hi1'</span>; <span class="comment">//老式写法,不推荐使用</span></div><div class="line"><span class="keyword">const</span> answer = <span class="number">4</span>;</div><div class="line"><span class="comment">// 多个生命简单些发</span></div><div class="line"><span class="keyword">let</span> a = <span class="string">'1'</span>; b = <span class="string">'2'</span>;</div><div class="line"><span class="comment">// 没有声明或者声明后没有赋值的变量值为常量</span></div></pre></td></tr></table></figure><h3 id="箭头函数"><a href="#箭头函数" class="headerlink" title="箭头函数"></a>箭头函数</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">(a,b) => a+b;</div></pre></td></tr></table></figure><ul><li><p>class外部:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> 函数名 = <span class="function">(<span class="params">参数</span>) =></span> { 内部逻辑 };</div></pre></td></tr></table></figure></li><li><p>class内部:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">SomeComponent</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>{</div><div class="line"> 方法名 = <span class="function">(<span class="params">参数</span>) =></span> {</div><div class="line"> <span class="comment">// 内部逻辑</span></div><div class="line"> };</div><div class="line">}</div></pre></td></tr></table></figure></li></ul><blockquote><p>可能并不是很习惯这种写法,联想方案<br>f(x) = πx^2<br>(x) => 3.14 <em> x </em> x<br>输入 => 输出<br>参数 => 返回表达式</p></blockquote><ul><li>Tips:这里有个坑,如果箭头函数的代码块部分使用了大括号,则此时切莫忘了使用return语句返回:</li><li>() => 1 // 返回1</li><li>() => { 1 } // 返回 undefined</li><li>() => { return 1; } // 返回1</li></ul><h3 id="扩展运算符"><a href="#扩展运算符" class="headerlink" title="扩展运算符"></a>扩展运算符</h3><p>…扩展运算符好比是把一个压缩包给解压到当前目录<br><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 数组</span></div><div class="line"> <span class="comment">// 数组</span></div><div class="line"> <span class="keyword">let</span> a = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</div><div class="line"> <span class="built_in">console</span>.log(...a) <span class="comment">// 1 2 3 </span></div><div class="line"> <span class="built_in">console</span>.log(<span class="number">1</span>, ...a, <span class="number">5</span>) <span class="comment">// 1 1 2 3 5</span></div><div class="line"></div><div class="line"> <span class="comment">// 对象</span></div><div class="line"> <span class="keyword">let</span> z = { <span class="attr">a</span>: <span class="number">3</span>, <span class="attr">b</span>: <span class="number">4</span> }; </div><div class="line"> <span class="keyword">let</span> n = { ...z }; </div><div class="line"> <span class="built_in">console</span>.log(n)<span class="comment">// { a: 3, b: 4 }</span></div></pre></td></tr></table></figure></p><h3 id="解构"><a href="#解构" class="headerlink" title="解构"></a>解构</h3><p>可以想象成一个语法糖,可以帮助你快速的从对象中获取数据<br><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 数组</span></div><div class="line"><span class="keyword">let</span> [a, b, c] = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>];</div><div class="line"><span class="built_in">console</span>.log(a, b, c); <span class="comment">// 1 2 3</span></div><div class="line"></div><div class="line"><span class="comment">// 对象</span></div><div class="line"><span class="keyword">var</span> props = { <span class="attr">checked</span>: <span class="literal">false</span>, <span class="attr">data</span>: { <span class="attr">count</span>: <span class="number">3</span>, <span class="attr">price</span>: <span class="number">100</span> } }; </div><div class="line"><span class="comment">// 从props中拿到checked的值和count值</span></div><div class="line"><span class="keyword">var</span> { checked, <span class="attr">data</span>: { count } } = props;</div><div class="line"><span class="built_in">console</span>.log(checked, count);<span class="comment">// false 3</span></div></pre></td></tr></table></figure></p><h3 id="数组的新增方法"><a href="#数组的新增方法" class="headerlink" title="数组的新增方法"></a>数组的新增方法</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 数组查找元素</span></div><div class="line">[<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>].includes(<span class="number">2</span>); <span class="comment">// true </span></div><div class="line">[<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>].includes(<span class="number">4</span>); <span class="comment">// false</span></div><div class="line"><span class="comment">// 字符串也可用</span></div><div class="line">‘hello’.includes(‘hell’);<span class="comment">// true</span></div><div class="line"></div><div class="line"><span class="comment">// map返回等长的新数组</span></div><div class="line"><span class="keyword">let</span> odds = [<span class="number">1</span>, <span class="number">3</span>, <span class="number">5</span>, <span class="number">7</span>, <span class="number">9</span>];</div><div class="line"><span class="keyword">let</span> evens = odds.map(<span class="function"><span class="params">i</span> =></span> i + <span class="number">1</span>); <span class="comment">// [2, 4, 6, 8, 10]</span></div><div class="line"></div><div class="line"><span class="comment">// 使用标记技巧</span></div><div class="line"><span class="keyword">let</span> names = [‘tom’, ‘jerry’];</div><div class="line"><span class="keyword">let</span> nameTags = names.map(<span class="function"><span class="params">n</span> =></span> <Text>{n}<<span class="regexp">/Text>)</span></div></pre></td></tr></table></figure><h3 id="推荐书籍"><a href="#推荐书籍" class="headerlink" title="推荐书籍"></a>推荐书籍</h3><ol><li><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide" target="_blank" rel="noopener">MDN(Mozilla开发者网络)</a></li><li><a href="http://es6.ruanyifeng.com/" target="_blank" rel="noopener">ECMAScript 6 入门(阮一峰)</a></li></ol><h2 id="Node-js"><a href="#Node-js" class="headerlink" title="Node.js"></a>Node.js</h2><h3 id="为什么RN需要Node-js"><a href="#为什么RN需要Node-js" class="headerlink" title="为什么RN需要Node.js"></a>为什么RN需要Node.js</h3><ol><li>提供React Packager的运行环境</li><li>提供npm包管理器来安装第三方模块 (node_modules)</li><li>提供一些可参考的代码规范(commonjs)</li></ol><h3 id="RN的基本架构"><a href="#RN的基本架构" class="headerlink" title="RN的基本架构"></a>RN的基本架构</h3><p><img src="/resources/A28F86AE1A1C71F1FC29711BAA78B8E0.jpg" alt="IMAGE"></p><h3 id="npm使用"><a href="#npm使用" class="headerlink" title="npm使用"></a>npm使用</h3><ol><li>npm依赖于同级目录下一个package.json文件进行,仓库的下载和依赖管理。没有这个文件可以使用npm init进行创建</li><li>npm install (npm i)包安装器<ul><li>附加命令 -g,安装在系统根目录中,比如说react-native-cli</li><li>–save/-S 查找仓库中某某库的方法下载,并且安装,填写进入package.json文件</li></ul></li><li>npm下载的库都会存放在同级目录下的node_modules文件夹中,此文件夹随用随装,原则上不复制、不移动、不修改、不上传</li></ol><h3 id="导入导出概念"><a href="#导入导出概念" class="headerlink" title="导入导出概念"></a>导入导出概念</h3><ol><li>JS本身没有模块的概念,他们之间是无法自由引用的。所以node.js制定了commonjs模块贵方,方便了拆分和引用代码<a href="https://nodejs.org/docs/latest/api/modules.html" target="_blank" rel="noopener">参考链接</a></li><li><p>提供两种引用的语法</p><ul><li><p>ES5 不推荐</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="built_in">module</span>.exports = moduleA; <span class="comment">// a.js导出moduleA模块</span></div><div class="line"><span class="keyword">var</span> moduleA = <span class="built_in">require</span>(’./a’) <span class="comment">// 同级目录下的B模块导入moduleA模块</span></div></pre></td></tr></table></figure></li><li><p>ES6 推荐使用</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">export</span> <span class="keyword">default</span> moduleA; <span class="comment">// a.js导出模块A</span></div><div class="line"><span class="keyword">import</span> moduleA <span class="keyword">from</span> ’./a’ <span class="comment">// b.js目录下导入同级目录的a.js文件的某个模块</span></div></pre></td></tr></table></figure></li></ul></li><li><p>模块查找/引用规则:</p><ul><li>require(‘ooxx’) – 引用node_modules/ooxx/index.js,或是node_modules/ooxx/package.json中main字段所指定的js文件</li><li>require(‘./a’) – 引用同目录下的a.js文件。注意 ./ 不能省略,.js后缀可写可不写</li><li>require(‘../a/b’) – 引用父目录中的a目录中的b.js文件;若b为目录名,则查找引用b目录中的index.js文件。</li></ul></li></ol><blockquote><p>npm仓库是需要翻墙的,可以使用国内的淘宝源,使用下述命令进行源的切换<br>npm config set registry <a href="https://registry.npm.taobao.org" target="_blank" rel="noopener">https://registry.npm.taobao.org</a> –global<br>npm config set disturl <a href="https://npm.taobao.org/dist" target="_blank" rel="noopener">https://npm.taobao.org/dist</a> –global</p></blockquote><h2 id="知识点储备"><a href="#知识点储备" class="headerlink" title="知识点储备"></a>知识点储备</h2><ol><li>所有的JS文件,都会被package打包整合进入main.jsbundle文件中。</li><li>main.jsbundle文件可以通过pushby/codepush(微软)进行发布后的热更新。</li></ol><h2 id="书籍网站推荐推荐"><a href="#书籍网站推荐推荐" class="headerlink" title="书籍网站推荐推荐"></a>书籍网站推荐推荐</h2><h3 id="JS相关"><a href="#JS相关" class="headerlink" title="JS相关"></a>JS相关</h3><ol><li>JavaScript 权威指南(第6版)</li><li>JavaScript 高级程序设计(第3版)</li><li>JavaScript 语言精粹</li></ol><blockquote><p>1和2为基础,二选一去看,3为使用技巧上的一些东西</p></blockquote>]]></content>
<summary type="html">
<h2 id="原生-Hybird-RN"><a href="#原生-Hybird-RN" class="headerlink" title="原生/Hybird/RN"></a>原生/Hybird/RN</h2><h3 id="对比"><a href="#对比" class="headerlink" title="对比"></a>对比</h3><table>
<thead>
<tr>
<th>形式</th>
<th>介绍</th>
<th>优势</th>
<th>劣势</th>
</tr>
</thead>
<tbody>
<tr>
<td>原生</td>
<td>使用原生语言进行编写开发</td>
<td>性能好/有生态圈的优势点</td>
<td>各个平台有各自的语言,学习成本大,不兼容</td>
</tr>
<tr>
<td>Hybrid App混合开发</td>
<td>使用前段代码,在各个平台上有他的容器WebView,利用他进行渲染</td>
<td>跨平台,适合简单应用</td>
<td>性能不是很好 </td>
</tr>
<tr>
<td>RN</td>
<td>较于前面两者之间,使用JS开发,翻译成原生组件进行展示</td>
<td>学习成本低,天然跨平台,无审核热更新,具有优秀的社区能力(开源库)</td>
<td>性能</td>
<td></td>
</tr>
</tbody>
</table>
<h2 id="ECMAScript"><a href="#ECMAScript" class="headerlink" title="ECMAScript"></a>ECMAScript</h2><p>名词解释:ECMAScript是一种由Ecma国际(前身为欧洲计算机制造商协会,英文名称是European Computer Manufacturers Association)通过ECMA-262标准化的脚本程序设计语言。这种语言在万维网上应用广泛,它往往被称为JavaScript或JScript,但实际上后两者是ECMA-262标准的实现和扩展。</p>
<p>简单的说:ECMAScript是一种标准文本语法,JS是根据其进行设计的</p>
</summary>
<category term="RN" scheme="http://www.zhz.io/tags/RN/"/>
</entry>
<entry>
<title>ReactiveCocoa - 信号类分析</title>
<link href="http://www.zhz.io/2017/08/18/ReactiveCocoa%E4%BF%A1%E5%8F%B7%E7%B1%BB%E5%88%86%E6%9E%90/"/>
<id>http://www.zhz.io/2017/08/18/ReactiveCocoa信号类分析/</id>
<published>2017-08-18T03:06:40.000Z</published>
<updated>2018-02-12T03:09:02.682Z</updated>
<content type="html"><![CDATA[<h2 id="信号类基础概念及原理"><a href="#信号类基础概念及原理" class="headerlink" title="信号类基础概念及原理"></a>信号类基础概念及原理</h2><h3 id="信号类的集成关系图"><a href="#信号类的集成关系图" class="headerlink" title="信号类的集成关系图"></a>信号类的集成关系图</h3><p><img src="/resources/FA1CDE683EA35A1CCDBAB883F9B0A64D.jpg" alt="IMAGE"></p><a id="more"></a><h3 id="RACSrteam"><a href="#RACSrteam" class="headerlink" title="RACSrteam"></a>RACSrteam</h3><ul><li>解释:所有信号的父类</li><li>作用:建立信号之间的链接,例如map/filter/zip函数</li><li>使用案例:一般情况下,我们不需要去创建它,直接创建会抛出异常。</li></ul><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 已Empty为例。</span></div><div class="line">+ (__kindof RACStream *)empty {</div><div class="line"><span class="built_in">NSString</span> *reason = [<span class="built_in">NSString</span> stringWithFormat:<span class="string">@"%@ must be overridden by subclasses"</span>, <span class="built_in">NSStringFromSelector</span>(_cmd)];</div><div class="line"><span class="keyword">@throw</span> [<span class="built_in">NSException</span> exceptionWithName:<span class="built_in">NSInternalInconsistencyException</span> reason:reason userInfo:<span class="literal">nil</span>];</div><div class="line">}</div></pre></td></tr></table></figure><h2 id="RACSignal"><a href="#RACSignal" class="headerlink" title="RACSignal"></a>RACSignal</h2><h3 id="综述"><a href="#综述" class="headerlink" title="综述"></a>综述</h3><p>RACEmptySignal/RACReturnSignal/RACErrorSignal/RACDynamicSignal都是对于RACSignal的补充,帮其完善已有的方法</p><h3 id="RACEmptySignal"><a href="#RACEmptySignal" class="headerlink" title="RACEmptySignal"></a>RACEmptySignal</h3><ul><li>解释:空信号,用来实现RACSignal的 +empty 方法</li><li>作用:在一些信号的创建的时候,不能返回nil,需要返回一个空信号</li><li>原理:在使用+[RACSignal empty]方法的时候,其内部转到了RACEmptySignal</li></ul><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">@implementation</span> <span class="title">RACSignal</span> (<span class="title">RACStream</span>)</span></div><div class="line">+ (RACSignal *)empty {</div><div class="line"><span class="keyword">return</span> [RACEmptySignal empty];</div><div class="line">}</div><div class="line"><span class="keyword">@end</span></div><div class="line"></div><div class="line"><span class="class"><span class="keyword">@implementation</span> <span class="title">RACEmptySignal</span></span></div><div class="line">+ (RACSignal *)empty {</div><div class="line"><span class="keyword">return</span> [[[<span class="keyword">self</span> alloc] init] setNameWithFormat:<span class="string">@"+empty"</span>];</div><div class="line">}</div><div class="line">- (RACDisposable *)subscribe:(<span class="keyword">id</span><RACSubscriber>)subscriber {</div><div class="line"><span class="built_in">NSCParameterAssert</span>(subscriber != <span class="literal">nil</span>);</div><div class="line"></div><div class="line"><span class="keyword">return</span> [RACScheduler.subscriptionScheduler schedule:^{</div><div class="line">[subscriber sendCompleted];</div><div class="line">}];</div><div class="line">}</div><div class="line"><span class="keyword">@end</span></div></pre></td></tr></table></figure><ul><li>使用案例:<figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">_buttonCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(<span class="keyword">id</span> _Nullable input) {</div><div class="line"> @strongify(<span class="keyword">self</span>);</div><div class="line"> RACSignal *signal = ...;</div><div class="line"> <span class="comment">// 根据条立案来创建信号</span></div><div class="line"> <span class="keyword">return</span> signal;</div><div class="line"> }];</div></pre></td></tr></table></figure></li></ul><blockquote><p>Note: 为了篇幅问题,上面的创建函数方法都被删减过,区别只在于DEBUG和REALSE情况下是够为单例,其他没有问题</p></blockquote><h3 id="RACReturnSignal"><a href="#RACReturnSignal" class="headerlink" title="RACReturnSignal"></a>RACReturnSignal</h3><ul><li>解释:一元信号,用来实现 RACSignal 的 +return: 方法;</li><li>作用:通常用于信号的转换操作,平常用的不多</li><li><p>原理:在使用+[RACSignal return:]方法的时候,其内部转到了RACReturnSignal</p><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">+ (RACSignal *)<span class="keyword">return</span>:(<span class="keyword">id</span>)value {</div><div class="line">RACReturnSignal *signal = [[<span class="keyword">self</span> alloc] init];</div><div class="line">signal->_value = value;</div><div class="line"><span class="keyword">return</span> signal;</div><div class="line">}</div><div class="line"></div><div class="line">- (RACDisposable *)subscribe:(<span class="keyword">id</span><RACSubscriber>)subscriber {</div><div class="line"> <span class="keyword">return</span> [RACScheduler.subscriptionScheduler schedule:^{</div><div class="line"> [subscriber sendNext:<span class="keyword">self</span>.value];</div><div class="line"> [subscriber sendCompleted];</div><div class="line"> }];</div><div class="line">}</div></pre></td></tr></table></figure></li><li><p>使用案例:</p><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">@weakify(<span class="keyword">self</span>);</div><div class="line"> [[[<span class="keyword">self</span>.tf_age rac_textSignal] flattenMap:^RACStream *(<span class="keyword">id</span> value) {</div><div class="line"> <span class="keyword">return</span> [RACReturnSignal <span class="keyword">return</span>:[<span class="built_in">NSString</span> stringWithFormat:<span class="string">@"年龄是:%@"</span>,value]];</div><div class="line"> }] subscribeNext:^(<span class="keyword">id</span> x) {</div><div class="line"> @strongify(<span class="keyword">self</span>);</div><div class="line"> <span class="keyword">self</span>.lb_age.text = x;</div><div class="line"> }];</div></pre></td></tr></table></figure></li></ul><h3 id="RACErrorSignal"><a href="#RACErrorSignal" class="headerlink" title="RACErrorSignal"></a>RACErrorSignal</h3><ul><li>解释:一元信号,用来实现 RACSignal 的 +error: 方法;</li><li>作用:通常用于订阅过程中发生的错误</li><li><p>原理:在使用+[RACSignal error:]方法的时候,其内部转到了RACErrorSignal</p><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">+ (RACSignal *)error:(<span class="built_in">NSError</span> *)error {</div><div class="line">RACErrorSignal *signal = [[<span class="keyword">self</span> alloc] init];</div><div class="line">signal->_error = error;</div><div class="line"><span class="keyword">return</span> signal;</div><div class="line">}</div><div class="line"><span class="comment">// 订阅者直接会收到Error信号</span></div><div class="line">- (RACDisposable *)subscribe:(<span class="keyword">id</span><RACSubscriber>)subscriber {</div><div class="line"> <span class="keyword">return</span> [RACScheduler.subscriptionScheduler schedule:^{</div><div class="line"> [subscriber sendError:<span class="keyword">self</span>.error];</div><div class="line"> }];</div><div class="line">}</div></pre></td></tr></table></figure></li><li><p>使用案例:</p><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">+ (RACSignal *)userRegister:(<span class="built_in">NSString</span> *)loginId</div><div class="line"> authCode:(<span class="built_in">NSString</span> *)authCode</div><div class="line"> password:(<span class="built_in">NSString</span> *)password {</div><div class="line"> <span class="keyword">if</span> (![HZLoginModuleUtils checkPassword:password]) {</div><div class="line"> <span class="keyword">return</span> [RACSignal error:[<span class="built_in">NSError</span> errorWithDomain:<span class="string">@"HZLoginModule"</span></div><div class="line"> code:<span class="number">1</span></div><div class="line"> userInfo:@{<span class="string">@"message"</span>:<span class="string">@"密码格式"</span>}]];</div><div class="line"> }</div><div class="line"> <span class="comment">// balabalabala</span></div><div class="line">}</div></pre></td></tr></table></figure></li></ul><h3 id="RACDynamicSignal"><a href="#RACDynamicSignal" class="headerlink" title="RACDynamicSignal"></a>RACDynamicSignal</h3><ul><li>解释:动态信号,使用一个 block 来实现订阅行为,我们在使用 RACSignal 的 +createSignal: 方法时创建的就是该类的实例;</li><li>作用:每时每刻用的冷信号都是这么创建的</li><li>原理:<ol><li>在使用+[RACSignal createSignal:]方法的时候,其内部转到了RACDynamicSignal</li><li>RACDynamicSignal copy了一份Block</li><li>在RACDynamicSignal收到订阅的时候,通过RACPassthroughSubscriber包装订阅者和disposable将其进行包装</li><li>包装完成之后,建立Scheduler进行Block执行</li><li>提供dispose方法给外界,可以随时进行信号的销毁。注意这个dispose是复合的dispose不是你自己指定的了。<figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div></pre></td><td class="code"><pre><div class="line">+ (RACSignal *)createSignal:(RACDisposable * (^)(<span class="keyword">id</span><RACSubscriber> subscriber))didSubscribe {</div><div class="line">RACDynamicSignal *signal = [[<span class="keyword">self</span> alloc] init];</div><div class="line">signal->_didSubscribe = [didSubscribe <span class="keyword">copy</span>];</div><div class="line"><span class="keyword">return</span> [signal setNameWithFormat:<span class="string">@"+createSignal:"</span>];</div><div class="line">}</div><div class="line"></div><div class="line">- (RACDisposable *)subscribe:(<span class="keyword">id</span><RACSubscriber>)subscriber {</div><div class="line"><span class="built_in">NSCParameterAssert</span>(subscriber != <span class="literal">nil</span>);</div><div class="line"> <span class="comment">// 这里有一个RACCompoundDisposable,这是一个RACDisposable,只不过RACCompoundDisposable可以存放多个RACDisposable 。当RACCompoundDisposable 执行dispose方法时,它所存放的disposable都会被释放。</span></div><div class="line">RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];</div><div class="line"><span class="comment">// 订阅者每一次订阅信号是产生一个 Disposable ,并将其与此次订阅关联起来,通过装饰器 RACPassthroughSubscriber 来做到,装饰器的功能如下:</span></div><div class="line"><span class="comment">// - 包装真正的订阅者,使自己成为订阅者的替代者</span></div><div class="line"> <span class="comment">// - 将真正的订阅者与一个订阅时产生的 Disposable 关联起来。</span></div><div class="line">subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:<span class="keyword">self</span> disposable:disposable];</div><div class="line"></div><div class="line"><span class="keyword">if</span> (<span class="keyword">self</span>.didSubscribe != <span class="literal">NULL</span>) {</div><div class="line"> <span class="comment">// 将当前的运行转接到RACScheduler进行</span></div><div class="line">RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{</div><div class="line"> <span class="comment">// 调用真正的订阅,但是传递的并非之前的订阅者,而是装饰器</span></div><div class="line">RACDisposable *innerDisposable = <span class="keyword">self</span>.didSubscribe(subscriber);</div><div class="line">[disposable addDisposable:innerDisposable];</div><div class="line">}];</div><div class="line"></div><div class="line">[disposable addDisposable:schedulingDisposable];</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">return</span> disposable;</div><div class="line">}</div></pre></td></tr></table></figure></li></ol></li></ul><blockquote><p>Note:RACDynamicSignal 使用 RACPassthroughSubscriber ,订阅者装饰器直接伪装成真正的订阅器,传给 didSubscribe 这个 block 使用。在这个 block 中,会有一些事件发送给订阅者装饰器,而这个订阅者装饰器则根据 disposable 的状态来来决定是否转发给真正的订阅者。disposable 作为返回值,返回给外部,也就是说能够从外部来取消这个订阅了。</p></blockquote><ul><li>使用案例:<figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">RACSignal *aSignal = [RACSignal createSignal:^RACDisposable *(<span class="keyword">id</span><RACSubscriber> subscriber) {</div><div class="line"> <span class="comment">// 此处subscriber为转换后的subscriber</span></div><div class="line"> [subscriber sendNext:<span class="string">@"abc"</span>];</div><div class="line"> [subscriber sendCompleted];</div><div class="line"></div><div class="line"> <span class="keyword">return</span> [RACDisposable disposableWithBlock:^{</div><div class="line"> <span class="built_in">NSLog</span>(<span class="string">@"disposable"</span>);</div><div class="line"> }];</div><div class="line">}];</div></pre></td></tr></table></figure></li></ul><h3 id="RACChannelTerminal"><a href="#RACChannelTerminal" class="headerlink" title="RACChannelTerminal"></a>RACChannelTerminal</h3><p>我们先来看看双向绑定这件事情。<br><img src="/resources/41863AF8A61873D1D38D72BF3C38FC69.jpg" alt="IMAGE"><br>我们如何实现信号从A传到B,又可以从B传到A呢。利用信号的双向传递的话,我们可以用RACSubject,这样A发的信号B就能接到,而且B发的信号A也能够接收到。<br><img src="/resources/3333985DB5A5A590D5966CFDDFDC34A6.jpg" alt="IMAGE"><br>这样也有个问题,因为A是Subject的订阅者又是接受者,A发送给B的东西,A也同样会被接受到,这是我们不希望看到的。</p><ul><li>解释:为了解决数据双向传递,特别设计的一个类</li><li>作用:上面已经有描述不再说了</li><li>案例:<figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 建立双向绑定方式1</span></div><div class="line">RACChannelTo(<span class="keyword">self</span>,string1) = RACChannelTo(<span class="keyword">self</span>,string2);</div><div class="line"><span class="comment">// 建立双向绑定方式2</span></div><div class="line"><span class="keyword">self</span>.channel1 = [[RACKVOChannel alloc] initWithTarget:<span class="keyword">self</span> keyPath:<span class="string">@"string1"</span> nilValue:<span class="literal">nil</span>];</div><div class="line"><span class="keyword">self</span>.channel2 = [[RACKVOChannel alloc] initWithTarget:<span class="keyword">self</span> keyPath:<span class="string">@"string2"</span> nilValue:<span class="literal">nil</span>];</div><div class="line"><span class="keyword">self</span>.channel1[<span class="string">@"followingTerminal"</span>] = <span class="keyword">self</span>.channel2[<span class="string">@"followingTerminal"</span>];</div></pre></td></tr></table></figure></li></ul><blockquote><p>方案2只是方案1的展开,我们平时用的都是RACKVOChannel。所以以此为例</p></blockquote><ul><li>订阅关系<br><img src="/resources/8053428106F17C5443DC8D72D019093C.jpg" alt="IMAGE"><br>特别解释:</li></ul><ol><li>lead中的value等于follow的other<br>3=7 4=6 10=12 11=13</li><li>同一个Terminal中的信号不会进行互相传递,因为使用ignoreValues进行了忽略(只是提及大可忽略这条规则)</li><li>string1和string2并不是真正的订阅者。这边只是为了显示方便给大家演示这么写了。<figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">@implementation</span> <span class="title">RACChannel</span></span></div><div class="line"></div><div class="line">- (<span class="keyword">instancetype</span>)init {</div><div class="line"><span class="keyword">self</span> = [<span class="keyword">super</span> init];</div><div class="line"></div><div class="line">RACReplaySubject *leadingSubject = [[RACReplaySubject replaySubjectWithCapacity:<span class="number">0</span>] setNameWithFormat:<span class="string">@"leadingSubject"</span>];</div><div class="line">RACReplaySubject *followingSubject = [[RACReplaySubject replaySubjectWithCapacity:<span class="number">1</span>] setNameWithFormat:<span class="string">@"followingSubject"</span>];</div><div class="line"></div><div class="line">[[leadingSubject ignoreValues] subscribe:followingSubject];</div><div class="line">[[followingSubject ignoreValues] subscribe:leadingSubject];</div><div class="line"></div><div class="line"> <span class="comment">// 结合下面的Terminal初始化,即可看到leaf的value和follow的other是相等的</span></div><div class="line">_leadingTerminal = [[[RACChannelTerminal alloc] initWithValues:leadingSubject otherTerminal:followingSubject] setNameWithFormat:<span class="string">@"leadingTerminal"</span>];</div><div class="line">_followingTerminal = [[[RACChannelTerminal alloc] initWithValues:followingSubject otherTerminal:leadingSubject] setNameWithFormat:<span class="string">@"followingTerminal"</span>];</div><div class="line"></div><div class="line"><span class="keyword">return</span> <span class="keyword">self</span>;</div><div class="line">}</div><div class="line"><span class="keyword">@end</span></div><div class="line"></div><div class="line"><span class="class"><span class="keyword">@implementation</span> <span class="title">RACChannelTerminal</span></span></div><div class="line">- (<span class="keyword">instancetype</span>)initWithValues:(RACSignal *)values otherTerminal:(<span class="keyword">id</span><RACSubscriber>)otherTerminal {</div><div class="line"><span class="built_in">NSCParameterAssert</span>(values != <span class="literal">nil</span>);</div><div class="line"><span class="built_in">NSCParameterAssert</span>(otherTerminal != <span class="literal">nil</span>);</div><div class="line"> </div><div class="line"><span class="keyword">self</span> = [<span class="keyword">super</span> init];</div><div class="line"></div><div class="line">_values = values;</div><div class="line">_otherTerminal = otherTerminal;</div><div class="line"></div><div class="line"><span class="keyword">return</span> <span class="keyword">self</span>;</div><div class="line">}</div><div class="line"><span class="keyword">@end</span></div></pre></td></tr></table></figure></li></ol><ul><li>发送流程</li></ul><ol><li>String1开始发出了属性改变的信号。</li><li>由1.2.3这个value订阅者进行信号的接收</li><li>由于3=7,3又是热信号,收到信号后进行信号转发发送给13这个订阅者</li><li>由于13=11,11为热信号,收到信号后,发送给订阅者。执行setValue:ForKey:将值进行传递</li></ol><ul><li>看下代码<br>步骤1中的关键代码<figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 这段是KVC注册属性变化之后的信号回调处理</span></div><div class="line">RACDisposable *observationDisposable = [strongTarget rac_observeKeyPath:keyPath options:<span class="built_in">NSKeyValueObservingOptionInitial</span> observer:<span class="literal">nil</span> block:^(<span class="keyword">id</span> value, <span class="built_in">NSDictionary</span> *change, <span class="built_in">BOOL</span> causedByDealloc, <span class="built_in">BOOL</span> affectedOnlyLastComponent) {</div><div class="line"><span class="keyword">if</span> (!causedByDealloc && affectedOnlyLastComponent && <span class="keyword">self</span>.currentThreadData.ignoreNextUpdate) {</div><div class="line">[<span class="keyword">self</span> destroyCurrentThreadData];</div><div class="line"><span class="keyword">return</span>;</div><div class="line">}</div><div class="line"> <span class="comment">// 交给了leadingTerminal进行信号转发</span></div><div class="line">[<span class="keyword">self</span>.leadingTerminal sendNext:value];</div><div class="line">}];</div></pre></td></tr></table></figure></li></ul><p>在Terminal中,我们可以看到其实terminal只是作为包装而已,真正发送和订阅时由value和other来进行订阅和发送的。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div></pre></td><td class="code"><pre><div class="line">@implementation RACChannelTerminal</div><div class="line"></div><div class="line">#pragma mark Lifecycle</div><div class="line"></div><div class="line">- (instancetype)initWithValues:(RACSignal *)values otherTerminal:(id<RACSubscriber>)otherTerminal {</div><div class="line">NSCParameterAssert(values != nil);</div><div class="line">NSCParameterAssert(otherTerminal != nil);</div><div class="line"> </div><div class="line">self = [super init];</div><div class="line"></div><div class="line">_values = values;</div><div class="line">_otherTerminal = otherTerminal;</div><div class="line"></div><div class="line">return self;</div><div class="line">}</div><div class="line"></div><div class="line">#pragma mark RACSignal</div><div class="line"></div><div class="line">- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {</div><div class="line">return [self.values subscribe:subscriber];</div><div class="line">}</div><div class="line"></div><div class="line">#pragma mark <RACSubscriber></div><div class="line"></div><div class="line">- (void)sendNext:(id)value {</div><div class="line">[self.otherTerminal sendNext:value];</div><div class="line">}</div><div class="line"></div><div class="line">- (void)sendError:(NSError *)error {</div><div class="line">[self.otherTerminal sendError:error];</div><div class="line">}</div><div class="line"></div><div class="line">- (void)sendCompleted {</div><div class="line">[self.otherTerminal sendCompleted];</div><div class="line">}</div><div class="line"></div><div class="line">- (void)didSubscribeWithDisposable:(RACCompoundDisposable *)disposable {</div><div class="line">[self.otherTerminal didSubscribeWithDisposable:disposable];</div><div class="line">}</div><div class="line"></div><div class="line">@end</div></pre></td></tr></table></figure></p><p>步骤4中的关键代码:<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line">[[<span class="keyword">self</span>.leadingTerminal</div><div class="line">finally:^{</div><div class="line">[observationDisposable dispose];</div><div class="line">}]</div><div class="line">subscribeNext:^(<span class="keyword">id</span> x) {</div><div class="line"><span class="built_in">NSObject</span> *object = (keyPathComponentsCount > <span class="number">1</span> ? [<span class="keyword">self</span>.target valueForKeyPath:keyPathByDeletingLastKeyPathComponent] : <span class="keyword">self</span>.target);</div><div class="line"><span class="keyword">if</span> (object == <span class="literal">nil</span>) <span class="keyword">return</span>;</div><div class="line"></div><div class="line">[<span class="keyword">self</span> createCurrentThreadData];</div><div class="line"><span class="keyword">self</span>.currentThreadData.ignoreNextUpdate = <span class="literal">YES</span>;</div><div class="line"> <span class="comment">// 可以看到收到信号后,进行setValue:forKey:进行赋值</span></div><div class="line">[object setValue:x ?: nilValue forKey:lastKeyPathComponent];</div><div class="line">} error:^(<span class="built_in">NSError</span> *error) {</div><div class="line"><span class="built_in">NSCAssert</span>(<span class="literal">NO</span>, <span class="string">@"Received error in %@: %@"</span>, <span class="keyword">self</span>, error);</div><div class="line"></div><div class="line"><span class="comment">// Log the error if we're running with assertions disabled.</span></div><div class="line"><span class="built_in">NSLog</span>(<span class="string">@"Received error in %@: %@"</span>, <span class="keyword">self</span>, error);</div><div class="line">}];</div></pre></td></tr></table></figure></p><h2 id="热信号"><a href="#热信号" class="headerlink" title="热信号"></a>热信号</h2><h3 id="RACSubject"><a href="#RACSubject" class="headerlink" title="RACSubject"></a>RACSubject</h3><p>了解RACSubject先扯一扯热冷信号概念<br>冷信号:冷信号只会在被订阅时,向订阅者发送通知。<br>热信号:热信号会在任意时间发送出信号,向所有订阅者发送。<br>举个购买报纸的🌰<br>冷信号:你每次去杂货铺购买报纸,才会拿到报纸<br>热信号:你向报社订阅报纸,报社会主动每天早上给你发报纸,不管你要不要接受,都给你发送</p><p>区别之处冷信号会直接去发送订阅内容<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">- (RACDisposable *)subscribe:(<span class="keyword">id</span><RACSubscriber>)subscriber {</div><div class="line"><span class="keyword">self</span>.didSubscribe(subscriber);</div><div class="line">}</div></pre></td></tr></table></figure></p><p>而热信号并不会及时发送订阅内容,而是在推送数据的时候,遍历订阅人,进行数据的推送<br>盗一张图来用一下<a href="http://draveness.me/racsubject.html" target="_blank" rel="noopener">出处</a><br><img src="/resources/3D51143DC6F75B2AEE05C2D59C8CCFEA.jpg" alt="IMAGE"><br>订阅过程</p><ol><li>初始化一个装饰器</li><li>将订阅者加入当前订阅者数组内</li><li>创建disposable,在订阅者被销毁(指的是被dispose)的同时,将订阅者从订阅列表中进行移除<figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line">- (RACDisposable *)subscribe:(<span class="keyword">id</span><RACSubscriber>)subscriber {</div><div class="line">RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];</div><div class="line">subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:<span class="keyword">self</span> disposable:disposable];</div><div class="line"></div><div class="line"><span class="built_in">NSMutableArray</span> *subscribers = <span class="keyword">self</span>.subscribers;</div><div class="line"><span class="keyword">@synchronized</span> (subscribers) {</div><div class="line">[subscribers addObject:subscriber];</div><div class="line">}</div><div class="line"></div><div class="line">[disposable addDisposable:[RACDisposable disposableWithBlock:^{</div><div class="line"><span class="keyword">@synchronized</span> (subscribers) {</div><div class="line"><span class="built_in">NSUInteger</span> index = [subscribers indexOfObjectWithOptions:<span class="built_in">NSEnumerationReverse</span> passingTest:^ <span class="built_in">BOOL</span> (<span class="keyword">id</span><RACSubscriber> obj, <span class="built_in">NSUInteger</span> index, <span class="built_in">BOOL</span> *stop) {</div><div class="line"><span class="keyword">return</span> obj == subscriber;</div><div class="line">}];</div><div class="line"></div><div class="line"><span class="keyword">if</span> (index != <span class="built_in">NSNotFound</span>) [subscribers removeObjectAtIndex:index];</div><div class="line">}</div><div class="line">}]];</div><div class="line"></div><div class="line"><span class="keyword">return</span> disposable;</div><div class="line">}</div></pre></td></tr></table></figure></li></ol><p><img src="/resources/BC332BF723353C86210F9DA243BB76E3.jpg" alt="IMAGE"><br>发送消息的过程</p><ol><li>遍历当前订阅者列表中有哪些元素</li><li>向订阅者发送订阅的消息<figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line">- (<span class="keyword">void</span>)sendNext:(<span class="keyword">id</span>)value {</div><div class="line">[<span class="keyword">self</span> enumerateSubscribersUsingBlock:^(<span class="keyword">id</span><RACSubscriber> subscriber) {</div><div class="line">[subscriber sendNext:value];</div><div class="line">}];</div><div class="line">}</div><div class="line"></div><div class="line">- (<span class="keyword">void</span>)sendError:(<span class="built_in">NSError</span> *)error {</div><div class="line">[<span class="keyword">self</span>.disposable dispose];</div><div class="line"></div><div class="line">[<span class="keyword">self</span> enumerateSubscribersUsingBlock:^(<span class="keyword">id</span><RACSubscriber> subscriber) {</div><div class="line">[subscriber sendError:error];</div><div class="line">}];</div><div class="line">}</div><div class="line"></div><div class="line">- (<span class="keyword">void</span>)sendCompleted {</div><div class="line">[<span class="keyword">self</span>.disposable dispose];</div><div class="line"></div><div class="line">[<span class="keyword">self</span> enumerateSubscribersUsingBlock:^(<span class="keyword">id</span><RACSubscriber> subscriber) {</div><div class="line">[subscriber sendCompleted];</div><div class="line">}];</div><div class="line">}</div></pre></td></tr></table></figure></li></ol><p>冷信号的使用<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(<span class="keyword">id</span><RACSubscriber> _Nonnull subscriber) {</div><div class="line"> [subscriber sendNext:<span class="string">@"😜"</span>];</div><div class="line"> <span class="keyword">return</span> <span class="literal">nil</span>;</div><div class="line">}];</div><div class="line"></div><div class="line">[signal subscribeNext:^(<span class="keyword">id</span> _Nullable x) {</div><div class="line"> <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>,x); </div><div class="line">}];</div></pre></td></tr></table></figure></p><p>输出<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">HZReactiveCocoaTest_Example[<span class="number">52485</span>:<span class="number">35373322</span>] 😜</div></pre></td></tr></table></figure></p><p>热信号的使用<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line">RACSubject *subject = [RACSubject subject];</div><div class="line"></div><div class="line"><span class="comment">// Subscriber 1</span></div><div class="line">[subject subscribeNext:^(<span class="keyword">id</span> _Nullable x) {</div><div class="line"> <span class="built_in">NSLog</span>(<span class="string">@"1st Sub: %@"</span>, x);</div><div class="line">}];</div><div class="line">[subject sendNext:@<span class="number">1</span>];</div><div class="line"></div><div class="line"><span class="comment">// Subscriber 2</span></div><div class="line">[subject subscribeNext:^(<span class="keyword">id</span> _Nullable x) {</div><div class="line"> <span class="built_in">NSLog</span>(<span class="string">@"2nd Sub: %@"</span>, x);</div><div class="line">}];</div><div class="line">[subject sendNext:@<span class="number">2</span>];</div><div class="line"></div><div class="line"><span class="comment">// Subscriber 3</span></div><div class="line">[subject subscribeNext:^(<span class="keyword">id</span> _Nullable x) {</div><div class="line"> <span class="built_in">NSLog</span>(<span class="string">@"3rd Sub: %@"</span>, x);</div><div class="line">}];</div><div class="line">[subject sendNext:@<span class="number">3</span>];</div><div class="line">[subject sendCompleted];</div></pre></td></tr></table></figure></p><p>输出<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">HZReactiveCocoaTest_Example[<span class="number">52559</span>:<span class="number">35393828</span>] <span class="number">1</span>st Sub: <span class="number">1</span></div><div class="line">HZReactiveCocoaTest_Example[<span class="number">52559</span>:<span class="number">35393828</span>] <span class="number">1</span>st Sub: <span class="number">2</span></div><div class="line">HZReactiveCocoaTest_Example[<span class="number">52559</span>:<span class="number">35393828</span>] <span class="number">2</span>nd Sub: <span class="number">2</span></div><div class="line">HZReactiveCocoaTest_Example[<span class="number">52559</span>:<span class="number">35393828</span>] <span class="number">1</span>st Sub: <span class="number">3</span></div><div class="line">HZReactiveCocoaTest_Example[<span class="number">52559</span>:<span class="number">35393828</span>] <span class="number">2</span>nd Sub: <span class="number">3</span></div><div class="line">HZReactiveCocoaTest_Example[<span class="number">52559</span>:<span class="number">35393828</span>] <span class="number">3</span>rd Sub: <span class="number">3</span></div></pre></td></tr></table></figure></p><p>盗一张图来用一下<a href="http://draveness.me/racsubject.html" target="_blank" rel="noopener">出处</a><br><img src="/resources/AD49783AEA246D939670EF5FE1E5B0CA.jpg" alt="IMAGE"><br>热信号不会再订阅的时候向订阅人去发送消息,只会在消息发送出去时候,向订阅者发送消息</p><h3 id="RACGroupedSignal"><a href="#RACGroupedSignal" class="headerlink" title="RACGroupedSignal"></a>RACGroupedSignal</h3><ul><li>解释:分组信号,用来实现 RACSignal 的分组功能;</li><li>作用:信号中包含一个key的属性,用来分组</li><li><p>原理:</p><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">+ (<span class="keyword">instancetype</span>)signalWithKey:(<span class="keyword">id</span><<span class="built_in">NSCopying</span>>)key {</div><div class="line">RACGroupedSignal *subject = [<span class="keyword">self</span> subject];</div><div class="line">subject.key = key;</div><div class="line"><span class="keyword">return</span> subject;</div><div class="line">}</div></pre></td></tr></table></figure></li><li><p>案例:</p><ol><li>配合groupBy:transform:进行信号的组合</li><li>所有数字大于3的生成一个good为key的RACGroupedSignal,小于3的会生成一个bad的RACGroupedSignal<figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div></pre></td><td class="code"><pre><div class="line">RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(<span class="keyword">id</span><RACSubscriber> subscriber)</div><div class="line"> {</div><div class="line"> [subscriber sendNext:@<span class="number">1</span>];</div><div class="line"> [subscriber sendNext:@<span class="number">2</span>];</div><div class="line"> [subscriber sendNext:@<span class="number">3</span>];</div><div class="line"> [subscriber sendNext:@<span class="number">4</span>];</div><div class="line"> [subscriber sendNext:@<span class="number">5</span>];</div><div class="line"> [subscriber sendCompleted];</div><div class="line"> <span class="keyword">return</span> [RACDisposable disposableWithBlock:^{</div><div class="line"> <span class="built_in">NSLog</span>(<span class="string">@"signal dispose"</span>);</div><div class="line"> }];</div><div class="line"> }];</div><div class="line"> <span class="comment">// 这段代码还无法完全解释,关于filter机制方面还不是很懂,再研究研究</span></div><div class="line"> RACSignal *signalGroup = [signalA groupBy:^<span class="keyword">id</span><<span class="built_in">NSCopying</span>>(<span class="built_in">NSNumber</span> *object) {</div><div class="line"> <span class="keyword">return</span> object.integerValue > <span class="number">3</span> ? <span class="string">@"good"</span> : <span class="string">@"bad"</span>;</div><div class="line"> } transform:^<span class="keyword">id</span>(<span class="built_in">NSNumber</span> * object) {</div><div class="line"> <span class="keyword">return</span> @(object.integerValue * <span class="number">10</span>);</div><div class="line"> }];</div><div class="line"></div><div class="line"> [[[signalGroup filter:^<span class="built_in">BOOL</span>(RACGroupedSignal *value) {</div><div class="line"> <span class="keyword">return</span> [(<span class="built_in">NSString</span> *)value.key isEqualToString:<span class="string">@"good"</span>];</div><div class="line"> }] flatten]subscribeNext:^(<span class="keyword">id</span> x) {</div><div class="line"> <span class="built_in">NSLog</span>(<span class="string">@"subscribeNext: %@"</span>, x);</div><div class="line"> }];</div></pre></td></tr></table></figure></li></ol></li></ul><p>输出<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">HZReactiveCocoaTest_Example[<span class="number">55955</span>:<span class="number">37029147</span>] subscribeNext: <span class="number">40</span></div><div class="line">HZReactiveCocoaTest_Example[<span class="number">55955</span>:<span class="number">37029147</span>] subscribeNext: <span class="number">50</span></div><div class="line">HZReactiveCocoaTest_Example[<span class="number">55955</span>:<span class="number">37029147</span>] signal dispose</div></pre></td></tr></table></figure></p><h3 id="RACBehaviorSubject"><a href="#RACBehaviorSubject" class="headerlink" title="RACBehaviorSubject"></a>RACBehaviorSubject</h3><ul><li>解释:RACBehaviorSubject是对RACSubject的扩充</li><li>作用:在订阅时会向订阅者发送最新的消息,内部会保存一个currentValue即消息的最新值</li><li>原理:内部有一个currentValue属性将数据进行保存。每次sendNext的时候就会将其进行发送</li><li><p>注意点:每次订阅的时候,会默认的去发送一次sendNext:</p><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">@property</span> (<span class="keyword">nonatomic</span>, <span class="keyword">strong</span>) <span class="keyword">id</span> currentValue;</div><div class="line">- (<span class="keyword">void</span>)sendNext:(<span class="keyword">id</span>)value {</div><div class="line"><span class="keyword">@synchronized</span> (<span class="keyword">self</span>) {</div><div class="line"><span class="keyword">self</span>.currentValue = value;</div><div class="line">[<span class="keyword">super</span> sendNext:value];</div><div class="line">}</div><div class="line">}</div><div class="line">- (RACDisposable *)subscribe:(<span class="keyword">id</span><RACSubscriber>)subscriber {</div><div class="line">RACDisposable *subscriptionDisposable = [<span class="keyword">super</span> subscribe:subscriber];</div><div class="line">RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{</div><div class="line"><span class="keyword">@synchronized</span> (<span class="keyword">self</span>) {</div><div class="line">[subscriber sendNext:<span class="keyword">self</span>.currentValue];</div><div class="line">}</div><div class="line">}];</div><div class="line"></div><div class="line"><span class="keyword">return</span> [RACDisposable disposableWithBlock:^{</div><div class="line">[subscriptionDisposable dispose];</div><div class="line">[schedulingDisposable dispose];</div><div class="line">}];</div><div class="line">}</div></pre></td></tr></table></figure></li><li><p>案例:</p><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line">RACBehaviorSubject *subject = [RACBehaviorSubject subject];</div><div class="line"></div><div class="line">[subject subscribeNext:^(<span class="keyword">id</span> _Nullable x) {</div><div class="line"> <span class="built_in">NSLog</span>(<span class="string">@"1st Sub: %@"</span>, x);</div><div class="line">}];</div><div class="line">[subject sendNext:@<span class="number">1</span>];</div><div class="line"></div><div class="line">[subject subscribeNext:^(<span class="keyword">id</span> _Nullable x) {</div><div class="line"> <span class="built_in">NSLog</span>(<span class="string">@"2nd Sub: %@"</span>, x);</div><div class="line">}];</div><div class="line">[subject sendNext:@<span class="number">2</span>];</div><div class="line"></div><div class="line">[subject subscribeNext:^(<span class="keyword">id</span> _Nullable x) {</div><div class="line"> <span class="built_in">NSLog</span>(<span class="string">@"3rd Sub: %@"</span>, x);</div><div class="line">}];</div><div class="line">[subject sendNext:@<span class="number">3</span>];</div><div class="line">[subject sendCompleted];</div></pre></td></tr></table></figure></li></ul><p>嗯,又要盗图了<br><img src="/resources/9A0C0AC5F26D27C5690B6E7496306B79.jpg" alt="IMAGE"><br>因为默认会发送一次,所以创建的时候允许创建默认值<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">RACBehaviorSubject *subject = [RACBehaviorSubject behaviorSubjectWithDefaultValue:@<span class="number">0</span>];</div></pre></td></tr></table></figure></p><h3 id="RACReplaySubject"><a href="#RACReplaySubject" class="headerlink" title="RACReplaySubject"></a>RACReplaySubject</h3><ul><li>解释:RACReplaySubject则是对RACBehaviorSubject的再一次加强,是保存所有值,而不仅仅是保存最新值</li><li>作用:在订阅时会向订阅者发送热信号创建以来发送过的所有数据</li><li>原理:内部包含有消息数组,默认存储量是无符号整型的最大值<br>发送过程<figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">- (<span class="keyword">void</span>)sendNext:(<span class="keyword">id</span>)value {</div><div class="line"><span class="keyword">@synchronized</span> (<span class="keyword">self</span>) {</div><div class="line">[<span class="keyword">self</span>.valuesReceived addObject:value ?: RACTupleNil.tupleNil];</div><div class="line">[<span class="keyword">super</span> sendNext:value];</div><div class="line"></div><div class="line"><span class="keyword">if</span> (<span class="keyword">self</span>.capacity != RACReplaySubjectUnlimitedCapacity && <span class="keyword">self</span>.valuesReceived.count > <span class="keyword">self</span>.capacity) {</div><div class="line">[<span class="keyword">self</span>.valuesReceived removeObjectsInRange:<span class="built_in">NSMakeRange</span>(<span class="number">0</span>, <span class="keyword">self</span>.valuesReceived.count - <span class="keyword">self</span>.capacity)];</div><div class="line">}</div><div class="line">}</div><div class="line">}</div></pre></td></tr></table></figure></li></ul><p>订阅过程<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div></pre></td><td class="code"><pre><div class="line">- (RACDisposable *)subscribe:(<span class="keyword">id</span><RACSubscriber>)subscriber {</div><div class="line">RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable];</div><div class="line"></div><div class="line">RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{</div><div class="line"><span class="keyword">@synchronized</span> (<span class="keyword">self</span>) {</div><div class="line"><span class="keyword">for</span> (<span class="keyword">id</span> value <span class="keyword">in</span> <span class="keyword">self</span>.valuesReceived) {</div><div class="line"><span class="keyword">if</span> (compoundDisposable.disposed) <span class="keyword">return</span>;</div><div class="line"></div><div class="line">[subscriber sendNext:(value == RACTupleNil.tupleNil ? <span class="literal">nil</span> : value)];</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">if</span> (compoundDisposable.disposed) <span class="keyword">return</span>;</div><div class="line"></div><div class="line"><span class="keyword">if</span> (<span class="keyword">self</span>.hasCompleted) {</div><div class="line">[subscriber sendCompleted];</div><div class="line">} <span class="keyword">else</span> <span class="keyword">if</span> (<span class="keyword">self</span>.hasError) {</div><div class="line">[subscriber sendError:<span class="keyword">self</span>.error];</div><div class="line">} <span class="keyword">else</span> {</div><div class="line">RACDisposable *subscriptionDisposable = [<span class="keyword">super</span> subscribe:subscriber];</div><div class="line">[compoundDisposable addDisposable:subscriptionDisposable];</div><div class="line">}</div><div class="line">}</div><div class="line">}];</div><div class="line"></div><div class="line">[compoundDisposable addDisposable:schedulingDisposable];</div><div class="line"></div><div class="line"><span class="keyword">return</span> compoundDisposable;</div><div class="line">}</div></pre></td></tr></table></figure></p><ul><li><p>注意点:当然你也可以设置存储历史消息的数量,跟behavior一样,已创建就会发送历史消息</p><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">+ (<span class="keyword">instancetype</span>)replaySubjectWithCapacity:(<span class="built_in">NSUInteger</span>)capacity {</div><div class="line"><span class="keyword">return</span> [(RACReplaySubject *)[<span class="keyword">self</span> alloc] initWithCapacity:capacity];</div><div class="line">}</div></pre></td></tr></table></figure></li><li><p>使用案例</p><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line">RACReplaySubject *subject = [RACReplaySubject subject];</div><div class="line"></div><div class="line">[subject subscribeNext:^(<span class="keyword">id</span> _Nullable x) {</div><div class="line"> <span class="built_in">NSLog</span>(<span class="string">@"1st Subscriber: %@"</span>, x);</div><div class="line">}];</div><div class="line">[subject sendNext:@<span class="number">1</span>];</div><div class="line"></div><div class="line">[subject subscribeNext:^(<span class="keyword">id</span> _Nullable x) {</div><div class="line"> <span class="built_in">NSLog</span>(<span class="string">@"2nd Subscriber: %@"</span>, x);</div><div class="line">}];</div><div class="line">[subject sendNext:@<span class="number">2</span>];</div><div class="line"></div><div class="line">[subject subscribeNext:^(<span class="keyword">id</span> _Nullable x) {</div><div class="line"> <span class="built_in">NSLog</span>(<span class="string">@"3rd Subscriber: %@"</span>, x);</div><div class="line">}];</div><div class="line">[subject sendNext:@<span class="number">3</span>];</div><div class="line">[subject sendCompleted];</div></pre></td></tr></table></figure></li></ul><p>0 0请允许我再盗一次图<br><img src="/resources/2246DA721B846E004E6485B304F0E2A5.jpg" alt="IMAGE"></p><h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><p><a href="http://www.jianshu.com/p/912c69dfb55a" target="_blank" rel="noopener">ReactiveCocoa信号发送详解</a><br><a href="http://draveness.me/racchannel" target="_blank" rel="noopener">RAC 中的双向数据绑定</a><br><a href="http://draveness.me/racsubject.html" target="_blank" rel="noopener">RACSubject</a><br><a href="http://www.jianshu.com/p/707ece08257e" target="_blank" rel="noopener">RACGroupedSignal</a></p>]]></content>
<summary type="html">
<h2 id="信号类基础概念及原理"><a href="#信号类基础概念及原理" class="headerlink" title="信号类基础概念及原理"></a>信号类基础概念及原理</h2><h3 id="信号类的集成关系图"><a href="#信号类的集成关系图" class="headerlink" title="信号类的集成关系图"></a>信号类的集成关系图</h3><p><img src="/resources/FA1CDE683EA35A1CCDBAB883F9B0A64D.jpg" alt="IMAGE"></p>
</summary>
<category term="ReactiveCocoa" scheme="http://www.zhz.io/tags/ReactiveCocoa/"/>
</entry>
<entry>
<title>ReactiveCocoa - 双向绑定,不完全解析</title>
<link href="http://www.zhz.io/2017/08/17/ReactiveCocoa-%E5%8F%8C%E5%90%91%E7%BB%91%E5%AE%9A%EF%BC%8C%E4%B8%8D%E5%AE%8C%E5%85%A8%E8%A7%A3%E6%9E%90/"/>
<id>http://www.zhz.io/2017/08/17/ReactiveCocoa-双向绑定,不完全解析/</id>
<published>2017-08-17T03:00:56.000Z</published>
<updated>2018-02-12T03:03:08.615Z</updated>
<content type="html"><![CDATA[<h3 id="RACChannelTerminal"><a href="#RACChannelTerminal" class="headerlink" title="RACChannelTerminal"></a>RACChannelTerminal</h3><p>我们先来看看双向绑定这件事情。<br><img src="/resources/41863AF8A61873D1D38D72BF3C38FC69.jpg" alt="IMAGE"><br>我们如何实现信号从A传到B,又可以从B传到A呢。利用信号的双向传递的话,我们可以用RACSubject,这样A发的信号B就能接到,而且B发的信号A也能够接收到。<br><img src="/resources/3333985DB5A5A590D5966CFDDFDC34A6.jpg" alt="IMAGE"><br><a id="more"></a><br>这样也有个问题,因为A是Subject的订阅者又是接受者,A发送给B的东西,A也同样会被接受到,这是我们不希望看到的。</p><ul><li>解释:为了解决数据双向传递,特别设计的一个类</li><li>作用:上面已经有描述不再说了</li><li>案例:<figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 建立双向绑定方式1</span></div><div class="line">RACChannelTo(<span class="keyword">self</span>,string1) = RACChannelTo(<span class="keyword">self</span>,string2);</div><div class="line"><span class="comment">// 建立双向绑定方式2</span></div><div class="line"><span class="keyword">self</span>.channel1 = [[RACKVOChannel alloc] initWithTarget:<span class="keyword">self</span> keyPath:<span class="string">@"string1"</span> nilValue:<span class="literal">nil</span>];</div><div class="line"><span class="keyword">self</span>.channel2 = [[RACKVOChannel alloc] initWithTarget:<span class="keyword">self</span> keyPath:<span class="string">@"string2"</span> nilValue:<span class="literal">nil</span>];</div><div class="line"><span class="keyword">self</span>.channel1[<span class="string">@"followingTerminal"</span>] = <span class="keyword">self</span>.channel2[<span class="string">@"followingTerminal"</span>];</div></pre></td></tr></table></figure></li></ul><blockquote><p>方案2只是方案1的展开,我们平时用的都是RACKVOChannel。所以以此为例</p></blockquote><ul><li>订阅关系<br><img src="/resources/8053428106F17C5443DC8D72D019093C.jpg" alt="IMAGE"><br>特别解释:</li></ul><ol><li>lead中的value等于follow的other<br>3=7 4=6 10=12 11=13</li><li>同一个Terminal中的信号不会进行互相传递,因为使用ignoreValues进行了忽略(只是提及大可忽略这条规则)</li><li>string1和string2并不是真正的订阅者。这边只是为了显示方便给大家演示这么写了。<figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">@implementation</span> <span class="title">RACChannel</span></span></div><div class="line"></div><div class="line">- (<span class="keyword">instancetype</span>)init {</div><div class="line"><span class="keyword">self</span> = [<span class="keyword">super</span> init];</div><div class="line"></div><div class="line">RACReplaySubject *leadingSubject = [[RACReplaySubject replaySubjectWithCapacity:<span class="number">0</span>] setNameWithFormat:<span class="string">@"leadingSubject"</span>];</div><div class="line">RACReplaySubject *followingSubject = [[RACReplaySubject replaySubjectWithCapacity:<span class="number">1</span>] setNameWithFormat:<span class="string">@"followingSubject"</span>];</div><div class="line"></div><div class="line">[[leadingSubject ignoreValues] subscribe:followingSubject];</div><div class="line">[[followingSubject ignoreValues] subscribe:leadingSubject];</div><div class="line"></div><div class="line"> <span class="comment">// 结合下面的Terminal初始化,即可看到leaf的value和follow的other是相等的</span></div><div class="line">_leadingTerminal = [[[RACChannelTerminal alloc] initWithValues:leadingSubject otherTerminal:followingSubject] setNameWithFormat:<span class="string">@"leadingTerminal"</span>];</div><div class="line">_followingTerminal = [[[RACChannelTerminal alloc] initWithValues:followingSubject otherTerminal:leadingSubject] setNameWithFormat:<span class="string">@"followingTerminal"</span>];</div><div class="line"></div><div class="line"><span class="keyword">return</span> <span class="keyword">self</span>;</div><div class="line">}</div><div class="line"><span class="keyword">@end</span></div><div class="line"></div><div class="line"><span class="class"><span class="keyword">@implementation</span> <span class="title">RACChannelTerminal</span></span></div><div class="line">- (<span class="keyword">instancetype</span>)initWithValues:(RACSignal *)values otherTerminal:(<span class="keyword">id</span><RACSubscriber>)otherTerminal {</div><div class="line"><span class="built_in">NSCParameterAssert</span>(values != <span class="literal">nil</span>);</div><div class="line"><span class="built_in">NSCParameterAssert</span>(otherTerminal != <span class="literal">nil</span>);</div><div class="line"> </div><div class="line"><span class="keyword">self</span> = [<span class="keyword">super</span> init];</div><div class="line"></div><div class="line">_values = values;</div><div class="line">_otherTerminal = otherTerminal;</div><div class="line"></div><div class="line"><span class="keyword">return</span> <span class="keyword">self</span>;</div><div class="line">}</div><div class="line"><span class="keyword">@end</span></div></pre></td></tr></table></figure></li></ol><ul><li>发送流程</li></ul><ol><li>String1开始发出了属性改变的信号。</li><li>由1.2.3这个value订阅者进行信号的接收</li><li>由于3=7,3又是热信号,收到信号后进行信号转发发送给13这个订阅者</li><li>由于13=11,11为热信号,收到信号后,发送给订阅者。执行setValue:ForKey:将值进行传递</li></ol><ul><li>看下代码<br>步骤1中的关键代码<figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 这段是KVC注册属性变化之后的信号回调处理</span></div><div class="line">RACDisposable *observationDisposable = [strongTarget rac_observeKeyPath:keyPath options:<span class="built_in">NSKeyValueObservingOptionInitial</span> observer:<span class="literal">nil</span> block:^(<span class="keyword">id</span> value, <span class="built_in">NSDictionary</span> *change, <span class="built_in">BOOL</span> causedByDealloc, <span class="built_in">BOOL</span> affectedOnlyLastComponent) {</div><div class="line"><span class="keyword">if</span> (!causedByDealloc && affectedOnlyLastComponent && <span class="keyword">self</span>.currentThreadData.ignoreNextUpdate) {</div><div class="line">[<span class="keyword">self</span> destroyCurrentThreadData];</div><div class="line"><span class="keyword">return</span>;</div><div class="line">}</div><div class="line"> <span class="comment">// 交给了leadingTerminal进行信号转发</span></div><div class="line">[<span class="keyword">self</span>.leadingTerminal sendNext:value];</div><div class="line">}];</div></pre></td></tr></table></figure></li></ul><p>在Terminal中,我们可以看到其实terminal只是作为包装而已,真正发送和订阅时由value和other来进行订阅和发送的。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div></pre></td><td class="code"><pre><div class="line">@implementation RACChannelTerminal</div><div class="line"></div><div class="line">#pragma mark Lifecycle</div><div class="line"></div><div class="line">- (instancetype)initWithValues:(RACSignal *)values otherTerminal:(id<RACSubscriber>)otherTerminal {</div><div class="line">NSCParameterAssert(values != nil);</div><div class="line">NSCParameterAssert(otherTerminal != nil);</div><div class="line"> </div><div class="line">self = [super init];</div><div class="line"></div><div class="line">_values = values;</div><div class="line">_otherTerminal = otherTerminal;</div><div class="line"></div><div class="line">return self;</div><div class="line">}</div><div class="line"></div><div class="line">#pragma mark RACSignal</div><div class="line"></div><div class="line">- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {</div><div class="line">return [self.values subscribe:subscriber];</div><div class="line">}</div><div class="line"></div><div class="line">#pragma mark <RACSubscriber></div><div class="line"></div><div class="line">- (void)sendNext:(id)value {</div><div class="line">[self.otherTerminal sendNext:value];</div><div class="line">}</div><div class="line"></div><div class="line">- (void)sendError:(NSError *)error {</div><div class="line">[self.otherTerminal sendError:error];</div><div class="line">}</div><div class="line"></div><div class="line">- (void)sendCompleted {</div><div class="line">[self.otherTerminal sendCompleted];</div><div class="line">}</div><div class="line"></div><div class="line">- (void)didSubscribeWithDisposable:(RACCompoundDisposable *)disposable {</div><div class="line">[self.otherTerminal didSubscribeWithDisposable:disposable];</div><div class="line">}</div><div class="line"></div><div class="line">@end</div></pre></td></tr></table></figure></p><p>步骤4中的关键代码:<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line">[[<span class="keyword">self</span>.leadingTerminal</div><div class="line">finally:^{</div><div class="line">[observationDisposable dispose];</div><div class="line">}]</div><div class="line">subscribeNext:^(<span class="keyword">id</span> x) {</div><div class="line"><span class="built_in">NSObject</span> *object = (keyPathComponentsCount > <span class="number">1</span> ? [<span class="keyword">self</span>.target valueForKeyPath:keyPathByDeletingLastKeyPathComponent] : <span class="keyword">self</span>.target);</div><div class="line"><span class="keyword">if</span> (object == <span class="literal">nil</span>) <span class="keyword">return</span>;</div><div class="line"></div><div class="line">[<span class="keyword">self</span> createCurrentThreadData];</div><div class="line"><span class="keyword">self</span>.currentThreadData.ignoreNextUpdate = <span class="literal">YES</span>;</div><div class="line"> <span class="comment">// 可以看到收到信号后,进行setValue:forKey:进行赋值</span></div><div class="line">[object setValue:x ?: nilValue forKey:lastKeyPathComponent];</div><div class="line">} error:^(<span class="built_in">NSError</span> *error) {</div><div class="line"><span class="built_in">NSCAssert</span>(<span class="literal">NO</span>, <span class="string">@"Received error in %@: %@"</span>, <span class="keyword">self</span>, error);</div><div class="line"></div><div class="line"><span class="comment">// Log the error if we're running with assertions disabled.</span></div><div class="line"><span class="built_in">NSLog</span>(<span class="string">@"Received error in %@: %@"</span>, <span class="keyword">self</span>, error);</div><div class="line">}];</div></pre></td></tr></table></figure></p>]]></content>
<summary type="html">
<h3 id="RACChannelTerminal"><a href="#RACChannelTerminal" class="headerlink" title="RACChannelTerminal"></a>RACChannelTerminal</h3><p>我们先来看看双向绑定这件事情。<br><img src="/resources/41863AF8A61873D1D38D72BF3C38FC69.jpg" alt="IMAGE"><br>我们如何实现信号从A传到B,又可以从B传到A呢。利用信号的双向传递的话,我们可以用RACSubject,这样A发的信号B就能接到,而且B发的信号A也能够接收到。<br><img src="/resources/3333985DB5A5A590D5966CFDDFDC34A6.jpg" alt="IMAGE"><br>
</summary>
<category term="ReactiveCocoa" scheme="http://www.zhz.io/tags/ReactiveCocoa/"/>
</entry>
<entry>
<title>Advanced_Apple_Debugging阅读笔记Part1</title>
<link href="http://www.zhz.io/2017/06/05/Advanced-Apple-Debugging%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0Part1/"/>
<id>http://www.zhz.io/2017/06/05/Advanced-Apple-Debugging阅读笔记Part1/</id>
<published>2017-06-05T13:54:27.000Z</published>
<updated>2017-06-05T13:57:22.000Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>本文为<a href="https://store.raywenderlich.com/products/advanced-apple-debugging-and-reverse-engineering" target="_blank" rel="noopener">Advanced Apple Debugging & Reverse Engineering</a>的第一章阅读笔记,<br>有不正之处望指正</p><h2 id="基础"><a href="#基础" class="headerlink" title="基础"></a>基础</h2><p>关闭Rootless模式</p><ul><li>重启电脑,启动后长按Commond+R</li><li>进入控制台</li><li>输入 csrutil disable; reboot</li><li>不久后就会重启电脑</li></ul><h3 id="File"><a href="#File" class="headerlink" title="File"></a>File</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">$ lldb /Projects/Sketch/build/Debug/Sketch.app </div><div class="line">>> Current executable set to '/Projects/Sketch/build/Debug/Sketch.app' (x86_64)</div></pre></td></tr></table></figure><p>等同于<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">$ lldb </div><div class="line">(lldb) file /Projects/Sketch/build/Debug/Sketch.app </div><div class="line">>> Current executable set to '/Projects/Sketch/build/Debug/Sketch.app' (x86_64).</div></pre></td></tr></table></figure></p><a id="more"></a><h3 id="process"><a href="#process" class="headerlink" title="process"></a>process</h3><ul><li>launch 启动执行目录的程序</li></ul><h3 id="image"><a href="#image" class="headerlink" title="image"></a>image</h3><ul><li><p>lookup </p><ul><li><p>-n NAME 查找具体代码</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">(lldb) image lookup -n "-[UIViewController viewDidLoad]"</div></pre></td></tr></table></figure></li><li><p>-rn NAME 查找所有符合NAME规则的符号。符号可以正则,所以说符合规则</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">(lldb) image lookup -rn test</div><div class="line">#Swift</div><div class="line">(lldb) image lookup -rn Signals.SwiftTestClass.name.setter </div><div class="line">2 matches found in /Users/derekselander/Library/Developer/Xcode/DerivedData/Signals-bqrjxlceauwfuihjesxmgfodimef/Build/Products/Debug-iphonesimulator/Signals.app/Signals:</div><div class="line"> Address: Signals[0x000000010000aba0] (Signals.__TEXT.__text +38704)</div><div class="line"> Summary: Signals`@objc Signals.SwiftTestClass.name.setter :raywenderlich.com 47</div><div class="line"> Advanced Apple Debugging Chapter 4: Stopping in Code Swift.ImplicitlyUnwrappedOptional<Swift.String> at SwiftTestClass.swift</div><div class="line"> Address: Signals[0x000000010000ac60] (Signals.__TEXT.__text + 38896)</div><div class="line"> Summary: Signals`Signals.SwiftTestClass.name.setter :Swift.ImplicitlyUnwrappedOptional<Swift.String> at SwiftTestClass.swift</div><div class="line">#Mark 一下这边出现了两个,是因为这个工程既有Swift代码也有OC代码,苹果为了做桥接加了第一个后续可以无视</div></pre></td></tr></table></figure></li></ul></li></ul><h3 id="Breakpoint"><a href="#Breakpoint" class="headerlink" title="Breakpoint"></a>Breakpoint</h3><ul><li><p>设置断点</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div></pre></td><td class="code"><pre><div class="line">// 简写的形式</div><div class="line">(lldb) b -[UIViewController viewDidLoad]</div><div class="line"> Breakpoint 1: where = UIKit`-[UIViewController viewDidLoad], address = 0x0000000102bbd788</div><div class="line">//如果想在某个文件中的某行设置一个断点,可使用以下命令:</div><div class="line">(lldb) breakpoint set --file foo.c --line 12</div><div class="line">//如果想给某个函数设置断点,可使用以下命令:</div><div class="line">(lldb) breakpoint set --name foo</div><div class="line">//如果想给C++中所有命名为foo的方法设置断点,可以使用以下命令:</div><div class="line">(lldb) breakpoint set --method foo</div><div class="line"></div><div class="line"># 下面两者等价</div><div class="line">(lldb) b Breakpoints.SwiftTestClass.name.setter :Swift.ImplicitlyUnwrappedOptional<Swift.String></div><div class="line">(lldb) rb SwiftTestClass.name.setter</div><div class="line"></div><div class="line"># 部分正则参考</div><div class="line">(lldb) rb name\.setter // image lookup后给所有匹配上name.setter的打上断点</div><div class="line">(lldb) rb '\-\[UIViewController\ ' // 给这个类的所有方法打上断点</div><div class="line">(lldb) rb '\-\[UIViewController(\(\w+\))?\ ' // 给这个类的所有Category打上断点(没有验证)</div><div class="line">(lldb) rb . -f DetailViewController.swift // 给这个文件内的所有方法打上断点</div><div class="line">(lldb) rb . //给所有的方法打上断点 很危险</div><div class="line">(lldb) rb . -s UIKit // 只断点UIKit库里面的所有方法</div><div class="line"></div><div class="line"># 特殊用法</div><div class="line">^(@).* 这个需要考证是用来屏蔽上面说的桥接问题</div></pre></td></tr></table></figure></li><li><p>其他操作</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"> # 断点列表</div><div class="line"> (lldb) breakpoint list 1</div><div class="line"> 1: name = 'main', locations = 20, resolved = 20, hit count = 0</div><div class="line">1.1: where = Breakpoints`main + 22 at AppDelegate.swift:12, address =0x00000001057676e6, resolved, hit count = 0</div><div class="line">1.2: where = Foundation`-[NSThread main], address = 0x000000010584d182,resolved, hit count = 0</div><div class="line">1.3: where = Foundation`-[NSBlockOperation main], address =0x000000010585df4a, resolved, hit count = 0</div><div class="line">...</div><div class="line"></div><div class="line"> # 断点删除</div><div class="line"> (lldb) breakpoint delete 1</div><div class="line"> (lldb) breakpoint delete 1.1 这个准确的说是disable并不是真正意义上的删除</div></pre></td></tr></table></figure></li></ul><h3 id="Expression-amp-po-amp-p"><a href="#Expression-amp-po-amp-p" class="headerlink" title="Expression & po & p"></a>Expression & po & p</h3><p>po = expression -O –</p><p>Note: lldb的Context的问题,如果当前代码是在OC下的话要用OC语法,如果是在Swift的环境下要用Swift代码<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"># Swift环境下</div><div class="line">(lldb) po [UIApplication sharedApplication] # 不能这么使用</div><div class="line">(lldb) expression -l objc -O -- [UIApplication sharedApplication]</div><div class="line">(lldb) po UIApplication.shared </div><div class="line"></div><div class="line"># OC环境下</div><div class="line">(lldb) po $R0.title</div><div class="line">(lldb) expression -l swift -- $R0.title</div></pre></td></tr></table></figure></p><p>创建实例的时候要在实例名字前面加$符号,不然不能够正常使用<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"># 反例</div><div class="line">(lldb) po id test = [NSObject new]</div><div class="line">(lldb) po test</div><div class="line">error: use of undeclared identifier 'test'</div><div class="line"></div><div class="line"># 正确用法</div><div class="line">(lldb) po id $test = [NSObject new]</div><div class="line">(lldb) po $test</div><div class="line"><NSObject: 0x60000001d190></div><div class="line">(lldb) expression -l swift -O -- $test</div></pre></td></tr></table></figure></p><p>细节:在用Expression调用方法的时候,设置的Breakpoint是不会被主动触发的。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"># 不会被触发</div><div class="line">(lldb) expression -l swift -O -- $R0.viewDidLoad()</div><div class="line"># 会被触发</div><div class="line">(lldb) expression -l swift -O -i 0 -- $R0.viewDidLoad()</div></pre></td></tr></table></figure></p><h3 id="Thread-Frame-amp-Step"><a href="#Thread-Frame-amp-Step" class="headerlink" title="Thread, Frame & Step"></a>Thread, Frame & Step</h3><ul><li><p>Thread & Frame</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"> # 打印当前栈内情况</div><div class="line">(lldb) thread backtrace</div><div class="line"> # 打印栈顶信息</div><div class="line">(lldb) frame info</div><div class="line">frame #0: 0x00000001075d0ae0 Signals`MasterViewController.viewWillAppear(animated=<invalid> (0xd1),self=0x00007fff5862dac0) -> () at MasterViewController.swift:47</div><div class="line"> # 根据数据选择栈</div><div class="line"> (lldb) frame select 1</div><div class="line"> # 打印当前所在栈块内的实例</div><div class="line"> (lldb) frame variable</div><div class="line"> # 打印self 符号的所有实例</div><div class="line"> (lldb) frame variable -F self</div></pre></td></tr></table></figure></li><li><p>Step</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"># 重新启动程序</div><div class="line">(lldb) run</div><div class="line"># 继续</div><div class="line">(lldb) continue</div><div class="line"># 断点下一步 step over</div><div class="line">(lldb) next</div><div class="line"># 进入函数下一步 step into</div><div class="line">(lldb) step</div><div class="line"># 跳出当前函数 step out</div><div class="line">(lldb) finish</div></pre></td></tr></table></figure></li></ul><h3 id="TypeFormat"><a href="#TypeFormat" class="headerlink" title="TypeFormat"></a>TypeFormat</h3><p><a href="https://sourceware.org/gdb/ onlinedocs/gdb/Output-Formats.html" target="_blank" rel="noopener">参考链接</a></p><h3 id="Customizing-Commands"><a href="#Customizing-Commands" class="headerlink" title="Customizing Commands"></a>Customizing Commands</h3><ol><li>LLDB支持自定义命令行操作 (著名的chisel就是使用这种方法进行编写的)。<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"># 这个命令相比大家都不陌生,是用来打印当前的视图结构的</div><div class="line">(lldb) po [[[[[UIApplication sharedApplication] keyWindow] rootViewController] view] recursiveDescription]</div><div class="line"></div><div class="line"># 自定义命令(运行时替换)</div><div class="line"># Note:如果想全局替换可以把这个写入~/.lldbinit里面即可</div><div class="line">(lldb) command alias -- Yay_Autolayout expression -l objc -O -- [[[[[UIApplication sharedApplication] keyWindow] rootViewController] view] recursiveDescription]</div><div class="line"></div><div class="line"># 下面的调用等价于第一个</div><div class="line">(lldb) Yay_Autolayout</div></pre></td></tr></table></figure></li></ol><h3 id="正则表达"><a href="#正则表达" class="headerlink" title="正则表达"></a>正则表达</h3><ul><li>LLDB的正则实现原理是基于Shell的SED命令,详细可以参考sed命令<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">(lldb) command regex rlook 's/(.+)/image lookup -rn %1/'</div><div class="line">(lldb) rlook viewDidLoad</div></pre></td></tr></table></figure></li></ul><h3 id="简写"><a href="#简写" class="headerlink" title="简写"></a>简写</h3><p>c = continue<br>ex = expression<br>b = breakpoint<br>n = next</p><h3 id="杂项"><a href="#杂项" class="headerlink" title="杂项"></a>杂项</h3><ul><li>$ ttys //查找当前终端的名称<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">~ $ tty</div><div class="line">>> /dev/ttys027</div></pre></td></tr></table></figure></li></ul>]]></content>
<summary type="html">
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>本文为<a href="https://store.raywenderlich.com/products/advanced-apple-debugging-and-reverse-engineering" target="_blank" rel="noopener">Advanced Apple Debugging &amp; Reverse Engineering</a>的第一章阅读笔记,<br>有不正之处望指正</p>
<h2 id="基础"><a href="#基础" class="headerlink" title="基础"></a>基础</h2><p>关闭Rootless模式</p>
<ul>
<li>重启电脑,启动后长按Commond+R</li>
<li>进入控制台</li>
<li>输入 csrutil disable; reboot</li>
<li>不久后就会重启电脑</li>
</ul>
<h3 id="File"><a href="#File" class="headerlink" title="File"></a>File</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">$ lldb /Projects/Sketch/build/Debug/Sketch.app </div><div class="line">&gt;&gt; Current executable set to &apos;/Projects/Sketch/build/Debug/Sketch.app&apos; (x86_64)</div></pre></td></tr></table></figure>
<p>等同于<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">$ lldb </div><div class="line">(lldb) file /Projects/Sketch/build/Debug/Sketch.app </div><div class="line">&gt;&gt; Current executable set to &apos;/Projects/Sketch/build/Debug/Sketch.app&apos; (x86_64).</div></pre></td></tr></table></figure></p>
</summary>
<category term="LLDB" scheme="http://www.zhz.io/tags/LLDB/"/>
</entry>
<entry>
<title>Block 的为什么会有循环引用</title>
<link href="http://www.zhz.io/2017/03/15/Block-%E7%9A%84%E4%B8%BA%E4%BB%80%E4%B9%88%E4%BC%9A%E6%9C%89%E5%BE%AA%E7%8E%AF%E5%BC%95%E7%94%A8/"/>
<id>http://www.zhz.io/2017/03/15/Block-的为什么会有循环引用/</id>
<published>2017-03-15T02:52:13.000Z</published>
<updated>2018-02-12T02:53:13.793Z</updated>
<content type="html"><![CDATA[<h2 id="这次分享要解决的问题"><a href="#这次分享要解决的问题" class="headerlink" title="这次分享要解决的问题"></a>这次分享要解决的问题</h2><p>Block 为什么会引起循环引用</p><h2 id="本次实验"><a href="#本次实验" class="headerlink" title="本次实验"></a>本次实验</h2><ul><li>平台信息</li><li>Apple LLVM version 8.0.0 (clang-800.0.42.1)</li><li>Target: x86_64-apple-darwin16.4.0</li><li>Thread model: posix</li><li>InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin</li><li>MRC</li></ul><a id="more"></a><h2 id="为什么会出现循环引用"><a href="#为什么会出现循环引用" class="headerlink" title="为什么会出现循环引用"></a>为什么会出现循环引用</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"> +-----------+ +-----------+</div><div class="line"> | instance | | Block |</div><div class="line">---> | | --------> | |</div><div class="line"> | retain 2 | <-------- | retain 1 |</div><div class="line"> | | | |</div><div class="line"> +-----------+ +-----------+</div></pre></td></tr></table></figure><p>如上图所示。两个实例相互持有。<br>1.如果要释放instance,那么要先释放Block。<br>2.如果要释放block,那么要先释放instance。<br>条件1和2不能够同时满足,那么两个都不会被进行释放,这就是所谓的循环引用。</p><h2 id="Block的实现"><a href="#Block的实现" class="headerlink" title="Block的实现"></a>Block的实现</h2><p>工具Clang(GCC的替代品),编译源码生成可执行文件。<br><em>–rewrite-objc</em>将OC的.m文件转换为cpp文件。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">clang -rewrite-objc Test.m</div></pre></td></tr></table></figure></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">#import <Foundation/Foundation.h></div><div class="line"></div><div class="line">int main(int argc, const char * argv[]) {</div><div class="line"> @autoreleasepool {</div><div class="line"> // insert code here...</div><div class="line"> NSLog(@"Hello, World!");</div><div class="line"> ^{</div><div class="line"> NSLog(@"Hello Block");</div><div class="line"> }();</div><div class="line"> }</div><div class="line"> return 0;</div><div class="line">}</div></pre></td></tr></table></figure><p>利用Clang命令对源码进行重写。提取关键代码。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div></pre></td><td class="code"><pre><div class="line">struct __block_impl {</div><div class="line"> void *isa;</div><div class="line"> int Flags;</div><div class="line"> int Reserved;</div><div class="line"> void *FuncPtr;</div><div class="line">};</div><div class="line"></div><div class="line">struct __main_block_impl_0 {</div><div class="line"> struct __block_impl impl;</div><div class="line"> struct __main_block_desc_0* Desc;</div><div class="line"> __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {</div><div class="line"> impl.isa = &_NSConcreteStackBlock;</div><div class="line"> impl.Flags = flags;</div><div class="line"> impl.FuncPtr = fp;</div><div class="line"> Desc = desc;</div><div class="line"> }</div><div class="line">};</div><div class="line"></div><div class="line">static void __main_block_func_0(struct __main_block_impl_0 *__cself) {</div><div class="line"> NSLog((NSString *)&__NSConstantStringImpl__var_folders_dz_d6n3371d4951v8wz6yx9tbwc0000gn_T_main_cd25fe_mi_1);</div><div class="line">}</div><div class="line"></div><div class="line">static struct __main_block_desc_0 {</div><div class="line"> size_t reserved;</div><div class="line"> size_t Block_size;</div><div class="line">} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};</div><div class="line"></div><div class="line"></div><div class="line">((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA))();</div></pre></td></tr></table></figure></p><p>从中我们可以得到些什么关键的数据<br>一个基础block结构体<strong>_block_imp</strong><br>一个静态C函数<strong>_main_block_func_0</strong>(命名方式:当前类的函数名+block+func+当前Block在文件中Block的序列号。)<br>一个block的结构体<strong>_main_block_impl_0</strong>(命名方式:当前类的函数名+block+impl+当前Block在文件中Block的序列号。)<br>一个block的解释体<strong>main_block_desc_0</strong>(命名方式:当前类的函数名+desc+impl+当前Block在文件中Block的序列号。)</p><p>简化得到基础的Block结构体模型<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line">struct Block_literal_1 {</div><div class="line"> void *isa;//ARC:__NSMallocBlock__/__NSGlobalBlock__ MRC:__NSMallocBlock__/__NSStackBlock__/__NSGlobalBlock__</div><div class="line"> int flags;//copy到堆时候用的标识</div><div class="line"> int reserved;//保留变量</div><div class="line"> void (*invoke)(void *, ...);//函数指针,实际Block的函数调用地址,类似于实例方法的IMP</div><div class="line"> struct Block_descriptor_1 *descriptor;//Block描述信息</div><div class="line">};</div><div class="line">//非本节关注点。有兴趣自由研究。</div><div class="line">struct Block_descriptor_1 {</div><div class="line"> unsigned long int reserved;//保留变量</div><div class="line"> unsigned long int size;//Block在内存中的大小</div><div class="line"> void (*copy)(void *dst, void *src);//copy到堆上使用的方法</div><div class="line"> void (*dispose)(void *);//执行Release操作</div><div class="line"> const char *signature;//函数签名</div><div class="line">};</div></pre></td></tr></table></figure></p><ul><li><strong>NSMallocBlock</strong>:堆区</li><li><strong>NSStackBlock</strong> :栈区</li><li><strong>NSGlobalBlock</strong>:Data/Text区</li></ul><h2 id="Block参数自动捕获机制"><a href="#Block参数自动捕获机制" class="headerlink" title="Block参数自动捕获机制"></a>Block参数自动捕获机制</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line">#import <Foundation/Foundation.h></div><div class="line"></div><div class="line">int main(int argc, const char * argv[]) {</div><div class="line"> @autoreleasepool {</div><div class="line"> // insert code here...</div><div class="line"> NSLog(@"Hello, World!");</div><div class="line"> NSString *a = nil;</div><div class="line"> ^{</div><div class="line"> NSLog(@"%@",a);</div><div class="line"> NSLog(@"Hello Block");</div><div class="line"> }();</div><div class="line"> }</div><div class="line"> return 0;</div><div class="line">}</div></pre></td></tr></table></figure><p>简化上述描述,最后得到结果Block为如下值,从中我们可以看到Block的结构体实例会持有a。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">struct Block_literal_1 {</div><div class="line"> void *isa;</div><div class="line"> int flags;</div><div class="line"> int reserved;</div><div class="line"> void (*invoke)(void *, ...);</div><div class="line"> struct Block_descriptor_1 *descriptor;</div><div class="line"> </div><div class="line"> NSString *a;</div><div class="line">};</div></pre></td></tr></table></figure></p><p>看完了C的这些实现我们来看OC的代码,测试代码如下<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">@interface Test ()</div><div class="line">@property (nonatomic, strong) NSString *a;</div><div class="line">@end</div><div class="line"></div><div class="line">@implementation Test</div><div class="line">- (void)test{</div><div class="line"> ^{</div><div class="line"> NSLog(@"%@",self.a);</div><div class="line"> NSLog(@"Hello Block");</div><div class="line"> }();</div><div class="line">}</div><div class="line">@end</div></pre></td></tr></table></figure></p><p>简化得到Block如下,从中我们看到其实Block是持有self的。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">struct Block_literal_1 {</div><div class="line"> void *isa;</div><div class="line"> int flags;</div><div class="line"> int reserved;</div><div class="line"> void (*invoke)(void *, ...);</div><div class="line"> struct Block_descriptor_1 *descriptor;</div><div class="line"> </div><div class="line"> Test *self;</div><div class="line">};</div></pre></td></tr></table></figure></p><p>所以Block是通过持有self实例来调用a的成员变量<br>简化版调用a成员变量的实现<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">static void __Test__test_block_func_0(struct __Test__test_block_impl_0 *__cself) {</div><div class="line"> Test *self = __cself->self; // bound by copy</div><div class="line"> NSLog((NSString *)objc_msgSend)((id)self, sel_registerName("a"));</div><div class="line"> NSLog((NSString *)&__NSConstantStringImpl__var_folders_dz_d6n3371d4951v8wz6yx9tbwc0000gn_T_Test_92b943_mi_1);</div><div class="line"> }</div></pre></td></tr></table></figure></p><h2 id="实际使用"><a href="#实际使用" class="headerlink" title="实际使用"></a>实际使用</h2><p>retain cycle问题的根源在于Block和obj可能会互相强引用,互相retain对方,这样就导致了retain cycle,最后这个Block和obj就变成了孤岛,谁也释放不了谁。比如:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];</div><div class="line">[request setCompletionBlock:^{</div><div class="line"> NSString* string = [request responseString];</div><div class="line">}];</div><div class="line"></div><div class="line"> +-----------+ +-----------+</div><div class="line"> | request | | Block |</div><div class="line">---> | | --------> | |</div><div class="line"> | retain 2 | <-------- | retain 1 |</div><div class="line"> | | | |</div><div class="line"> +-----------+ +-----------+</div></pre></td></tr></table></figure><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"> __block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];</div><div class="line"> [request setCompletionBlock:^{</div><div class="line"> NSString* string = [request responseString];</div><div class="line"> }];</div><div class="line"> +-----------+ +-----------+</div><div class="line"> | request | | Block |</div><div class="line">---->| | --------> | |</div><div class="line"> | retain 1 | < - - - - | retain 1 |</div><div class="line"> | | weak | |</div><div class="line"> +-----------+ +-----------+</div></pre></td></tr></table></figure><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line">ClassA* objA = [[[ClassA alloc] init] autorelease];</div><div class="line">objA.myBlock = ^{</div><div class="line"> [self doSomething];</div><div class="line">};</div><div class="line">self.objA = objA;</div><div class="line"></div><div class="line">+-----------+ +-----------+ +-----------+</div><div class="line">| self | | objA | | Block |</div><div class="line">| | --------> | | --------> | |</div><div class="line">| retain 2 | | retain 1 | | retain 1 |</div><div class="line">| | | | | |</div><div class="line">+-----------+ +-----------+ +-----------+</div><div class="line"> ^ |</div><div class="line"> | |</div><div class="line"> +------------------------------------------------+</div></pre></td></tr></table></figure><h2 id="扩展知识"><a href="#扩展知识" class="headerlink" title="扩展知识"></a>扩展知识</h2><p>__block和__weak实现原理(有兴趣的自行了解)<br>关键代码<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line">struct __Block_byref_weakSelf_0 {</div><div class="line"> void *__isa;</div><div class="line">__Block_byref_weakSelf_0 *__forwarding;</div><div class="line"> int __flags;</div><div class="line"> int __size;</div><div class="line"> void (*__Block_byref_id_object_copy)(void*, void*);</div><div class="line"> void (*__Block_byref_id_object_dispose)(void*);</div><div class="line"> typeof (self) weakSelf;</div><div class="line">};</div><div class="line">struct __Test__test_block_impl_0 {</div><div class="line"> struct __block_impl impl;</div><div class="line"> struct __Test__test_block_desc_0* Desc;</div><div class="line"> __Block_byref_weakSelf_0 *weakSelf; // by ref</div><div class="line"> __Block_byref_string_1 *string; // by ref</div><div class="line"> __Test__test_block_impl_0(void *fp, struct __Test__test_block_desc_0 *desc, __Block_byref_weakSelf_0 *_weakSelf, __Block_byref_string_1 *_string, int flags=0) : weakSelf(_weakSelf->__forwarding), string(_string->__forwarding) {</div><div class="line"> impl.isa = &_NSConcreteStackBlock;</div><div class="line"> impl.Flags = flags;</div><div class="line"> impl.FuncPtr = fp;</div><div class="line"> Desc = desc;</div><div class="line"> }</div><div class="line">};</div><div class="line">static void __Test__test_block_dispose_0(struct __Test__test_block_impl_0*src) {_Block_object_dispose((void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);}</div><div class="line">static void __Test__test_block_dispose_0(struct __Test__test_block_impl_0*src) {_Block_object_dispose((void*)src->weakSelf, 8/*BLOCK_FIELD_IS_BYREF*/);}</div></pre></td></tr></table></figure></p><h2 id="参考文档"><a href="#参考文档" class="headerlink" title="参考文档"></a>参考文档</h2><p><a href="http://clang.llvm.org/docs/Block-ABI-Apple.html" target="_blank" rel="noopener">LLVM 中 block 实现源码</a><br><a href="http://blog.devtang.com/2013/07/28/a-look-inside-blocks/" target="_blank" rel="noopener">谈Objective-C block的实现</a><br><a href="http://tanqisen.github.io/blog/2013/04/19/gcd-block-cycle-retain/" target="_blank" rel="noopener">文中实际使用举例</a><br><a href="https://github.com/deput/NSInvocation-Block/blob/master/NSInvocation%2BBlock.m" target="_blank" rel="noopener">NSInvocation<->block</a></p>]]></content>
<summary type="html">
<h2 id="这次分享要解决的问题"><a href="#这次分享要解决的问题" class="headerlink" title="这次分享要解决的问题"></a>这次分享要解决的问题</h2><p>Block 为什么会引起循环引用</p>
<h2 id="本次实验"><a href="#本次实验" class="headerlink" title="本次实验"></a>本次实验</h2><ul>
<li>平台信息</li>
<li>Apple LLVM version 8.0.0 (clang-800.0.42.1)</li>
<li>Target: x86_64-apple-darwin16.4.0</li>
<li>Thread model: posix</li>
<li>InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin</li>
<li>MRC</li>
</ul>
</summary>
</entry>
<entry>
<title>我的2016年总结</title>
<link href="http://www.zhz.io/2016/12/31/%E6%88%91%E7%9A%842016%E5%B9%B4%E6%80%BB%E7%BB%93/"/>
<id>http://www.zhz.io/2016/12/31/我的2016年总结/</id>
<published>2016-12-31T04:44:36.000Z</published>
<updated>2018-02-12T02:54:15.000Z</updated>
<content type="html"><![CDATA[<h2 id="0x00-前言"><a href="#0x00-前言" class="headerlink" title="0x00 前言"></a>0x00 前言</h2><p>呵呵哒,这好像是我第一次写这种总结(非逼迫性),来记录一下今年我都干了写啥蠢事。<br>OK~2016年,就这样过去了,这几天看了几个小伙伴和一些人的博客,套用下模板吧。The Good The Bad</p><h2 id="0x01-The-Good"><a href="#0x01-The-Good" class="headerlink" title="0x01 The Good"></a>0x01 The Good</h2><p>今年顺利毕业了,算是度过了人生中最后一段学校的时光。<br>今年的变化还是挺多的吧,单从职业上来说,从学生变成了从业者。从生活上来说,从一个满受父母关怀的孩子变成了一个独立者。</p><h2 id="看了挺多的书"><a href="#看了挺多的书" class="headerlink" title="看了挺多的书"></a>看了挺多的书</h2><p>今年看了挺多的书吧,比如说CA、OC编程之道、Effective Objective-C、代码之外的生存指南等等。收益良多,了解了更多iOS底层的东西,对于后续帮助的确挺大的。<br><a id="more"></a></p><h2 id="从业务转向组件"><a href="#从业务转向组件" class="headerlink" title="从业务转向组件"></a>从业务转向组件</h2><p>来到了一家1K以上规模的公司,终于可以开始不用写一些业务上面的代码了,对于我来说,感觉业务的代码比较繁琐,因为平时的工作量比较多,所以想研究一些东西变成了不可能。<br>现在有所好转,可以开始重构一些模块,开始做一些模块化的东西,接触一些之前会接触不到的东西。慢慢的开始优化自己的代码,因为我写的并不是只有我一个人用,而是面向整个团队。</p><h2 id="提升工作效率的方法并不是加班"><a href="#提升工作效率的方法并不是加班" class="headerlink" title="提升工作效率的方法并不是加班"></a>提升工作效率的方法并不是加班</h2><p>有段时间,曾经强迫自己每天晚上一定要加班,把自己搞的累死累活的,重复着一样的工作。然而并没有什么很好的进展,工作完成量和时间成了一次性方程直线。<br>在做自动化静态库构建的时候,刚开始并没有完整的理解Ruby语言的魅力,导致自己走了很多的弯路,而且少了错过了很多更加有效的方案方法。</p><h2 id="当下能做的为什么要拖到明天"><a href="#当下能做的为什么要拖到明天" class="headerlink" title="当下能做的为什么要拖到明天"></a>当下能做的为什么要拖到明天</h2><p>这应该算一个方法的转变吧,我觉得我是一个很懒的人,所以一直都是这件事情明天做吧,那就放到明天好了。当时这个明天就永远变成了明天。这是我不愿意看到的,所以开始慢慢的调整自己的习惯。从小事做起吧,比如说,洗衣机的衣服洗好了,那就马上去晒掉,不丢到明天。</p><h2 id="1年-1年?"><a href="#1年-1年?" class="headerlink" title="1年=1年?"></a>1年=1年?</h2><p>在技术圈子里时不时就会听到这句话,“技术无时无刻都在变化,你永远都不知道下一秒变成了什么”。我听到了另外一句话,“你的一年是几年”,你是每天重复着一样的工作,还是说你无时无刻都在学习。</p><h2 id="0x02-The-Bad"><a href="#0x02-The-Bad" class="headerlink" title="0x02 The Bad"></a>0x02 The Bad</h2><h2 id="年初定下的一些目标并没有实现"><a href="#年初定下的一些目标并没有实现" class="headerlink" title="年初定下的一些目标并没有实现"></a>年初定下的一些目标并没有实现</h2><p>关于年初定了一堆的目标,被遗忘了好多了。呵呵哒。其中有个减肥的好像被遗忘的特别严重吧。去年狠下心减了那么多,今年弹了一部分。<br>改进:可能要实行一些GTD的方案,来规划一下后面走的路。</p><h2 id="认识的人太少了"><a href="#认识的人太少了" class="headerlink" title="认识的人太少了"></a>认识的人太少了</h2><p>不知道是不是因为工作了的关系,开始认识的人越来越少,自己的圈子被局限了起来。一些交流会感觉无意义就被略过了,或者已没时间为借口匆匆的避开了一系列聚会。和朋友之间呆的时间也开始变少了。或者说知心朋友应该是越来越少了吧。<br>改进:多参加一些技术交流会吧,即使不是技术的。</p><h2 id="其他领域的知识狩猎太少了"><a href="#其他领域的知识狩猎太少了" class="headerlink" title="其他领域的知识狩猎太少了"></a>其他领域的知识狩猎太少了</h2><p>今年一股脑门的投入在iOS圈子里面,对于其他的技术了解的太少了,还好最后一个月,在写一些脚本构建方面的东西,从而能够学习到一些Shell和Ruby。不然真的要被OC给玩死了,到头来一点意思都没有。几年都不加语言特性(呵呵哒)。<br>改进:应该会对一些其他领域进行研究,特别是安全方面的。</p><h2 id="重构上测试写太少了"><a href="#重构上测试写太少了" class="headerlink" title="重构上测试写太少了"></a>重构上测试写太少了</h2><p>最后几个月重构了一些东西,但是呢出了线上Bug,原因都很简单,都是几分钟内解决的小Bug,而且一些测试明显就能够测试出来。哎~只能说自己重构前没有写测试吧。导致自己犯了一些很低级的错误。<br>改进:以后重构的时候前写好单元测试和功能测试吧。</p><h2 id="0x03-2017-一些计划"><a href="#0x03-2017-一些计划" class="headerlink" title="0x03 2017 一些计划"></a>0x03 2017 一些计划</h2><ol><li>明确自己的人生目标(应该是阶段性的目标)</li><li>去一次日本</li><li>改变自己</li><li>能够从容的应对各种事情</li></ol>]]></content>
<summary type="html">
<h2 id="0x00-前言"><a href="#0x00-前言" class="headerlink" title="0x00 前言"></a>0x00 前言</h2><p>呵呵哒,这好像是我第一次写这种总结(非逼迫性),来记录一下今年我都干了写啥蠢事。<br>OK~2016年,就这样过去了,这几天看了几个小伙伴和一些人的博客,套用下模板吧。The Good The Bad</p>
<h2 id="0x01-The-Good"><a href="#0x01-The-Good" class="headerlink" title="0x01 The Good"></a>0x01 The Good</h2><p>今年顺利毕业了,算是度过了人生中最后一段学校的时光。<br>今年的变化还是挺多的吧,单从职业上来说,从学生变成了从业者。从生活上来说,从一个满受父母关怀的孩子变成了一个独立者。</p>
<h2 id="看了挺多的书"><a href="#看了挺多的书" class="headerlink" title="看了挺多的书"></a>看了挺多的书</h2><p>今年看了挺多的书吧,比如说CA、OC编程之道、Effective Objective-C、代码之外的生存指南等等。收益良多,了解了更多iOS底层的东西,对于后续帮助的确挺大的。<br>
</summary>
<category term="总结" scheme="http://www.zhz.io/tags/%E6%80%BB%E7%BB%93/"/>
</entry>
<entry>
<title>Cocoapods常用命令</title>
<link href="http://www.zhz.io/2016/12/15/Cocoapods%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4/"/>
<id>http://www.zhz.io/2016/12/15/Cocoapods常用命令/</id>
<published>2016-12-15T08:57:23.000Z</published>
<updated>2016-12-15T09:01:08.000Z</updated>
<content type="html"><![CDATA[<h1 id="0x00-Pod基础"><a href="#0x00-Pod基础" class="headerlink" title="0x00 Pod基础"></a>0x00 Pod基础</h1><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">pod --help 这个可以用来所有的命令</div></pre></td></tr></table></figure><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">$ pod [Commond] [Options]</div><div class="line">$ pod install --verbose --no-repo-update</div></pre></td></tr></table></figure><a id="more"></a><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">文件目录</div><div class="line">which pod Pod组件安装的目录</div><div class="line"></div><div class="line">Find里面进入./cocoapods可以看到你本地所有的仓库</div></pre></td></tr></table></figure><p>Commonds<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">pod setup 重新安装master仓库,最好别用。因为会把master库删掉,然后下载又很慢,可以用Airdrop让别人给你传一份。</div></pre></td></tr></table></figure></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">pod install </div><div class="line">官方说法:Install project dependencies according to versions from a Podfile.lock</div><div class="line">解释:解析.podfile文件,跟本地的podfile进行比对,并按不同之处重新进行依赖关系修复。</div></pre></td></tr></table></figure><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">pod update </div><div class="line">官方说法:Update outdated project dependencies and create new Podfile.lock</div><div class="line">解释:解析.podfile文件,忽略本地的Podfile.lock文件,重新生成Podfile</div><div class="line"></div><div class="line">pod update Your_Project_Name</div><div class="line">解释:仅仅更新Name仓库</div></pre></td></tr></table></figure><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">pod search [name]</div><div class="line">官方说法:Search for pods</div><div class="line">解释:扫描本地的./cocoapods目录下所有的podspec。匹配含有name的项目</div></pre></td></tr></table></figure><p>Options </p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">--verbose </div><div class="line">官方说法:Show more debugging information</div><div class="line">解释:显示出所有Debug信息</div></pre></td></tr></table></figure><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">--no-repo-update</div><div class="line">解释:不去更新现有的本地仓库(包括master,和私有的)</div></pre></td></tr></table></figure><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">--version 当前使用Cocoapods的版本</div></pre></td></tr></table></figure><p>###本地gems的程序安装包,可以看到Cocoapods一共安装了那些组件<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ gem list</div></pre></td></tr></table></figure></p><p>###找到对应的组件,可以移除安装包<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ sudo gem uninstall cocoapods -v 0.35.0</div></pre></td></tr></table></figure></p><p>###安装制定版本的安装包<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ sudo gem install cocoapods -v 0.34.4</div></pre></td></tr></table></figure></p><p>###查看本地的源路径<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ gem sources -l</div></pre></td></tr></table></figure></p><p>###切换Gem Source<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">#淘宝的据说已经不维护了</div><div class="line">$ gem sources --add https://gems.ruby-china.org/ #添加源</div><div class="line">$ gem sources --remove https://rubygems.org/ #移除源</div></pre></td></tr></table></figure></p><h1 id="0x00-Pod进阶"><a href="#0x00-Pod进阶" class="headerlink" title="0x00 Pod进阶"></a>0x00 Pod进阶</h1><p>Commonds<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">pod cache </div><div class="line">官方解释:Manipulate the CocoaPods cache</div><div class="line">解释:本地的依赖库缓存,不是项目工程Pods下的缓存。</div><div class="line"></div><div class="line">pod cache list 显示所有的本地缓存</div><div class="line">pod cache clean Your_Project_Name --all 清除所有的Your_Project_Name在本地的缓存</div><div class="line">pod cache clean Your_Project_Name 会显示出所有的Your_Project_Name缓存,然后再根据你想要的去清除</div></pre></td></tr></table></figure></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">pod repo 管理仓库的命令</div><div class="line">pod repo update 更新本地所有仓库</div><div class="line">pod repo update [Name] 仅仅更新Name仓库</div><div class="line">pod repo push [Name.podspec] [本地仓库名字] 推送一个私有项目的配置文件podspec 到制定的私有仓库</div></pre></td></tr></table></figure><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">pod trunk 将项目配置推送到公共的master仓库</div></pre></td></tr></table></figure><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">pod outdated</div><div class="line">官方说法:Show outdated project dependencies</div><div class="line">解释:分析现有的依赖项,并与最新的比较。如图所示</div></pre></td></tr></table></figure><p>###运行不同版本的pod前提是你本地有不同版本的pod<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">pod _1.0.1_ install</div></pre></td></tr></table></figure></p><h1 id="0x00-Pod插件"><a href="#0x00-Pod插件" class="headerlink" title="0x00 Pod插件"></a>0x00 Pod插件</h1><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">gem install cocoapods-package 安装用来打包成静态库的插件</div></pre></td></tr></table></figure><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">pod package [Name.podspec] [Options]</div></pre></td></tr></table></figure><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">--force 替换本地已有的文件</div><div class="line">--no-mangle 接触Pod中已有的依赖冠以</div><div class="line">--library 打包成.a静态库</div><div class="line">--exclude-deps 不把依赖项打包进入项目</div><div class="line">--spec-sources=private,https://github.com/CocoaPods/Specs.git 如果项目中有私有源一定要加上这个</div></pre></td></tr></table></figure>]]></content>
<summary type="html">
<h1 id="0x00-Pod基础"><a href="#0x00-Pod基础" class="headerlink" title="0x00 Pod基础"></a>0x00 Pod基础</h1><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">pod --help 这个可以用来所有的命令</div></pre></td></tr></table></figure>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">$ pod [Commond] [Options]</div><div class="line">$ pod install --verbose --no-repo-update</div></pre></td></tr></table></figure>
</summary>
<category term="Cocoapods" scheme="http://www.zhz.io/tags/Cocoapods/"/>
</entry>
<entry>
<title>Share Keychain Access</title>
<link href="http://www.zhz.io/2016/08/25/Shared%20Keychain%20Access/"/>
<id>http://www.zhz.io/2016/08/25/Shared Keychain Access/</id>
<published>2016-08-25T08:57:23.000Z</published>
<updated>2016-12-15T10:04:43.000Z</updated>
<content type="html"><![CDATA[<h2 id="0x00-什么是Keychain"><a href="#0x00-什么是Keychain" class="headerlink" title="0x00 什么是Keychain"></a>0x00 什么是Keychain</h2><p>据介绍,Keychain是iOS系统官方提供的安全存储容器,我们可以用它来存一些敏感信息,EG:密码,用户名,证书等等。<br>Mac电脑上自带的Keychain Access.app就是苹果自己的Keychain读取软件。<br>Keychain是保存在沙盒之外的数据库的,所以在删除App后,在重新下载App后,这些信息依旧存在,并且你可以通过设置一些属性,让你的数据保#####存到iCloud中,达到跨设备存储。</p><h2 id="0x01-Keychain本质是什么"><a href="#0x01-Keychain本质是什么" class="headerlink" title="0x01 Keychain本质是什么"></a>0x01 Keychain本质是什么</h2><p>Keychain存储本质是Sqlite。真机地址:/private/var/Keychains/keychain-2.db<br>既然是Sqlite,那么Keychain存储的对象(后面统称为Item),即对应Sqlite上的一条记录而已。</p><a id="more"></a><h2 id="0x02-Item"><a href="#0x02-Item" class="headerlink" title="0x02 Item"></a>0x02 Item</h2><p>Item 结构</p><ul><li>Class -> kSecClass //指向对应的表结构</li><li>Attributes -> kSecAttrXXXX //每个表中对应的字段</li><li>Value -> kSecValueXXX //存储的类型</li></ul><p>Class<br>有Sqlite,那么就会有对应的表。<br>Item在保存对象的时候,你需要指定你要保存的表。</p><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">extern</span> <span class="keyword">const</span> <span class="built_in">CFStringRef</span> kSecClassGenericPassword <span class="comment">//一般的密码存储表,基本用这个为主,对应genp表</span></div><div class="line"><span class="keyword">extern</span> <span class="keyword">const</span> <span class="built_in">CFStringRef</span> kSecClassInternetPassword <span class="comment">//用来存储网络密码的表,对应inet表</span></div><div class="line"><span class="keyword">extern</span> <span class="keyword">const</span> <span class="built_in">CFStringRef</span> kSecClassCertificate <span class="comment">//用来存储证书表,对应cert表</span></div><div class="line"><span class="keyword">extern</span> <span class="keyword">const</span> <span class="built_in">CFStringRef</span> kSecClassKey <span class="comment">//存一些Key之类的东西,对应keys表</span></div><div class="line"><span class="keyword">extern</span> <span class="keyword">const</span> <span class="built_in">CFStringRef</span> kSecClassIdentity <span class="comment">//一些特殊的认证信息之类的,这个不知道。</span></div></pre></td></tr></table></figure><p>Attributes<br>关于Item的属性,对应的不同表是不同的。每个都介绍不现实,讲讲最常用的kSecClassGenericPassword表中的一些字段吧</p><table><thead><tr><th>属性</th><th>Value值</th><th>作用</th></tr></thead><tbody><tr><td>kSecAttrAccessible</td><td>kSecAttrAccessibleWhenUnlocked (Default)</td><td>你能获取到Keychain的数据必须在设备被解锁的情况下获取</td></tr><tr><td></td><td>kSecAttrAccessibleAfterFirstUnlock</td><td>Keychain的数据在设备第一次解锁之后就能够获取使用</td></tr><tr><td></td><td>kSecAttrAccessibleAlways</td><td>Keychain数据始终可以获取到</td></tr><tr><td>kSecAttrAccessGroup</td><td>自定义Like(@”AppIdentifierPrefix.com.hongzhi.test”)</td><td>设置你的存储区域,后面会详细讲</td></tr><tr><td>kSecAttrAccount</td><td>自定义</td><td>标识此条数据的主Key之一</td></tr><tr><td>kSecAttrService</td><td>自定义</td><td>标识此条数据的主Key之一</td></tr><tr><td>kSecAttrGeneric</td><td>自定义</td><td>非主键用来标识</td></tr><tr><td>kSecAttrSynchronizable</td><td>BOOL</td><td>标识是否同步到iCloud</td></tr></tbody></table><blockquote><p>Note:<br> kSecAttrAccessible还有另外三种是在上面这几个后面加ThisDeviceOnly,则数据不会跟随设备移动,会跟设备绑定。<br><br> AppIdentifierPrefix是你开发证书的唯一标识符号,com.hongzhi.test是你应用的BundleId。</p></blockquote><p>Value</p><table><thead><tr><th>属性</th><th>Value值</th><th>作用</th></tr></thead><tbody><tr><td>kSecValueData</td><td>Get或者Set</td><td>这个就是你要存数据的Value,只能存NSData对象</td></tr></tbody></table><h2 id="0x03-用法"><a href="#0x03-用法" class="headerlink" title="0x03 用法"></a>0x03 用法</h2><p>既然是Sqlite数据库的存储,那么肯定就有增删该查。<br>每个函数都会有一个OSStatus的标识,标识操作是否成功,更多的标识参考SecBase.h文件中的OSStatus枚举值。</p><ol><li>SecItemAdd 添加一个keychain item</li><li>SecItemUpdate 修改一个keychain item</li><li>SecItemCopyMatching 搜索一个keychain item</li><li>SecItemDelete 删除一个keychain item</li></ol><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div></pre></td><td class="code"><pre><div class="line">- (<span class="built_in">NSMutableDictionary</span> *)newSearchDictionary:(<span class="built_in">NSString</span> *)identifier {</div><div class="line"> <span class="built_in">NSMutableDictionary</span> *searchDictionary = [[<span class="built_in">NSMutableDictionary</span> alloc] init];</div><div class="line"> <span class="comment">//指定item的类型为GenericPassword</span></div><div class="line"> [searchDictionary setObject:(<span class="keyword">id</span>)kSecClassGenericPassword forKey:(<span class="keyword">id</span>)kSecClass];</div><div class="line"> </div><div class="line"> <span class="comment">//类型为GenericPassword的信息必须提供以下两条属性作为unique identifier</span></div><div class="line"> [searchDictionary setObject:encodedIdentifier forKey:(<span class="keyword">id</span>)kSecAttrAccount];</div><div class="line"> [searchDictionary setObject:encodedIdentifier forKey:(<span class="keyword">id</span>)kSecAttrService];</div><div class="line"> </div><div class="line"> <span class="keyword">return</span> searchDictionary;</div><div class="line">}</div><div class="line">- (<span class="built_in">NSData</span> *)searchKeychainCopyMatching:(<span class="built_in">NSString</span> *)identifier {</div><div class="line"> <span class="built_in">NSMutableDictionary</span> *searchDictionary = [<span class="keyword">self</span> newSearchDictionary:identifier];</div><div class="line"> </div><div class="line"> <span class="comment">//在搜索keychain item的时候必须提供下面的两条用于搜索的属性</span></div><div class="line"> <span class="comment">//只返回搜索到的第一条item</span></div><div class="line"> [searchDictionary setObject:(<span class="keyword">id</span>)kSecMatchLimitOne forKey:(<span class="keyword">id</span>)kSecMatchLimit];</div><div class="line"> <span class="comment">//返回item的kSecValueData</span></div><div class="line"> [searchDictionary setObject:(<span class="keyword">id</span>)kCFBooleanTrue forKey:(<span class="keyword">id</span>)kSecReturnData];</div><div class="line"> </div><div class="line"> <span class="built_in">NSData</span> *result = <span class="literal">nil</span>;</div><div class="line"> OSStatus status = SecItemCopyMatching((<span class="built_in">CFDictionaryRef</span>)searchDictionary,</div><div class="line"> (<span class="built_in">CFTypeRef</span> *)&result);</div><div class="line"> <span class="keyword">return</span> result;</div><div class="line">}</div><div class="line">- (<span class="built_in">BOOL</span>)createKeychainValue:(<span class="built_in">NSString</span> *)password forIdentifier:(<span class="built_in">NSString</span> *)identifier {</div><div class="line"> <span class="comment">//设置添加的字典</span></div><div class="line"> <span class="built_in">NSMutableDictionary</span> *dictionary = [<span class="keyword">self</span> newSearchDictionary:identifier];</div><div class="line"> </div><div class="line"> <span class="built_in">NSData</span> *passwordData = [password dataUsingEncoding:<span class="built_in">NSUTF8StringEncoding</span>];</div><div class="line"> [dictionary setObject:passwordData forKey:(<span class="keyword">id</span>)kSecValueData];</div><div class="line"> </div><div class="line"> OSStatus status = SecItemAdd((<span class="built_in">CFDictionaryRef</span>)dictionary, <span class="literal">NULL</span>);</div><div class="line"> <span class="keyword">if</span> (status == errSecSuccess) {</div><div class="line"> <span class="keyword">return</span> <span class="literal">YES</span>;</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> <span class="literal">NO</span>;</div><div class="line">}</div><div class="line">- (<span class="built_in">BOOL</span>)updateKeychainValue:(<span class="built_in">NSString</span> *)password forIdentifier:(<span class="built_in">NSString</span> *)identifier {</div><div class="line"> <span class="built_in">NSMutableDictionary</span> *searchDictionary = [<span class="keyword">self</span> newSearchDictionary:identifier];</div><div class="line"> </div><div class="line"> <span class="built_in">NSMutableDictionary</span> *updateDictionary = [[<span class="built_in">NSMutableDictionary</span> alloc] init];</div><div class="line"> <span class="built_in">NSData</span> *passwordData = [password dataUsingEncoding:<span class="built_in">NSUTF8StringEncoding</span>];</div><div class="line"> [updateDictionary setObject:passwordData forKey:(<span class="keyword">id</span>)kSecValueData];</div><div class="line"> </div><div class="line"> OSStatus status = SecItemUpdate((<span class="built_in">CFDictionaryRef</span>)searchDictionary,</div><div class="line"> (<span class="built_in">CFDictionaryRef</span>)updateDictionary);</div><div class="line"> <span class="keyword">if</span> (status == errSecSuccess) {</div><div class="line"> <span class="keyword">return</span> <span class="literal">YES</span>;</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> <span class="literal">NO</span>;</div><div class="line">}</div><div class="line">- (<span class="keyword">void</span>)deleteKeychainValue:(<span class="built_in">NSString</span> *)identifier {</div><div class="line"> <span class="built_in">NSMutableDictionary</span> *searchDictionary = [<span class="keyword">self</span> newSearchDictionary:identifier];</div><div class="line"> SecItemDelete((<span class="built_in">CFDictionaryRef</span>)searchDictionary);</div><div class="line">}</div></pre></td></tr></table></figure><h2 id="0x04-关于iCloud备份"><a href="#0x04-关于iCloud备份" class="headerlink" title="0x04 关于iCloud备份"></a>0x04 关于iCloud备份</h2><ol><li>使用kSecAttrSynchronizable可以对存储的值进行跨设备的备份,但是添加这个字段后,搜索会以这个值作为主要的Key。</li><li>搜索匹配的时候要带上这个字段才能查询到相应的Item。</li></ol><h2 id="0x05-App间共享数据"><a href="#0x05-App间共享数据" class="headerlink" title="0x05 App间共享数据"></a>0x05 App间共享数据</h2><ol><li>关键东西:Keychain access group。</li><li>原理:Keychain通过Provisioning profile来区分应用,每个profile会带有相应的bundle id和添加的Access Group,应用保存的数据指定在对应的Access group中,如果没有在属性中设置,即存储在已bundle id命名的Access Group中。</li><li>非代码指定Access Group的话,就要在Capabilities中打开Keychain Sharing,在其中添加相应的BundleId,系统会自动帮你添加AppIdentifierPrefix,即Id值。<br>Use:<figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">[searchDictionary setObject:@“AppIdentifierPrefix.UC.testWriteKeychainSuit” forKey:(<span class="keyword">id</span>)kSecAttrAccessGroup];</div></pre></td></tr></table></figure></li></ol><blockquote><p>如果多个App指定了同一个Access group,并且它是由同一个证书下发的,那么这两个App就能够从这个Access Group中获取数据。</p></blockquote><h2 id="0xfe-总结"><a href="#0xfe-总结" class="headerlink" title="0xfe 总结"></a>0xfe 总结</h2><p>Keychain很方便,支持App间,设备间数据的传输,为一大群开发者解决了一堆的难题,但是也不能过度依赖于这个东西。Keychain的读取速度是NSUserDefault的3倍时长左右。一些简单的数据存储在Info.plist或者NSUserdefault中反而会更好。</p><h2 id="0xff-参考链接"><a href="#0xff-参考链接" class="headerlink" title="0xff 参考链接"></a>0xff 参考链接</h2><p><a href="https://developer.apple.com/videos/play/wwdc2013/709/" target="_blank" rel="noopener">2013届WWDC Keychain简介</a><br><a href="http://blog.sheliw.com/blog/2015/02/16/keychain/" target="_blank" rel="noopener">Max Blog Keychain</a></p>]]></content>
<summary type="html">
<h2 id="0x00-什么是Keychain"><a href="#0x00-什么是Keychain" class="headerlink" title="0x00 什么是Keychain"></a>0x00 什么是Keychain</h2><p>据介绍,Keychain是iOS系统官方提供的安全存储容器,我们可以用它来存一些敏感信息,EG:密码,用户名,证书等等。<br>Mac电脑上自带的Keychain Access.app就是苹果自己的Keychain读取软件。<br>Keychain是保存在沙盒之外的数据库的,所以在删除App后,在重新下载App后,这些信息依旧存在,并且你可以通过设置一些属性,让你的数据保#####存到iCloud中,达到跨设备存储。</p>
<h2 id="0x01-Keychain本质是什么"><a href="#0x01-Keychain本质是什么" class="headerlink" title="0x01 Keychain本质是什么"></a>0x01 Keychain本质是什么</h2><p>Keychain存储本质是Sqlite。真机地址:/private/var/Keychains/keychain-2.db<br>既然是Sqlite,那么Keychain存储的对象(后面统称为Item),即对应Sqlite上的一条记录而已。</p>
</summary>
<category term="iOS" scheme="http://www.zhz.io/tags/iOS/"/>
</entry>
</feed>