-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
530 lines (408 loc) · 80.7 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<!-- hexo-inject:begin --><!-- hexo-inject:end --><meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 5.4.0">
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin>
<link rel="apple-touch-icon" sizes="180x180" href="/images/favicon-32x32-next.png">
<link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
<link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
<link rel="mask-icon" href="/images/favicon-32x32-next.png" color="#222">
<meta name="google-site-verification" content="vL3EGzFZkG5EXIacMCDRxvYS85228CXfTWdQQp_Nvgo">
<meta name="msvalidate.01" content="code-j7XwhMNr0I">
<link rel="stylesheet" href="/css/main.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/[email protected]/css/all.min.css" integrity="sha256-mUZM63G8m73Mcidfrv5E+Y61y7a12O5mW4ezU3bxqW4=" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/animate.min.css" integrity="sha256-PR7ttpcvz8qrF57fur/yAx1qXMFJeJFiA6pSzWi0OIE=" crossorigin="anonymous">
<script class="next-config" data-name="main" type="application/json">{"hostname":"example.com","root":"/","images":"/images","scheme":"Pisces","darkmode":false,"version":"8.8.1","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":true,"bookmark":{"enable":true,"color":"#222","save":"auto"},"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"stickytabs":false,"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果:${query}","hits_time":"找到 ${hits} 个搜索结果(用时 ${time} 毫秒)","hits":"找到 ${hits} 个搜索结果"}}</script><script src="/js/config.js"></script>
<meta name="description" content="三刀流和拿三把刀,是不一样的。">
<meta property="og:type" content="website">
<meta property="og:title" content="zoro's note">
<meta property="og:url" content="http://example.com/index.html">
<meta property="og:site_name" content="zoro's note">
<meta property="og:description" content="三刀流和拿三把刀,是不一样的。">
<meta property="og:locale" content="zh_CN">
<meta property="article:author" content="zoro">
<meta property="article:tag" content="iOS Objective-C Swift Flutter 程序员 底层原理">
<meta name="twitter:card" content="summary">
<link rel="canonical" href="http://example.com/">
<script class="next-config" data-name="page" type="application/json">{"sidebar":"","isHome":true,"isPost":false,"lang":"zh-CN","comments":"","permalink":"","path":"index.html","title":""}</script>
<script class="next-config" data-name="calendar" type="application/json">""</script>
<title>zoro's note - 学习笔记,查缺补漏</title>
<noscript>
<link rel="stylesheet" href="/css/noscript.css">
</noscript><!-- hexo-inject:begin --><!-- hexo-inject:end -->
</head>
<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
<!-- hexo-inject:begin --><!-- hexo-inject:end --><div class="headband"></div>
<main class="main">
<header class="header" itemscope itemtype="http://schema.org/WPHeader">
<div class="header-inner"><div class="site-brand-container">
<div class="site-nav-toggle">
<div class="toggle" aria-label="切换导航栏" role="button">
<span class="toggle-line"></span>
<span class="toggle-line"></span>
<span class="toggle-line"></span>
</div>
</div>
<div class="site-meta">
<a href="/" class="brand" rel="start">
<i class="logo-line"></i>
<h1 class="site-title">zoro's note</h1>
<i class="logo-line"></i>
</a>
<p class="site-subtitle" itemprop="description">学习笔记,查缺补漏</p>
</div>
<div class="site-nav-right">
<div class="toggle popup-trigger">
</div>
</div>
</div>
<nav class="site-nav">
<ul class="main-menu menu">
<li class="menu-item menu-item-home"><a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a></li>
<li class="menu-item menu-item-archives"><a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档<span class="badge">1</span></a></li>
<li class="menu-item menu-item-tags"><a href="/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>标签<span class="badge">1</span></a></li>
<li class="menu-item menu-item-categories"><a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类<span class="badge">1</span></a></li>
<li class="menu-item menu-item-about"><a href="/about/" rel="section"><i class="fa fa-user fa-fw"></i>关于</a></li>
</ul>
</nav>
</div>
<div class="toggle sidebar-toggle" role="button">
<span class="toggle-line"></span>
<span class="toggle-line"></span>
<span class="toggle-line"></span>
</div>
<aside class="sidebar">
<div class="sidebar-inner sidebar-overview-active">
<ul class="sidebar-nav">
<li class="sidebar-nav-toc">
文章目录
</li>
<li class="sidebar-nav-overview">
站点概览
</li>
</ul>
<div class="sidebar-panel-container">
<!--noindex-->
<div class="post-toc-wrap sidebar-panel">
</div>
<!--/noindex-->
<div class="site-overview-wrap sidebar-panel">
<div class="site-author site-overview-item animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
<img class="site-author-image" itemprop="image" alt="zoro"
src="https://xhp281-blog.oss-cn-beijing.aliyuncs.com/blog_config/zoro_h_02.jpeg">
<p class="site-author-name" itemprop="name">zoro</p>
<div class="site-description" itemprop="description">三刀流和拿三把刀,是不一样的。</div>
</div>
<div class="site-state-wrap site-overview-item animated">
<nav class="site-state">
<div class="site-state-item site-state-posts">
<a href="/archives/">
<span class="site-state-item-count">1</span>
<span class="site-state-item-name">日志</span>
</a>
</div>
<div class="site-state-item site-state-categories">
<a href="/categories/">
<span class="site-state-item-count">1</span>
<span class="site-state-item-name">分类</span></a>
</div>
<div class="site-state-item site-state-tags">
<a href="/tags/">
<span class="site-state-item-count">1</span>
<span class="site-state-item-name">标签</span></a>
</div>
</nav>
</div>
<div class="links-of-author site-overview-item animated">
<span class="links-of-author-item">
<a href="mailto:[email protected]" title="E-Mail → mailto:[email protected]" rel="noopener" target="_blank"><i class="fa fa-envelope fa-fw"></i></a>
</span>
</div>
</div>
</div>
</div>
</aside>
<div class="sidebar-dimmer"></div>
</header>
<div class="back-to-top" role="button" aria-label="返回顶部">
<i class="fa fa-arrow-up"></i>
<span>0%</span>
</div>
<a role="button" class="book-mark-link book-mark-link-fixed"></a>
<noscript>
<div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>
<div class="main-inner index posts-expand">
<div class="post-block">
<article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
<link itemprop="mainEntityOfPage" href="http://example.com/2021/11/23/objc_block_01/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="https://xhp281-blog.oss-cn-beijing.aliyuncs.com/blog_config/zoro_h_02.jpeg">
<meta itemprop="name" content="zoro">
<meta itemprop="description" content="三刀流和拿三把刀,是不一样的。">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="zoro's note">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/2021/11/23/objc_block_01/" class="post-title-link" itemprop="url">Block本质</a>
</h2>
<div class="post-meta-container">
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建时间:2021-11-23 14:50:18" itemprop="dateCreated datePublished" datetime="2021-11-23T14:50:18+08:00">2021-11-23</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar-check"></i>
</span>
<span class="post-meta-item-text">更新于</span>
<time title="修改时间:2021-12-10 00:08:49" itemprop="dateModified" datetime="2021-12-10T00:08:49+08:00">2021-12-10</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-folder"></i>
</span>
<span class="post-meta-item-text">分类于</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/objc/" itemprop="url" rel="index"><span itemprop="name">objc</span></a>
</span>
</span>
</div>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h2 id="Block本质"><a href="#Block本质" class="headerlink" title="Block本质"></a>Block本质</h2><blockquote>
<p>1、Block本质是一个对象,内部有一个isa指针<br>2、封装了函数调用以及函数调用环境的OC对象</p>
</blockquote>
<h3 id="探索本质"><a href="#探索本质" class="headerlink" title="探索本质"></a>探索本质</h3><figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> main(<span class="keyword">int</span> argc, <span class="keyword">const</span> <span class="keyword">char</span> * argv[]) {</span><br><span class="line"> <span class="keyword">@autoreleasepool</span> {</span><br><span class="line"> <span class="keyword">int</span> age = <span class="number">10</span>; <span class="comment">// 默认是 auto 类型变量</span></span><br><span class="line"> <span class="keyword">void</span>(^block)(<span class="keyword">int</span>, <span class="keyword">int</span>) = ^(<span class="keyword">int</span> a, <span class="keyword">int</span> b){</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"this is block a = %d, b = %d"</span>, a, b);</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"this is block, age = %d"</span>,age);</span><br><span class="line"> };</span><br><span class="line"> block(<span class="number">2</span>,<span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>将示例代码转换为C++查看内部结构,与OC代码进行比较,转换代码如下:<br><code>xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m</code></p>
<p>编译之后的代码如下</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span 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[]) {</span><br><span class="line"> <span class="comment">/* @autoreleasepool */</span> { __AtAutoreleasePool __autoreleasepool; </span><br><span class="line"> <span class="keyword">int</span> age = <span class="number">10</span>;</span><br><span class="line"> <span class="keyword">void</span>(*block)(<span class="keyword">int</span>, <span class="keyword">int</span>) = ((<span class="keyword">void</span> (*)(<span class="keyword">int</span>, <span class="keyword">int</span>))&__main_block_impl_0((<span class="keyword">void</span> *)__main_block_func_0, &__main_block_desc_0_DATA, age));</span><br><span class="line"></span><br><span class="line"> ((<span class="keyword">void</span> (*)(__block_impl *, <span class="keyword">int</span>, <span class="keyword">int</span>))((__block_impl *)block)->FuncPtr)((__block_impl *)block, <span class="number">2</span>, <span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h4 id="定义block变量"><a href="#定义block变量" class="headerlink" title="定义block变量"></a>定义block变量</h4><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in"><span class="keyword">void</span></span>(*block)(<span class="keyword">int</span>, <span class="keyword">int</span>) = ((<span class="built_in"><span class="keyword">void</span></span> (*)(<span class="keyword">int</span>, <span class="keyword">int</span>))&__main_block_impl_0((<span class="keyword">void</span> *)__main_block_func_0, &__main_block_desc_0_DATA, age));</span><br></pre></td></tr></table></figure>
<p>上述代码中,可以发现block的定义调用了 <code>__main_block_impl_0</code> 函数,并将 <code>__main_block_impl_0</code> 的地址赋值给了block。下面我们分析下 <code>__main_block_impl_0</code> 的内部结构。</p>
<h5 id="main-block-impl-0-结构体"><a href="#main-block-impl-0-结构体" class="headerlink" title="__main_block_impl_0 结构体"></a>__main_block_impl_0 结构体</h5><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> __<span class="title">main_block_impl_0</span> {</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> __<span class="title">block_impl</span> <span class="title">impl</span>;</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> __<span class="title">main_block_desc_0</span>* <span class="title">Desc</span>;</span></span><br><span class="line"> <span class="keyword">int</span> age;</span><br><span class="line"> __main_block_impl_0(<span class="keyword">void</span> *fp, struct __main_block_desc_0 *desc, <span class="keyword">int</span> _age, <span class="keyword">int</span> flags=<span class="number">0</span>) : <span class="built_in">age</span>(_age) {</span><br><span class="line"> impl.isa = &_NSConcreteStackBlock;</span><br><span class="line"> impl.Flags = flags;</span><br><span class="line"> impl.FuncPtr = fp;</span><br><span class="line"> Desc = desc;</span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure>
<p>在 <code>__main_block_impl_0</code> 结构体内部有一个同名构造函数 <code>__main_block_impl_0</code>,构造函数对变量进行了赋值,最终返回一个结构体。<br>构造函数中对一些变量进行了赋值(或者其他操作)最终会返回一个结构体。<br><code>__main_block_impl_0</code> 内部的构造函数传入了四个参数,<code>(void *)__main_block_func_0</code>、<code>&__main_block_desc_0_DATA</code>、<code>age</code>、<code>flags</code>。其中 <code>flags</code> 有默认参数,在函数调用的时候可以省略不传。最后的 <code>age(_age)</code> 表示传入的 <code>_age</code> 会自动赋值给结构体内部的 <code>age</code> 成员,相当于 <code>age = _age</code>。</p>
<p>接下来分析一下前面三个参数的具体内容</p>
<h5 id="void-main-block-func-0"><a href="#void-main-block-func-0" class="headerlink" title="(void *)__main_block_func_0"></a>(void *)__main_block_func_0</h5><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">void</span> __main_block_func_0(struct __main_block_impl_0 *__cself, <span class="keyword">int</span> a, <span class="keyword">int</span> b) {</span><br><span class="line"> <span class="keyword">int</span> age = __cself->age; <span class="comment">// bound by copy</span></span><br><span class="line"> <span class="built_in">NSLog</span>((NSString *)&__NSConstantStringImpl__var_folders_nf_7714tb2x50vctp5b0v2xmpt4nrww4l_T_main_6c06d8_mi_0, a, b);</span><br><span class="line"> <span class="built_in">NSLog</span>((NSString *)&__NSConstantStringImpl__var_folders_nf_7714tb2x50vctp5b0v2xmpt4nrww4l_T_main_6c06d8_mi_1,age);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>在 <code>__main_block_func_0</code> 函数中首先取出block中 <code>age</code> 的值,然后是两个NSLog的打印信息。在 <code>__main_block_func_0</code> 内部存储了block引用的外部的变量的值。而 <code>__main_block_impl_0</code> 函数中传入的第二个参数是 <code>(void *)__main_block_func_0</code>,也就是说block块内部的代码封装成了 <code>__main_block_func_0</code> 函数,并将 <code>__main_block_func_0</code> 函数的地址传入了 <code>__main_block_impl_0</code> 的构造函数中保存在了结构体内。</p>
<h5 id="amp-main-block-desc-0-DATA"><a href="#amp-main-block-desc-0-DATA" class="headerlink" title="&__main_block_desc_0_DATA"></a>&__main_block_desc_0_DATA</h5><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="class"><span class="keyword">struct</span> __<span class="title">main_block_desc_0</span> {</span></span><br><span class="line"> <span class="keyword">size_t</span> reserved;</span><br><span class="line"> <span class="keyword">size_t</span> Block_size;</span><br><span class="line">} __main_block_desc_0_DATA = { <span class="number">0</span>, <span class="built_in"><span class="keyword">sizeof</span></span>(struct __main_block_impl_0)};</span><br></pre></td></tr></table></figure>
<p>在 <code>__main_block_desc_0_DATA</code> 内部存储了两个参数,<code>reserved</code> 和 <code>Block_size</code>, <code>reserved</code> 直接赋值为0,<code>Block_size</code> 存储的是 <code>__main_block_impl_0</code> 所占用空间大小。最终将 <code>__main_block_desc_0</code> 结构体的地址传入 <code>__main_block_impl_0</code> 并赋值给 <code>Desc</code></p>
<h5 id="age"><a href="#age" class="headerlink" title="age"></a>age</h5><p><code>age</code> 是我们定义的局部变量,因为在block内部使用到了局部变量,所以block声明的时候,这里会将 <code>age</code> 作为参数传入,也就是说block会捕获 <code>age</code>,如果没有在block中使用 <code>age</code> ,这里将只会传入 <code>(void *)__main_block_func_0</code> 和 <code>&__main_block_desc_0_DATA</code> 这两个参数,如果有多个参数,那么传入的时候就会变成多个</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 没有捕获的情况</span></span><br><span class="line"><span class="built_in"><span class="keyword">void</span></span>(*block)(<span class="keyword">int</span>, <span class="keyword">int</span>) = ((<span class="built_in"><span class="keyword">void</span></span> (*)(<span class="keyword">int</span>, <span class="keyword">int</span>))&__main_block_impl_0((<span class="keyword">void</span> *)__main_block_func_0, &__main_block_desc_0_DATA));</span><br><span class="line"><span class="comment">// 捕获多个变量</span></span><br><span class="line"><span class="built_in"><span class="keyword">void</span></span>(*block)(<span class="keyword">int</span>, <span class="keyword">int</span>) = ((<span class="built_in"><span class="keyword">void</span></span> (*)(<span class="keyword">int</span>, <span class="keyword">int</span>))&__main_block_impl_0((<span class="keyword">void</span> *)__main_block_func_0, &__main_block_desc_0_DATA, age, name, numer));</span><br></pre></td></tr></table></figure>
<h4 id="再次查看-main-block-impl-0"><a href="#再次查看-main-block-impl-0" class="headerlink" title="再次查看 __main_block_impl_0"></a>再次查看 __main_block_impl_0</h4><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> __<span class="title">main_block_impl_0</span> {</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> __<span class="title">block_impl</span> <span class="title">impl</span>;</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> __<span class="title">main_block_desc_0</span>* <span class="title">Desc</span>;</span></span><br><span class="line"> <span class="keyword">int</span> age;</span><br><span class="line"> __main_block_impl_0(<span class="keyword">void</span> *fp, struct __main_block_desc_0 *desc, <span class="keyword">int</span> _age, <span class="keyword">int</span> flags=<span class="number">0</span>) : <span class="built_in">age</span>(_age) {</span><br><span class="line"> impl.isa = &_NSConcreteStackBlock;</span><br><span class="line"> impl.Flags = flags;</span><br><span class="line"> impl.FuncPtr = fp; <span class="comment">// block内部代码块地址</span></span><br><span class="line"> Desc = desc; <span class="comment">// block描述信息(占用内存大小)</span></span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure>
<p><code>__main_block_impl_0</code> 的第一个变量就是 <code>__block_impl</code> 结构体,内部代码如下:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> __<span class="title">block_impl</span> {</span></span><br><span class="line"> <span class="keyword">void</span> *isa;</span><br><span class="line"> <span class="keyword">int</span> Flags;</span><br><span class="line"> <span class="keyword">int</span> Reserved;</span><br><span class="line"> <span class="keyword">void</span> *FuncPtr;</span><br><span class="line">};</span><br></pre></td></tr></table></figure>
<p><code>__block_impl</code> 内部有一个 <code>isa</code> 指针。因此可以证明block本质上就是一个OC对象。而在构造函数中将函数传入的值分别存储在 <code>__main_block_impl_0</code> 结构体实例中,最终将结构体的地址赋值给block。</p>
<p>通过上面对 <code>__main_block_impl_0</code> 结构体构造函数三个参数的问题,可以得出结论:<br>1.<code>__block_impl</code>结构体中 <code>isa</code> 指针存储着 <code>&_NSConcreteStackBlock</code> 地址,可以暂时理解为其类对象地址,block 就是 <code>_NSConcreteStackBlock</code> 类型的。<br>2.block代码块中的代码呗封装成 <code>__main_block_func_0</code> 函数,<code>FuncPtr</code> 存储着 <code>__main_block_func_0</code> 函数的地址。<br>3.<code>Desc</code> 指向 <code>__main_block_desc_0</code>结构体对象,其中存储 <code>__main_block_impl_0</code> 结构体所占用的内存大小。</p>
<h4 id="调用block执行内部代码"><a href="#调用block执行内部代码" class="headerlink" title="调用block执行内部代码"></a>调用block执行内部代码</h4><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 执行block代码</span></span><br><span class="line">((<span class="built_in"><span class="keyword">void</span></span> (*)(__block_impl *, <span class="keyword">int</span>, <span class="keyword">int</span>))((__block_impl *)block)->FuncPtr)((__block_impl *)block, <span class="number">2</span>, <span class="number">1</span>);</span><br></pre></td></tr></table></figure>
<p>通过上述代码可以发现,block 调用是通过 <code>block</code> 找到 <code>FuncPtr</code> 直接调用,通上面分析可以明确 block 执行的是 __main_block_impl_0 类型的结构体,但是在 <code>__main_block_impl_0</code> 内部并不能直接找到 <code>FuncPtr</code>,<code>FuncPtr</code> 是存在 <code>__block_impl</code> 中的。</p>
<p><code>block</code> 可以直接调用 <code>__block_impl</code> 中的 <code>FuncPtr</code>原因,通过查看上述源码可以发现,<code>(__block_impl *)</code> 将 block 强制转换为了 <code>__block_impl</code> 类型,因为 <code>__block_impl</code> 是 <code>__main_block_impl_0</code> 结构体的第一个成员,相当于将 <code>__block_impl</code> 结构体的成员拿出来直接放在 <code>__main_block_impl_0</code> 中,也就说明 <code>__block_impl</code> 的内存地址就是 <code>__main_block_impl_0</code> 结构体内存地址的开头。所以可以转换成功,并找到 <code>FuncPtr</code> 成员。</p>
<p>通过上述可以知道,<code>FuncPtr</code> 存储着代码块的函数地址,调用此函数就会执行代码块中的代码,回头查看 <code>__main_block_func_0</code> ,可以发现第一个参数就是 <code>__main_block_impl_0</code> 类型的指针。也就是说将 <code>block</code> 传入 <code>__main_block_func_0</code> 函数中,便于从中捕获 <code>block</code> 的值。</p>
<h4 id="验证block的本质确实是-main-block-impl-0-结构体类型"><a href="#验证block的本质确实是-main-block-impl-0-结构体类型" class="headerlink" title="验证block的本质确实是 __main_block_impl_0 结构体类型"></a>验证block的本质确实是 __main_block_impl_0 结构体类型</h4><p>通过代码证明一下上述内容:通用使用之前的方法,我们按照上面分析block内部结构自定义结构体,并将block内部的结构体强制转换为自定义的结构体,转换成功说明底层结构体确实如之前分析的一样。</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> __block_impl {</span><br><span class="line"> <span class="keyword">void</span> *isa;</span><br><span class="line"> <span class="keyword">int</span> Flags;</span><br><span class="line"> <span class="keyword">int</span> Reserved;</span><br><span class="line"> <span class="keyword">void</span> *FuncPtr;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> __main_block_desc_0 {</span><br><span class="line"> size_t reserved;</span><br><span class="line"> size_t Block_size;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> __main_block_impl_0 {</span><br><span class="line"> <span class="keyword">struct</span> __block_impl impl;</span><br><span class="line"> <span class="keyword">struct</span> __main_block_desc_0* Desc;</span><br><span class="line"> <span class="keyword">int</span> age;</span><br><span class="line">};</span><br><span class="line"></span><br><span 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[]) {</span><br><span class="line"> <span class="keyword">@autoreleasepool</span> {</span><br><span class="line"> <span class="keyword">int</span> age = <span class="number">10</span>; <span class="comment">// 默认是 auto 类型变量</span></span><br><span class="line"> <span class="keyword">void</span>(^block)(<span class="keyword">int</span>, <span class="keyword">int</span>) = ^(<span class="keyword">int</span> a, <span class="keyword">int</span> b){</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"this is block a = %d, b = %d"</span>, a, b);</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"this is block, age = %d"</span>,age);</span><br><span class="line"> };</span><br><span class="line"> <span class="comment">// 进行类型转换</span></span><br><span class="line"> <span class="keyword">struct</span> __main_block_impl_0 *blockStruct = (__bridge <span class="keyword">struct</span> __main_block_impl_0 *)block;</span><br><span class="line"> block(<span class="number">2</span>,<span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>通过xcode断点可以看出自定义结构体可以被成功赋值。</p>
<p> <img src="https://xhp281-blog.oss-cn-beijing.aliyuncs.com/ios_objc/block_%E8%87%AA%E5%AE%9A%E4%B9%89%E6%9E%84%E4%BD%93%E9%AA%8C%E8%AF%81.jpeg" alt="block_自定义构体验证"></p>
<p>接下来断点来到block代码块中,看下对战信息中心的函数调用地址。<code>Debug -> Debug workflow -> always show Disassembly</code> 。</p>
<p> <img src="https://xhp281-blog.oss-cn-beijing.aliyuncs.com/ios_objc/block_%E8%87%AA%E5%AE%9A%E4%B9%89%E6%9E%84%E4%BD%93%E9%AA%8C%E8%AF%81_%E6%B1%87%E7%BC%96.jpeg" alt="block_自定义构体验证_汇编"></p>
<p>通过上图可以看到 <code>block</code> 的地址确实和 <code>FuncPtr</code> 的地址是一样的。</p>
<h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>通过上述分析已经对block底层有了一个基本认识,将上述代码转换为一张图片看下具体的关系。</p>
<p><img src="https://xhp281-blog.oss-cn-beijing.aliyuncs.com/ios_objc/block_%E7%BB%93%E6%9E%84%E4%BD%93%E5%85%B3%E7%B3%BB%E5%9B%BE.png"></p>
<p>block的底层数据可以通过一张图来展示</p>
<p><img src="https://xhp281-blog.oss-cn-beijing.aliyuncs.com/ios_objc/block_%E5%BA%95%E5%B1%82%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84.png" alt="block_底层数据结构"></p>
<h3 id="block变量的捕获"><a href="#block变量的捕获" class="headerlink" title="block变量的捕获"></a>block变量的捕获</h3><p>为了保证block内部能正常访问外部的变量,block有一个变量捕获机制。</p>
<h4 id="局部变量"><a href="#局部变量" class="headerlink" title="局部变量"></a>局部变量</h4><h5 id="auto变量"><a href="#auto变量" class="headerlink" title="auto变量"></a>auto变量</h5><p>上述代码中已经了解过block对age变量的捕获。auto自动变量,离开作用域就销毁,通常局部变量前面自动添加 <code>auto</code> 关键字。自动变量会捕获到block内部,也就是说block会专门新增一个参数来存储变量的值。<code>auto</code> 只存在于局部变量中,访问方式为值传递,通过上述对age参数额解释,我们也可以确定确实是值传递。</p>
<h5 id="static变量"><a href="#static变量" class="headerlink" title="static变量"></a>static变量</h5><p>static修饰的变量为指针传递,同样会被block捕获。</p>
<p>接下来分别添加auto修饰的局部变量和static修饰的局部变量,通过源码查看下他们的区别。</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> main(<span class="keyword">int</span> argc, <span class="keyword">const</span> <span class="keyword">char</span> * argv[]) {</span><br><span class="line"> <span class="keyword">@autoreleasepool</span> {</span><br><span class="line"> auto <span class="keyword">int</span> a = <span class="number">66</span>;</span><br><span class="line"> <span class="keyword">static</span> <span class="keyword">int</span> b = <span class="number">99</span>;</span><br><span class="line"> <span class="keyword">void</span>(^block)(<span class="keyword">void</span>) = ^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"this is block a = %d, b = %d"</span>, a, b);</span><br><span class="line"> };</span><br><span class="line"> a = <span class="number">88</span>;</span><br><span class="line"> b = <span class="number">77</span>;</span><br><span class="line"> block();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"><span class="comment">// log:this is block a = 66, b = 77</span></span><br><span class="line"><span class="comment">// block中a的值并没有被改变,b的值被改变了</span></span><br></pre></td></tr></table></figure>
<p>将源码转换成c++代码之后的两个参数的区别如下图:</p>
<p><img src="https://xhp281-blog.oss-cn-beijing.aliyuncs.com/ios_objc/block_%E4%B8%8D%E5%90%8C%E5%8F%98%E9%87%8F%E6%8D%95%E8%8E%B7%E7%9A%84%E5%8C%BA%E5%88%AB.jpeg"></p>
<p>从上述源码中可以看出,a,b两个变量都有捕获到block内部。但是a传入的是值,b传入的是地址。</p>
<p>这两种变量产生差异的原因是,自动变量可能会销毁,block执行的时候自动变量有可能已经被销毁了,那么此时再去访问被销毁的地址肯定会发生坏的内存访问,因此对于自动变量一定是值传递而不是指针传递。而静态变量不会被销毁,所以完全可以传递地址。因为传递的是值的地址,所以block调用之前修改地址中保存的值,block中的地址是不会变的,所以值会随之改变。</p>
<h4 id="全局变量"><a href="#全局变量" class="headerlink" title="全局变量"></a>全局变量</h4><p>示例代码:</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> a = <span class="number">66</span>;</span><br><span class="line"><span class="keyword">static</span> <span class="keyword">int</span> b = <span class="number">99</span>;</span><br><span 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[]) {</span><br><span class="line"> <span class="keyword">@autoreleasepool</span> {</span><br><span class="line"> <span class="keyword">void</span>(^block)(<span class="keyword">void</span>) = ^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"this is block a = %d, b = %d"</span>, a, b);</span><br><span class="line"> };</span><br><span class="line"> a = <span class="number">88</span>;</span><br><span class="line"> b = <span class="number">77</span>;</span><br><span class="line"> block();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"><span class="comment">// log:this is block a = 88, b = 77</span></span><br></pre></td></tr></table></figure>
<p>生成的c++代码看下全局调用方式,可以发现结构体中没有a,b的成员变量,传递的时候是直接调用。</p>
<p><img src="https://xhp281-blog.oss-cn-beijing.aliyuncs.com/ios_objc/block_%E5%85%A8%E5%B1%80%E5%8F%98%E9%87%8F_c%2B%2B%E6%BA%90%E7%A0%81.jpg" alt="block_全局变量_c++源码"></p>
<p>通过上述代码可以发现,<code>__main_block_impl_0</code> 没有添加任何变量,因此block不需要捕获全局变量,因为全局变量在哪都可以访问。</p>
<p><strong>局部变量因为跨函数访问所以需要捕获,全局变量哪里都可以访问,因此不需要捕获。</strong></p>
<table>
<thead>
<tr>
<th align="center">变量类型</th>
<th align="center">捕获到block内部</th>
<th align="center">访问方式</th>
</tr>
</thead>
<tbody><tr>
<td align="center">局部变量(auto)</td>
<td align="center">✅</td>
<td align="center">值传递</td>
</tr>
<tr>
<td align="center">局部变量(static)</td>
<td align="center">✅</td>
<td align="center">指针传递</td>
</tr>
<tr>
<td align="center">全局变量</td>
<td align="center">❌</td>
<td align="center">直接访问</td>
</tr>
</tbody></table>
<p><strong>总结:局部变量都会被block捕获,自动变量是值捕获,静态变量是地址捕获,全局变量则不会被捕获。</strong></p>
<h5 id="疑问:以下代码中block是否会捕获变量呢?"><a href="#疑问:以下代码中block是否会捕获变量呢?" class="headerlink" title="疑问:以下代码中block是否会捕获变量呢?"></a>疑问:以下代码中block是否会捕获变量呢?</h5><p>1、在block中使用self会不会被捕获?</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#import <span class="meta-string">"Person.h"</span></span></span><br><span class="line"><span class="class"><span class="keyword">@implementation</span> <span class="title">Person</span></span></span><br><span class="line">- (<span class="keyword">void</span>)test {</span><br><span class="line"> <span class="keyword">void</span>(^block)(<span class="keyword">void</span>) = ^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>,<span class="keyword">self</span>);</span><br><span class="line"> };</span><br><span class="line"> block();</span><br><span class="line">}</span><br><span class="line">- (<span class="keyword">instancetype</span>)initWithName:(<span class="built_in">NSString</span> *)name {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">self</span> = [<span class="keyword">super</span> init]) {</span><br><span class="line"> <span class="keyword">self</span>.name = name;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">self</span>;</span><br><span class="line">}</span><br><span class="line">+ (<span class="keyword">void</span>) test2 {</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"类方法test2"</span>);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">@end</span></span><br></pre></td></tr></table></figure>
<p>同样转化为c++代码查看其内部结构</p>
<p><img src="https://xhp281-blog.oss-cn-beijing.aliyuncs.com/ios_objc/block_%E6%8D%95%E8%8E%B7_%E7%A4%BA%E4%BE%8B%E4%BB%A3%E7%A0%81.jpg" alt="block_捕获_示例代码"></p>
<p>上图中可以发现,self同样被block捕获,通过test方法发可以发现,test方法默认传递了两个参数self和_cmd。类方法test也是同样的。</p>
<p>不论对象方法还是类方法都会默认将self作为参数传递给方法内部,既然是作为参数传入,那么self肯定是局部变量。局部变量肯定会被block捕获。</p>
<p>2、在block中使用成员变量或者调用实例的属性会有什么不同的结果?</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">- (<span class="keyword">void</span>)test {</span><br><span class="line"> <span class="keyword">void</span>(^block)(<span class="keyword">void</span>) = ^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>,<span class="keyword">self</span>.name);</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>,_name);</span><br><span class="line"> };</span><br><span class="line"> block();</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><img src="https://xhp281-blog.oss-cn-beijing.aliyuncs.com/ios_objc/block_%E8%87%AA%E5%AE%9A%E4%B9%89%E5%AF%B9%E8%B1%A1%E5%B1%9E%E6%80%A7%E5%92%8C%E6%88%90%E5%91%98%E5%8F%98%E9%87%8F.jpg" alt="block_自定义对象属性和成员变量"></p>
<p>通过上面图片可以发现,即使block中使用的是实例对象的属性,block中捕获的仍然是实例对象,并通过实例对象通过不同的方式去获取使用到的属性。</p>
<h3 id="block的类型"><a href="#block的类型" class="headerlink" title="block的类型"></a>block的类型</h3><p>block对象是什么类型的,之前的代码中提到过,通过源码可以知道block中的isa指针指向的是 <code>_NSConcreteStackBlock</code> 类对象地址。那么block的类型是否就是 <code>_NSConcreteStackBlock</code>类型吗?</p>
<p>通过代码调用class方法或者isa指针查看具体类型。</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span 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[]) {</span><br><span class="line"> <span class="keyword">@autoreleasepool</span> {</span><br><span class="line"> <span class="keyword">void</span> (^block)(<span class="keyword">void</span>) = ^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"Hello"</span>);</span><br><span class="line"> };</span><br><span class="line"> </span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, [block <span class="keyword">class</span>]);</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, [[block <span class="keyword">class</span>] superclass]);</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, [[[block <span class="keyword">class</span>] superclass] superclass]);</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, [[[[block <span class="keyword">class</span>] superclass] superclass] superclass]);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"><span class="comment">// log: __NSGlobalBlock__ : __NSGlobalBlock : NSBlock : NSObject</span></span><br></pre></td></tr></table></figure>
<p>从上述打印内容可以看出block最终都是继承自NSBlock类型,而NSBlock继承于NSObjcet。那么block其中的isa指针其实是来自NSObject中的。证明了block的本质其实就是OC对象。</p>
<h4 id="block的3种类型"><a href="#block的3种类型" class="headerlink" title="block的3种类型"></a>block的3种类型</h4><p>block有3中类型</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">__NSGlobalBlock__ ( _NSConcreteGlobalBlock )</span><br><span class="line">__NSStackBlock__ ( _NSConcreteStackBlock )</span><br><span class="line">__NSMallocBlock__ ( _NSConcreteMallocBlock )</span><br></pre></td></tr></table></figure>
<p>通过代码查看一下block在什么情况下其类型会各不相同</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> main(<span class="keyword">int</span> argc, <span class="keyword">const</span> <span class="keyword">char</span> * argv[]) {</span><br><span class="line"> <span class="keyword">@autoreleasepool</span> {</span><br><span class="line"> <span class="comment">// 1. 内部没有调用外部变量的block</span></span><br><span class="line"> <span class="keyword">void</span> (^block1)(<span class="keyword">void</span>) = ^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"Hello"</span>);</span><br><span class="line"> };</span><br><span class="line"> <span class="comment">// 2. 内部调用外部变量的block</span></span><br><span class="line"> <span class="keyword">int</span> a = <span class="number">10</span>;</span><br><span class="line"> <span class="keyword">void</span> (^block2)(<span class="keyword">void</span>) = ^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"Hello - %d"</span>,a);</span><br><span class="line"> };</span><br><span class="line"> <span class="comment">// 3. 直接调用的block的class</span></span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"%@ %@ %@"</span>, [block1 <span class="keyword">class</span>], [block2 <span class="keyword">class</span>], [^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"%d"</span>,a);</span><br><span class="line"> } <span class="keyword">class</span>]);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">} </span><br></pre></td></tr></table></figure>
<p>通过打印内容确实发现block的三种类型</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">__NSGlobalBlock__ __NSMallocBlock__ __NSStackBlock__</span><br></pre></td></tr></table></figure>
<p>上述代码转化为c++代码查看源码时却发现block的类型与打印出来的类型不一样,c++源码中三个block的isa指针全部都指向_NSConcreteStackBlock类型地址。</p>
<p>可以猜测runtime运行时过程中也许对类型进行了转变。最终类型当然以runtime运行时类型也就是我们打印出的类型为准。</p>
<h4 id="block的内存中的存储"><a href="#block的内存中的存储" class="headerlink" title="block的内存中的存储"></a>block的内存中的存储</h4><p><img src="/Users/xhp281/Documents/%E7%AC%94%E8%AE%B0/iOS/blog_imags/block/block_%E4%B8%8D%E5%90%8Cblock%E7%9A%84%E5%AD%98%E6%94%BE%E5%8C%BA%E5%9F%9F.png" alt="block_不同block的存放区域"></p>
<p>上图中可以发现,根据block的类型不同,block存放在不同的区域中。 数据段中的 <code>__NSGlobalBlock__</code> 直到程序结束才会被回收,不过我们很少使用到 <code>__NSGlobalBlock__</code> 类型的block,因为这样使用block并没有什么意义。</p>
<p><code>__NSStackBlock__</code> 类型的block存放在栈中,我们知道栈中的内存由系统自动分配和释放,作用域执行完毕之后就会被立即释放,而在相同的作用域中定义block并且调用block似乎也多此一举。</p>
<p><code>__NSMallocBlock__</code> 是在平时编码过程中最常使用到的。存放在堆中需要我们自己进行内存管理。</p>
<h4 id="block的如何定义其类型"><a href="#block的如何定义其类型" class="headerlink" title="block的如何定义其类型"></a>block的如何定义其类型</h4><p>block是如何定义其类型,依据什么来为block定义不同类型并分配不同的空间呢?首先看下面内容</p>
<table>
<thead>
<tr>
<th align="center">block类型</th>
<th align="center">环境</th>
<th align="center">内存区域</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><em>NSGlobalBlock</em></td>
<td align="center">没有访问auto变量</td>
<td align="center">数据段</td>
</tr>
<tr>
<td align="center"><em>NSStackBlock</em></td>
<td align="center">访问了auto变量</td>
<td align="center">栈</td>
</tr>
<tr>
<td align="center"><em>NSMallocBlock</em></td>
<td align="center"><em>NSStackBlock</em> 调用了copy</td>
<td align="center">堆</td>
</tr>
</tbody></table>
<p>接着我们使用代码验证上述问题,首先关闭ARC回到MRC环境下,因为ARC会帮助我们做很多事情,可能会影响我们的观察。</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// MRC环境!!!</span></span><br><span 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[]) {</span><br><span class="line"> <span class="keyword">@autoreleasepool</span> {</span><br><span class="line"> <span class="comment">// Global:没有访问auto变量:__NSGlobalBlock__</span></span><br><span class="line"> <span class="keyword">void</span> (^block1)(<span class="keyword">void</span>) = ^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"block1---------"</span>);</span><br><span class="line"> };</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// Stack:访问了auto变量: __NSStackBlock__</span></span><br><span class="line"> <span class="keyword">int</span> a = <span class="number">10</span>;</span><br><span class="line"> <span class="keyword">void</span> (^block2)(<span class="keyword">void</span>) = ^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"block2---------%d"</span>, a);</span><br><span class="line"> };</span><br><span class="line"> </span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"%@ %@"</span>, [block1 <span class="keyword">class</span>], [block2 <span class="keyword">class</span>]);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// __NSStackBlock__调用copy : __NSMallocBlock__</span></span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, [[block2 <span class="keyword">copy</span>] <span class="keyword">class</span>]);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"><span class="comment">// log: __NSGlobalBlock__ __NSStackBlock__ __NSMallocBlock__</span></span><br></pre></td></tr></table></figure>
<p>通过打印的内容可以发现:</p>
<p>1.<strong>没有访问auto变量的block是 <code>__NSGlobalBlock__</code> 类型的,存放在数据段中。</strong></p>
<p>2.<strong>访问了auto变量的block是 <code>__NSStackBlock__</code> 类型的,存放在栈中。</strong></p>
<p>3.<strong><code>__NSStackBlock__</code> 类型的block调用copy成为 <code>__NSMallocBlock__</code> 类型并被复制存放在堆中。</strong></p>
<p>上面提到过 <code>__NSGlobalBlock__</code> 类型的我们很少使用到,因为如果不需要访问外界的变量,直接通过函数实现就可以了,不需要使用block。</p>
<p>但是 <code>__NSStackBlock__</code> 访问了auto变量,并且是存放在栈中的,上面提到过,栈中的代码在作用域结束之后内存就会被销毁,那么我们很有可能block内存销毁之后才去调用他,那样就会发生问题,通过下面代码可以证实这个问题。</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> (^block)(<span class="keyword">void</span>);</span><br><span class="line"><span class="keyword">void</span> test() {</span><br><span class="line"> <span class="comment">// __NSStackBlock__</span></span><br><span class="line"> <span class="keyword">int</span> a = <span class="number">10</span>;</span><br><span class="line"> block = ^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"block---------%d"</span>, a);</span><br><span class="line"> };</span><br><span class="line">}</span><br><span 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[]) {</span><br><span class="line"> <span class="keyword">@autoreleasepool</span> {</span><br><span class="line"> test();</span><br><span class="line"> block();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"><span class="comment">// log: block---------68334376</span></span><br></pre></td></tr></table></figure>
<p>可以发现a的值变为了不可控的一个数字。因为上述代码中创建的block是 <code>__NSStackBlock__</code> 类型的,因此block是存储在栈中的,那么当test函数执行完毕之后,栈内存中block所占用的内存已经被系统回收,因此就有可能出现乱得数据。查看其c++代码可以更清楚的理解。</p>
<p><img src="https://xhp281-blog.oss-cn-beijing.aliyuncs.com/ios_objc/block_%E6%A0%88%E5%86%85%E5%AD%98%E8%A2%AB%E5%9B%9E%E6%94%B6.png" alt="block_栈内存被回收"></p>
<p>为了避免这种情况发生,可以通过copy将 <strong>NSStackBlock</strong> 类型的block转化为 <strong>NSMallocBlock</strong> 类型的block,将block存储在堆中,以下是修改后的代码。</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> (^block)(<span class="keyword">void</span>);</span><br><span class="line"><span class="keyword">void</span> test()</span><br><span class="line">{</span><br><span class="line"> <span class="comment">// __NSStackBlock__ 调用copy 转化为__NSMallocBlock__</span></span><br><span class="line"> <span class="keyword">int</span> age = <span class="number">10</span>;</span><br><span class="line"> block = [^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"block---------%d"</span>, age);</span><br><span class="line"> } <span class="keyword">copy</span>];</span><br><span class="line"> [block release];</span><br><span class="line">}</span><br><span class="line"><span class="comment">// log: block---------10</span></span><br></pre></td></tr></table></figure>
<p>那么其他类型的block调用copy会改变block类型吗?下面表格已经展示的很清晰了。</p>
<table>
<thead>
<tr>
<th align="center">block类型</th>
<th align="center">内存区域</th>
<th align="center">copy效果</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><em>NSGlobalBlock</em></td>
<td align="center">数据段</td>
<td align="center">什么都不做,不改变类型</td>
</tr>
<tr>
<td align="center"><em>NSStackBlock</em></td>
<td align="center">栈</td>
<td align="center">从栈赋值到堆,改变类型为 <em>NSMallocBlock</em></td>
</tr>
<tr>
<td align="center"><em>NSMallocBlock</em></td>
<td align="center">堆</td>
<td align="center">引用计数增加,不改变类型</td>
</tr>
</tbody></table>
<p>所以在平时开发过程中MRC环境下经常需要使用copy来保存block,将栈上的block拷贝到堆中,即使栈上的block被销毁,堆上的block也不会被销毁,需要我们自己调用release操作来销毁。而在ARC环境下系统会自动调用copy操作,使block不会被销毁。</p>
<h3 id="ARC帮我们做了什么"><a href="#ARC帮我们做了什么" class="headerlink" title="ARC帮我们做了什么"></a>ARC帮我们做了什么</h3><p>在ARC环境下,编译器会根据情况自动将栈上的block进行一次copy操作,将block复制到堆上。</p>
<p><strong>什么情况下ARC会自动将block进行一次copy操作?</strong> (以下代码都在ARC环境下执行)</p>
<h4 id="1-block作为函数返回值时"><a href="#1-block作为函数返回值时" class="headerlink" title="1. block作为函数返回值时"></a>1. block作为函数返回值时</h4><figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="keyword">void</span>(^Block)(<span class="keyword">void</span>);</span><br><span class="line">Block myblock() {</span><br><span class="line"> <span class="keyword">int</span> a = <span class="number">10</span>;</span><br><span class="line"> Block block = ^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"----------%d"</span>,a);</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">return</span> block;</span><br><span class="line">}</span><br><span 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[]) {</span><br><span class="line"> <span class="keyword">@autoreleasepool</span> {</span><br><span class="line"> Block block = myblock();</span><br><span class="line"> block();</span><br><span class="line"> <span class="comment">// 打印block类型为 __NSMallocBlock__</span></span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>,[block <span class="keyword">class</span>]);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"><span class="comment">// log: ----------10 </span></span><br><span class="line"><span class="comment">// log: __NSMallocBlock__</span></span><br></pre></td></tr></table></figure>
<p><strong>上文提到过,如果在block中访问了auto变量时,block的类型为<code>__NSStackBlock__</code>,上面打印内容发现blcok为 <code>__NSMallocBlock__</code> 类型的,并且可以正常打印出a的值,说明block内存并没有被销毁。</strong></p>
<p><strong>block进行copy操作会转化为 <code>__NSMallocBlock__</code> 类型,来将block复制到堆中,那么说明RAC在 block作为函数返回值时会自动帮助我们对block进行copy操作,以保存block,并在适当的地方进行release操作。</strong></p>
<h4 id="2-将block赋值给-strong指针时"><a href="#2-将block赋值给-strong指针时" class="headerlink" title="2. 将block赋值给__strong指针时"></a>2. 将block赋值给__strong指针时</h4><p>block被强指针引用时,RAC也会自动对block进行一次copy操作。</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> main(<span class="keyword">int</span> argc, <span class="keyword">const</span> <span class="keyword">char</span> * argv[]) {</span><br><span class="line"> <span class="keyword">@autoreleasepool</span> {</span><br><span class="line"> <span class="comment">// block内没有访问auto变量</span></span><br><span class="line"> Block block = ^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"block---------"</span>);</span><br><span class="line"> };</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>,[block <span class="keyword">class</span>]);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// block内访问了auto变量,但没有赋值给__strong指针</span></span><br><span class="line"> <span class="keyword">int</span> a = <span class="number">10</span>;</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>,[^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"block1---------%d"</span>, a);</span><br><span class="line"> } <span class="keyword">class</span>]);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// block赋值给__strong指针</span></span><br><span class="line"> Block block2 = ^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"block2---------%d"</span>, a);</span><br><span class="line"> };</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>,[block2 <span class="keyword">class</span>]);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>查看打印内容可以看出,当block被赋值给__strong指针时,RAC会自动进行一次copy操作。</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">__NSGlobalBlock__ __NSStackBlock__ __NSMallocBlock__</span><br></pre></td></tr></table></figure>
<h4 id="3-block作为Cocoa-API中方法名含有usingBlock的方法参数时"><a href="#3-block作为Cocoa-API中方法名含有usingBlock的方法参数时" class="headerlink" title="3. block作为Cocoa API中方法名含有usingBlock的方法参数时"></a>3. block作为Cocoa API中方法名含有usingBlock的方法参数时</h4><p>例如:遍历数组的block方法,将block作为参数的时候。</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">NSArray</span> *array = @[];</span><br><span class="line">[array enumerateObjectsUsingBlock:^(<span class="keyword">id</span> _Nonnull obj, <span class="built_in">NSUInteger</span> idx, <span class="built_in">BOOL</span> * _Nonnull stop) {</span><br><span class="line">}];</span><br></pre></td></tr></table></figure>
<h4 id="4-block作为GCD-API的方法参数时"><a href="#4-block作为GCD-API的方法参数时" class="headerlink" title="4. block作为GCD API的方法参数时"></a>4. block作为GCD API的方法参数时</h4><p>例如:GDC的一次性函数或延迟执行的函数,执行完block操作之后系统才会对block进行release操作。</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="built_in">dispatch_once_t</span> onceToken;</span><br><span class="line"><span class="built_in">dispatch_once</span>(&onceToken, ^{</span><br><span class="line"> </span><br><span class="line">}); </span><br><span class="line">dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(<span class="number">1.0</span> * <span class="built_in">NSEC_PER_SEC</span>)), dispatch_get_main_queue(), ^{</span><br><span class="line"> </span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<h3 id="block声明写法"><a href="#block声明写法" class="headerlink" title="block声明写法"></a>block声明写法</h3><p>通过上面对MRC及ARC环境下block的不同类型的分析,总结出不同环境下block属性建议写法。</p>
<p>MRC下block属性的建议写法</p>
<p><code>@property (copy, nonatomic) void (^block)(void);</code></p>
<p>ARC下block属性的建议写法</p>
<p><code>@property (strong, nonatomic) void (^block)(void);</code> </p>
<p><code>@property (copy, nonatomic) void (^block)(void);</code></p>
<h3 id="block对对象变量的捕获"><a href="#block对对象变量的捕获" class="headerlink" title="block对对象变量的捕获"></a>block对对象变量的捕获</h3><p>block一般使用过程中都是对对象变量的捕获,那么对象变量的捕获同基本数据类型变量相同吗?</p>
<p>查看一下代码思考:当在block中访问的为对象类型时,对象什么时候会销毁?</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="keyword">void</span> (^Block)(<span class="keyword">void</span>);</span><br><span 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[]) {</span><br><span class="line"> <span class="keyword">@autoreleasepool</span> {</span><br><span class="line"> Block block;</span><br><span class="line"> {</span><br><span class="line"> Person *person = [[Person alloc] init];</span><br><span class="line"> person.name = <span class="string">@"cook"</span>;</span><br><span class="line"> </span><br><span class="line"> block = ^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"------block内部%@"</span>,person.name);</span><br><span class="line"> };</span><br><span class="line"> } <span class="comment">// 执行完毕,person没有被释放</span></span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"--------"</span>);</span><br><span class="line"> } <span class="comment">// person 释放</span></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>大括号执行完毕之后,<code>person</code> 依然不会被释放。<code>person</code> 为 <code>aotu</code> 变量,传入的 <code>block</code> 的变量同样为 <code>person</code>,即 <code>block</code> 有一个强引用引用 <code>person</code>,所以 <code>block </code>不被销毁的话,<code>peroson</code> 也不会销毁。 查看源代码确实如此</p>
<p><img src="https://xhp281-blog.oss-cn-beijing.aliyuncs.com/ios_objc/block_block%E5%AF%B9%E5%AF%B9%E8%B1%A1%E5%8F%98%E9%87%8F%E7%9A%84%E6%8D%95%E8%8E%B7.png" alt="block_block对对象变量的捕获"></p>
<p>将上述代码转移到MRC环境下,在MRC环境下即使block还在,<code>person</code> 却被释放掉了。因为MRC环境下block在栈空间,栈空间对外面的 <code>person</code> 不会进行强引用。</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="keyword">void</span> (^Block)(<span class="keyword">void</span>);</span><br><span 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[]) {</span><br><span class="line"> <span class="keyword">@autoreleasepool</span> {</span><br><span class="line"> Block block;</span><br><span class="line"> {</span><br><span class="line"> Person *person = [[Person alloc] init];</span><br><span class="line"> person.name = <span class="string">@"cook"</span>;</span><br><span class="line"> </span><br><span class="line"> block = ^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"------block内部%@"</span>,person.name);</span><br><span class="line"> };</span><br><span class="line"> [person release];</span><br><span class="line"> } <span class="comment">// 执行完毕,person没有被释放</span></span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"--------"</span>);</span><br><span class="line"> } <span class="comment">// person 释放</span></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>block调用copy操作之后,person不会被释放。</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">block = [^{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"------block内部%@"</span>,person.name);</span><br><span class="line">} <span class="keyword">copy</span>];</span><br></pre></td></tr></table></figure>
<p>上面提到只需要对栈空间的 <code>block</code> 进行一次 <code>copy</code> 操作,将栈空间的 <code>block</code> 拷贝到堆中,<code>person</code> 就不会被释放,说明堆空间的 <code>block</code> 可能会对 <code>person</code> 进行一次 <code>retain</code> 操作,保证 <code>person</code> 不会被销毁。堆空间的 <code>block</code> 自己销毁之后也会对持有的对象进行 <code>release</code> 操作。</p>
<p>也就是说栈空间上的 <code>block</code> 不会对对象强引用,堆空间的 <code>block</code> 有能力持有外部调用的对象,即对对象进行强引用或去除强引用的操作。</p>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
</div>
</div>
</main>
<footer class="footer">
<div class="footer-inner">
<div class="beian"><a href="https://beian.miit.gov.cn/" rel="noopener" target="_blank">京ICP备20002876号 </a>
</div>
<div class="copyright">
© 2020 –
<span itemprop="copyrightYear">2021</span>
<span class="with-love">
<i class="fa fa-heart"></i>
</span>
<span class="author" itemprop="copyrightHolder">zoro</span>
</div>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/anime.min.js" integrity="sha256-XL2inqUJaslATFnHdJOi9GfQ60on8Wx1C2H8DYiN1xY=" crossorigin="anonymous"></script>
<script src="/js/comments.js"></script><script src="/js/utils.js"></script><script src="/js/motion.js"></script><script src="/js/next-boot.js"></script><script src="/js/bookmark.js"></script>
<script class="next-config" data-name="pdf" type="application/json">{"object_url":{"url":"https://cdn.jsdelivr.net/npm/[email protected]/pdfobject.min.js","integrity":"sha256-ph3Dk89VmuTVXG6x/RDzk53SU9LPdAh1tpv0UvnDZ2I="},"url":"/lib/pdf/web/viewer.html"}</script>
<script src="/js/third-party/tags/pdf.js"></script><!-- hexo-inject:begin --><!-- hexo-inject:end -->
</body>
</html>