-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path16791200795656.html
634 lines (515 loc) · 28.9 KB
/
16791200795656.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=0.8,minimum-scale=0.8, maximum-scale=0.8,user-scalable=no,viewport-fit=cover">
<title>
面试 (五) :原理篇 - 宋明的博客
</title>
<link href="atom.xml" rel="alternate" title="宋明的博客" type="application/atom+xml">
<link rel="stylesheet" href="asset/css/style.min.css">
<link rel="stylesheet" href="asset/css/doc.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/css/font-awesome.min.css">
<!-- Global site tag (gtag.js) - Google Analytics -->
<!-- 百度分析 -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/L2Dwidget.min.js"></script>
<script src="asset/app.js"></script>
</head>
<body style="overflow-x: hidden;">
<section class="hero">
<div class="hero-head">
<nav class="navbar" role="navigation" aria-label="main navigation">
<div class="container">
<div class="navbar-brand">
<a target="self" class="navbar-item " href="index.html">Home</a>
<a target="_self" class="navbar-item " href="archives.html">Archives</a>
<a role="button" id="navbarSNSRssSwitchBtn" class="navbar-burger burger" aria-label="menu" aria-expanded="false" data-target="navbarSNSRssButtons">
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
<div id="navbarSNSRssButtons" class="navbar-menu">
<div class="navbar-start">
</div>
<div class="navbar-end">
<div class="navbar-item">
<!--buttons start-->
<div class="buttons">
<a href="mailto: [email protected]" target="_blank" title="email">
<span class="icon is-large has-text-grey-darker">
<svg class="svg-inline--fa fa-email fa-w-14 fa-lg" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1208" width="200" height="200"><path fill="currentColor" d="M935.335233 153.62202h-846.666656a84.666666 84.666666 0 0 0-84.666666 84.666666v550.333327a84.666666 84.666666 0 0 0 84.666666 84.666665h846.666656a84.666666 84.666666 0 0 0 84.666666-84.666665v-550.333327a84.666666 84.666666 0 0 0-84.666666-84.666666z m-27.293711 213.952665L557.558216 549.672927a94.993177 94.993177 0 0 1-87.065555 0.197555l-354.612218-182.202664a42.333333 42.333333 0 0 1 38.698311-75.308177l354.606573 182.202664a10.196689 10.196689 0 0 0 9.341556-0.022577l350.477662-182.089776a42.333333 42.333333 0 1 1 39.034155 75.127555z" fill="#2c2c2c" p-id="1209"></path></svg>
</span>
</a>
<a href="atom.xml" target="_blank" title="RSS">
<span class="icon is-large has-text-black-bis">
<svg class="svg-inline--fa fa-rss fa-w-14 fa-lg" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="rss" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" data-fa-i2svg=""><path fill="currentColor" d="M128.081 415.959c0 35.369-28.672 64.041-64.041 64.041S0 451.328 0 415.959s28.672-64.041 64.041-64.041 64.04 28.673 64.04 64.041zm175.66 47.25c-8.354-154.6-132.185-278.587-286.95-286.95C7.656 175.765 0 183.105 0 192.253v48.069c0 8.415 6.49 15.472 14.887 16.018 111.832 7.284 201.473 96.702 208.772 208.772.547 8.397 7.604 14.887 16.018 14.887h48.069c9.149.001 16.489-7.655 15.995-16.79zm144.249.288C439.596 229.677 251.465 40.445 16.503 32.01 7.473 31.686 0 38.981 0 48.016v48.068c0 8.625 6.835 15.645 15.453 15.999 191.179 7.839 344.627 161.316 352.465 352.465.353 8.618 7.373 15.453 15.999 15.453h48.068c9.034-.001 16.329-7.474 16.005-16.504z"></path></svg><!-- <i class="fas fa-rss fa-lg"></i> -->
</span>
</a>
</div>
<!--buttons end-->
</div>
</div>
</div>
</div>
</nav>
</div>
<div class="hero-body ct-body"></div>
</section>
<section class="ct-body">
<div class="container">
<div class="columns is-variable bd-klmn-columns is-4">
<div class="column is-two-thirds">
<div class="post-body single-content">
<div class="card-image">
<figure class="random-img">
</figure>
</div>
<h1 class="title">
面试 (五) :原理篇
</h1>
<div class="media">
<figure class="media-left">
<p class="image is-48x48">
<img class="is-rounded" src="">
</p>
</figure>
<div class="media-content">
<div class="content">
<p style="line-height: 30px; font-size: 12px;">
<a href="http://apolla.cc">宋明</a>
<span style="color: #ccc;">|</span>
<span class="date"><i class="fa fa-calendar-check-o" aria-hidden="true"></i> 2023/03/18</span>
<span class="tran-posted-in">posted in</span>
<span class="posted-in"><a href='%E8%91%B5%E8%8A%B1%E5%AE%9D%E5%85%B8.html'><i class="fa fa-folder" aria-hidden="true"></i> 葵花宝典</a></span>
</p>
</div>
</div>
</div>
</div>
<article class="markdown-body single-content">
<p>2015年11月25日 星期三<br />
10:08<br />
runtime怎么添加属性、方法等<br />
• ivar表示成员变量<br />
• class_addIvar<br />
• class_addMethod<br />
• class_addProperty<br />
• class_addProtocol<br />
• class_replaceProperty</p>
<p>是否可以把比较耗时的操作放在NSNotificationCenter中<br />
•首先必须明确通知在哪个线程中发出,那么处理接受到通知的方法也在这个线程中调用<br />
•如果在异步线程发的通知,那么可以执行比较耗时的操作;<br />
•如果在主线程发的通知,那么就不可以执行比较耗时的操作</p>
<p>runtime如何实现weak属性<br />
•首先要搞清楚weak属性的特点</p>
<p>weak策略表明该属性定义了一种“非拥有关系” (nonowning relationship)。<br />
为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。此特质同assign类似;<br />
然而在属性所指的对象遭到摧毁时,属性值也会清空(nil out)<br />
•那么runtime如何实现weak变量的自动置nil?</p>
<p>runtime对注册的类,会进行布局,会将weak对象放入一个hash表中。<br />
用weak指向的对象内存地址作为key,当此对象的引用计数为0的时候会调用对象的dealloc方法,<br />
假设weak指向的对象内存地址是a,那么就会以a为key,在这个weak hash表中搜索,找到所有以a为key的weak对象,从而设置为nil。<br />
weak属性需要在dealloc中置nil么<br />
•在ARC环境无论是强指针还是弱指针都无需在dealloc设置为nil,ARC会自动帮我们处理<br />
•即便是编译器不帮我们做这些,weak也不需要在dealloc中置nil<br />
•在属性所指的对象遭到摧毁时,属性值也会清空</p>
<p>//模拟下weak的setter方法,大致如下</p>
<ul>
<li>(void)setObject:(NSObject *)object<br />
{<br />
objc_setAssociatedObject( self, "object", object, OBJC_ASSOCIATION_ASSIGN);<br />
[object cyl_runAtDealloc:^{<br />
_object = nil;<br />
}];<br />
}<br />
#一个Objective-C对象如何进行内存布局?(考虑有父类的情况)<br />
•所有父类的成员变量和自己的成员变量都会存放在该对象所对应的存储空间中<br />
•父类的方法和自己的方法都会缓存在类对象的方法缓存中,类方法是缓存在元类对象中<br />
•每一个对象内部都有一个isa指针,指向他的类对象,类对象中存放着本对象的如下信息<br />
○对象方法列表<br />
○成员变量的列表<br />
○属性列表</li>
</ul>
<p>每个Objective-C对象都有相同的结构,如下图所示</p>
<table>
<thead>
<tr>
<th><strong>Objective-C 对象的结构图</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>ISA指针</td>
</tr>
<tr>
<td>根类(NSObject)的实例变量</td>
</tr>
<tr>
<td>倒数第二层父类的实例变量</td>
</tr>
<tr>
<td>...</td>
</tr>
<tr>
<td>父类的实例变量</td>
</tr>
<tr>
<td>类的实例变量</td>
</tr>
</tbody>
</table>
<p>•根类对象就是NSObject,它的super class指针指向nil<br />
类对象既然称为对象,那它也是一个实例。类对象中也有一个isa指针指向它的元类(metaclass),即类对象是元类的实例。元类内部存放的是类方法列表,根元类的isa指针指向自己,superclass指针指向NSObject类</p>
<p>#一个objc对象的isa的指针指向什么?有什么作用?<br />
•每一个对象内部都有一个isa指针,这个指针是指向它的真实类型<br />
•根据这个指针就能知道将来调用哪个类的方法<br />
下面的代码输出什么?</p>
<p>@implementationSon : Father</p>
<ul>
<li>(id)init<br />
{<br />
self = [super init];<br />
if (self) {<br />
NSLog( @"%@", NSStringFromClass([self class]));<br />
NSLog( @"%@", NSStringFromClass([super class]));<br />
}<br />
return self;<br />
}<br />
@end<br />
•答案:都输出Son<br />
•这个题目主要是考察关于objc中对self和super的理解:<br />
○self是类的隐藏参数,指向当前调用方法的这个类的实例。而super本质是一个编译器标示符,和self是指向的同一个消息接受者<br />
○当使用self调用方法时,会从当前类的方法列表中开始找,如果没有,就从父类中再找;<br />
○而当使用super时,则从父类的方法列表中开始找。然后调用父类的这个方法<br />
○调用[self class]时,会转化成objc_msgSend函数</li>
</ul>
<p>id objc_msgSend(id self, SEL op, ...)<br />
○调用[super class]时,会转化成objc_msgSendSuper函数</p>
<p>id objc_msgSendSuper(struct objc_super *super, SEL op, ...)<br />
○第一个参数是 objc_super 这样一个结构体,其定义如下</p>
<p>struct objc_super {<br />
__unsafe_unretained idreceiver;<br />
__unsafe_unretained Class super_class;<br />
};<br />
○第一个成员是receiver,类似于上面的objc_msgSend函数第一个参数self<br />
○第二个成员是记录当前类的父类是什么,告诉程序从父类中开始找方法,找到方法后,最后内部是使用objc_msgSend(objc_super->receiver,@selector(class))去调用, 此时已经和[self class]调用相同了,故上述输出结果仍然返回Son<br />
○ objc Runtime开源代码对- (Class)class方法的实现</p>
<p>-(Class)class {<br />
return object_getClass(self);<br />
}<br />
runtime如何通过selector找到对应的IMP地址?(分别考虑类方法和实例方法)<br />
•每一个类对象中都一个对象方法列表(对象方法缓存)<br />
•类方法列表是存放在类对象中isa指针指向的元类对象中(类方法缓存)<br />
•方法列表中每个方法结构体中记录着方法的名称,方法实现,以及参数类型,其实selector本质就是方法名称,通过这个方法名称就可以在方法列表中找到对应的方法实现.<br />
•当我们发送一个消息给一个NSObject对象时,这条消息会在对象的类对象方法列表里查找<br />
•当我们发送一个消息给一个类时,这条消息会在类的Meta Class对象的方法列表里查找<br />
objc中的类方法和实例方法有什么本质区别和联系<br />
•类方法:<br />
○类方法是属于类对象的<br />
○类方法只能通过类对象调用<br />
○类方法中的self是类对象<br />
○类方法可以调用其他的类方法<br />
○类方法中不能访问成员变量<br />
○类方法中不能直接调用对象方法<br />
○类方法是存储在元类对象的方法缓存中<br />
•实例方法:<br />
○实例方法是属于实例对象的<br />
○实例方法只能通过实例对象调用<br />
○实例方法中的self是实例对象<br />
○实例方法中可以访问成员变量<br />
○实例方法中直接调用实例方法<br />
○实例方法中可以调用类方法(通过类名)<br />
○实例方法是存放在类对象的方法缓存中<br />
使用runtime Associate方法关联的对象,需要在主对象dealloc的时候释放么?<br />
•无论在MRC下还是ARC下均不需要<br />
•被关联的对象在生命周期内要比对象本身释放的晚很多,它们会在被NSObject -dealloc调用的object_dispose()方法中释放<br />
•补充:对象的内存销毁时间表,分四个步骤</p>
<p>1.调用-release:引用计数变为零<br />
*对象正在被销毁,生命周期即将结束.<br />
*不能再有新的__weak弱引用,否则将指向nil.<br />
*调用[selfdealloc]<br />
2.父类调用-dealloc<br />
*继承关系中最直接继承的父类再调用-dealloc<br />
*如果是MRC代码 则会手动释放实例变量们(iVars)<br />
*继承关系中每一层的父类 都再调用-dealloc<br />
3. NSObject调-dealloc<br />
*只做一件事:调用Objective-C runtime中的object_dispose()方法<br />
4.调用object_dispose()<br />
*为C++的实例变量们(iVars)调用destructors<br />
*为ARC状态下的 实例变量们(iVars) 调用-release<br />
*解除所有使用runtime Associate方法关联的对象<br />
*解除所有__weak引用<br />
*调用free()<br />
_objc_msgForward函数是做什么的?直接调用它将会发生什么?<br />
• _objc_msgForward是IMP类型,用于消息转发的:当向一个对象发送一条消息,但它并没有实现的时候,_objc_msgForward会尝试做消息转发<br />
•直接调用_objc_msgForward是非常危险的事,这是把双刃刀,如果用不好会直接导致程序Crash,但是如果用得好,能做很多非常酷的事<br />
• JSPatch就是直接调用_objc_msgForward来实现其核心功能的<br />
•详细解说参见这里的第一个问题解答<br />
能否向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?为什么?<br />
•不能向编译后得到的类中增加实例变量;<br />
•能向运行时创建的类中添加实例变量;<br />
•分析如下:<br />
○因为编译后的类已经注册在runtime中,类结构体中的objc_ivar_list实例变量的链表和instance_size实例变量的内存大小已经确定,同时runtime会调用class_setIvarLayout或class_setWeakIvarLayout来处理strong weak引用,所以不能向存在的类中添加实例变量<br />
○运行时创建的类是可以添加实例变量,调用class_addIvar函数,但是得在调用objc_allocateClassPair之后,objc_registerClassPair之前,原因同上。<br />
runloop和线程有什么关系?<br />
•每条线程都有唯一的一个RunLoop对象与之对应的<br />
•主线程的RunLoop是自动创建并启动<br />
•子线程的RunLoop需要手动创建<br />
•子线程的RunLoop创建步骤如下:<br />
○在子线程中调用[NSRunLoop currentRunLoop]创建RunLoop对象(懒加载,只创建一次)<br />
○获得RunLoop对象后要调用run方法来启动一个运行循环</p>
<p>//启动RunLoop<br />
[[NSRunLoop currentRunLoop] run];<br />
○ RunLoop的其他启动方法</p>
<p>//第一个参数:指定运行模式<br />
//第二个参数:指定RunLoop的过期时间,即:到了这个时间后RunLoop就失效了<br />
[[NSRunLoop currentRunLoop] runMode:kCFRunLoopDefaultMode beforeDate:[NSDate distantFuture]];<br />
runloop的mode作用是什么?<br />
•用来控制一些特殊操作只能在指定模式下运行,一般可以通过指定操作的运行mode来控制执行时机,以提高用户体验<br />
•系统默认注册了5个Mode<br />
○ kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行,对应OC中的:NSDefaultRunLoopMode<br />
○ UITrackingRunLoopMode:界面跟踪 Mode,用于ScrollView追踪触摸滑动,保证界面滑动时不受其他Mode影响<br />
○ kCFRunLoopCommonModes:这是一个标记Mode,不是一种真正的Mode,事件可以运行在所有标有common modes标记的模式中,对应OC中的NSRunLoopCommonModes,带有common modes标记的模式有:UITrackingRunLoopMode和kCFRunLoopDefaultMode<br />
○ UIInitializationRunLoopMode:在启动 App时进入的第一个Mode,启动完成后就不再使用<br />
○ GSEventReceiveRunLoopMode:接受系统事件的内部Mode,通常用不到<br />
以+scheduledTimerWithTimeInterval...的方式触发的timer,在滑动页面上的列表时,timer会暂定回调,为什么?如何解决?<br />
•这里强调一点:在主线程中以+scheduledTimerWithTimeInterval...的方式触发的timer默认是运行在NSDefaultRunLoopMode模式下的,当滑动页面上的列表时,进入了UITrackingRunLoopMode模式,这时候timer就会停止<br />
•可以修改timer的运行模式为NSRunLoopCommonModes,这样定时器就可以一直运行了<br />
•以下是我的笔记补充:<br />
○在子线程中通过scheduledTimerWithTimeInterval:...方法来构建NSTimer<br />
§方法内部已经创建NSTimer对象,并加入到RunLoop中,运行模式为NSDefaultRunLoopMode<br />
§由于Mode有timer对象,所以RunLoop就开始监听定时器事件了,从而开始进入运行循环<br />
§这个方法仅仅是创建RunLoop对象,并不会主动启动RunLoop,需要再调用run方法来启动<br />
○如果在主线程中通过scheduledTimerWithTimeInterval:...方法来构建NSTimer,就不需要主动启动RunLoop对象,因为主线程的RunLoop对象在程序运行起来就已经被启动了</p>
<p>// userInfo参数:用来给NSTimer的userInfo属性赋值,userInfo是只读的,只能在构建NSTimer对象时赋值<br />
[NSTimer scheduledTimerWithTimeInterval:1.0 target:selfselector:@selector(run:) userInfo:@"ya了个hoo"repeats:YES];</p>
<p>// scheduledTimer...方法创建出来NSTimer虽然已经指定了默认模式,但是【允许你修改模式】<br />
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];</p>
<p>//【仅在子线程】需要手动启动RunLoop对象,进入运行循环<br />
[[NSRunLoop currentRunLoop] run];<br />
#猜想runloop内部是如何实现的?<br />
•从字面意思看:运行循环、跑圈;<br />
•本质:内部就是do-while循环,在这个循环内部不断地处理各种事件(任务),比如:Source、Timer、Observer;<br />
•每条线程都有唯一一个RunLoop对象与之对应,主线程的RunLoop默认已经启动,子线程的RunLoop需要手动启动;<br />
•每次RunLoop启动时,只能指定其中一个Mode,这个Mode被称作CurrentMode,如果需要切换Mode,只能退出Loop,再重新指定一个Mode进入,这样做主要是为了隔离不同Mode中的Source、Timer、Observer,让其互不影响;<br />
•附上RunLoop的运行图</p>
<p>不手动指定autoreleasepool的前提下,一个autorealese对象在什么时刻释放?(比如在一个vc的viewDidLoad中创建)<br />
•分两种情况:手动干预释放时机、系统自动去释放<br />
○手动干预释放时机:指定autoreleasepool就是所谓的:当前作用域大括号结束时就立即释放<br />
○系统自动去释放:不手动指定autoreleasepool,Autorelease对象会在当前的runloop迭代结束时释放,下面详细说明释放时机<br />
§ RunLoop中的三个状态会处理自动释放池,通过打印代码发现有两个Observer监听到状态值为:1和160(32+128)<br />
□ kCFRunLoopEntry(1) //第一次进入会创建一个自动释放池<br />
□ kCFRunLoopBeforeWaiting(32) //进入休眠状态前先销毁自动释放池,再创建一个新的自动释放池<br />
□ kCFRunLoopExit(128) //退出RunLoop时销毁最后一次创建的自动释放池<br />
•如果在一个vc的viewDidLoad中创建一个Autorelease对象,那么该对象会在viewDidAppear方法执行前就被销毁了(是这样的吗???)<br />
#苹果是如何实现autoreleasepool的?<br />
• autoreleasepool以一个队列数组的形式实现,主要通过下列三个函数完成.</p>
<p>objc_autoreleasepoolPush<br />
objc_autoreleasepoolPop<br />
objc_aurorelease<br />
•看函数名就可以知道,对autorelease分别执行push,和pop操作。销毁对象时执行release操作<br />
GCD的队列(dispatch_queue_t)分哪两种类型?背后的线程模型是什么样的?<br />
•串行队列<br />
•并行队列<br />
• dispatch_global_queue();是全局并发队列<br />
• dispatch_main_queue();是一种特殊串行队列<br />
•背后的线程模型:自定义队列dispatch_queue_t queue;可以自定义是并行:DISPATCH_QUEUE_CONCURRENT 或者 串行DISPATCH_QUEUE_SERIAL<br />
#苹果为什么要废弃dispatch_get_current_queue?<br />
•容易误用造成死锁<br />
如何用GCD同步若干个异步调用?(如根据若干个url异步加载多张图片,然后在都下载完成后合成一张整图)<br />
•必须是并发队列才起作用<br />
•需求分析<br />
○首先,分别异步执行2个耗时的操作<br />
○其次,等2个异步操作都执行完毕后,再回到主线程执行一些操作<br />
•使用队列组实现上面的需求</p>
<p>//创建队列组<br />
dispatch_group_t group = dispatch_group_create();<br />
//获取全局并发队列<br />
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);<br />
//往队列组中添加耗时操作<br />
dispatch_group_async(group, queue, ^{<br />
//执行耗时的异步操作1<br />
});<br />
//往队列组中添加耗时操作<br />
dispatch_group_async(group, queue, ^{<br />
//执行耗时的异步操作2<br />
});<br />
//当并发队列组中的任务执行完毕后才会执行这里的代码<br />
dispatch_group_notify(group, queue, ^{<br />
//如果这里还有基于上面两个任务的结果继续执行一些代码,建议还是放到子线程中,等代码执行完毕后在回到主线程<br />
//回到主线程<br />
dispatch_async(group, dispatch_get_main_queue(), ^{<br />
//执行相关代码...<br />
});<br />
});<br />
#dispatch_barrier_async的作用是什么?<br />
•函数定义</p>
<p>dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);<br />
•必须是并发队列,要是串行队列,这个函数就没啥意义了<br />
•注意:这个函数的第一个参数queue不能是全局的并发队列<br />
•作用:在它前面的任务执行结束后它才执行,在它后面的任务等它执行完成后才会执<br />
•示例代码</p>
<p>-(void)barrier<br />
{<br />
dispatch_queue_t queue = dispatch_queue_create( "12342234", DISPATCH_QUEUE_CONCURRENT);<br />
dispatch_async(queue, ^{<br />
NSLog( @"----1-----%@", [NSThread currentThread]);<br />
});<br />
dispatch_async(queue, ^{<br />
NSLog( @"----2-----%@", [NSThread currentThread]);<br />
});<br />
//在它前面的任务执行结束后它才执行,在它后面的任务等它执行完成后才会执行<br />
dispatch_barrier_async(queue, ^{<br />
NSLog( @"----barrier-----%@", [NSThread currentThread]);<br />
});<br />
dispatch_async(queue, ^{<br />
NSLog( @"----3-----%@", [NSThread currentThread]);<br />
});<br />
dispatch_async(queue, ^{<br />
NSLog( @"----4-----%@", [NSThread currentThread]);<br />
});<br />
}<br />
#以下代码运行结果如何?</p>
<ul>
<li>(void)viewDidLoad<br />
{<br />
[ super viewDidLoad];<br />
NSLog( @"1");<br />
dispatch_sync(dispatch_get_main_queue(), ^{<br />
NSLog( @"2");<br />
});<br />
NSLog( @"3");<br />
}<br />
•答案:主线程死锁</li>
</ul>
<p>已使用 Microsoft OneNote 2016 创建。</p>
</article>
<div class="comments-wrap">
<div class="share-comments">
<script src="https://utteranc.es/client.js"
repo="Apolla/gtalk"
issue-term="title"
theme="github-dark"
crossorigin="anonymous"
id="github-comment"
async>
</script>
</div>
</div><!-- end comments wrap -->
</div>
<div class="column">
<div class="card">
<header class="card-header">
<p class="card-header-title">
<i class="fa fa-commenting" aria-hidden="true"></i>
<span class="tran-notice">Notice</span>
</p>
</header>
<div class="card-content site-notice">
<div class="content">
</div>
</div>
</div>
<div class="card">
<header class="card-header">
<p class="card-header-title">
<i class="fa fa-folder-open" aria-hidden="true"></i>
<span class="tran-site-categories">Categories</span>
</p>
</header>
<div class="card-content site-categories">
<div class="content">
<ul>
<li><a href="%E7%BB%84%E4%BB%B6%E5%8C%96.html">组件化</a>
</li>
<li><a href="%E7%A2%8E%E7%89%87%E8%8A%9D%E5%A3%AB%E6%94%B6%E8%97%8F.html">碎片芝士收藏</a>
</li>
<li><a href="%E7%9B%B4%E6%92%AD.html">直播</a>
</li>
<li><a href="coreBluetooth.html">coreBluetooth</a>
</li>
<li><a href="%E4%B8%80%E9%98%85%E9%98%85%E8%AF%BB.html">一阅阅读</a>
</li>
<li><a href="SwiftUI.html">SwiftUI</a>
</li>
<li><a href="%E8%91%B5%E8%8A%B1%E5%AE%9D%E5%85%B8.html">葵花宝典</a>
</li>
</ul>
</div>
</div>
</div>
<div class="card">
<header class="card-header">
<p class="card-header-title">
<i class="fa fa-tags" aria-hidden="true"></i>
<span class="tran-site-tags">Tags</span>
</p>
</header>
<div class="card-content site-tags">
<div class="content">
<div class="tags">
</div>
</div>
</div>
</div>
</div>
</div><!-- end columns -->
</div><!-- end container -->
</section>
<footer class="footer">
<div id="plt"></div>
<div class="content has-text-centered">
<p>
Copyright © 2019
<span id="tran-author" class="tran-author">Author: </span><a target="_blank" href="http://apolla.cc">宋明</a>,
<span class="tran-theme">Theme: </span><a target="_blank" href="https://github.com/AlanAlbert/atheme">Atheme</a> (Based on BulmaCSS).
</p>
</div>
</footer>
<script type="text/javascript">
var imgApi = "https://source.unsplash.com/random/1024x";
var imgContainers = document.getElementsByClassName('random-img');
for (var i = 0; i <= imgContainers.length - 1; i++) {
// https://picsum.photos/1024/
var img = document.createElement('img');
img.src = imgApi + (400 + i);
imgContainers[i].appendChild(img);
}
</script>
<script type="text/javascript">
var modelJson = "asset/plt/model.json";
var pluginRootPath = 'asset/plt';
var pluginModelPath = 'asset/plt';
var config = {
pluginRootPath: pluginRootPath,
pluginJsPath: "lib/",
pluginModelPath: pluginModelPath,
tagMode:false,
debug:false,
model: {
jsonPath: modelJson, // xxx.model.json 的路径
},
display: {
width: 325, // canvas的宽度
height: 300, // canvas的高度
position: 'right', // 显示位置:左或右
hOffset: -75, // canvas水平偏移
vOffset: 0, // canvas垂直偏移
},
dialog:{
enable: true
},
mobile: {
show: false, // 是否在移动设备上显示
},
react: {
opacity: 1, // 透明度
},
log: false,
};
L2Dwidget.init(config);
</script>
</body>
</html>