-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
1047 lines (927 loc) · 247 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>2019总结</title>
<url>/2020/02/10/2019%E6%80%BB%E7%BB%93/</url>
<content><![CDATA[<div id="hexo-blog-encrypt" data-wpm="Oh, this is an invalid password. Check and try again, please." data-whm="OOPS, these decrypted content may changed, but you can still have a look.">
<div class="hbe-input-container">
<input type="password" id="hbePass" placeholder="" />
<label for="hbePass">输入密码,查看文章</label>
<div class="bottom-line"></div>
</div>
<script id="hbeData" type="hbeData" data-hmacdigest="581fb813f118dbba4c378dd08724d7094808db6cdb199de25e48019deee5b720">3733c3f25f8df209d9d8c6af07d466716e9b1312631e3a4f91f18355e5c30124abdf6db6f625980c79d1b50a33f64895b7b7382667f14e13186a37a206ef1c4a5f0d6f549ed2e2cd1e22386c99660e8eb8a25d93e0bdcfd1d61a3982b55a889e3972d2d89133de85d9b2585c34cf1bcc86f0d605b4e6a181fb0d16c36f74184802a77a6c9fb1821eea730b061c4c35237ffa72c144e06d74ab40b4b37eac4e0eac5a073b68b541a8185e771eb67bc70e121ce9cecf90473d4e694abeb6cbb79c0af2565c85392e29efb14222caead89f855eb39c6136c08298ce80dd406be2c5c96f5819b4ae589bb8b51587493a07821bdf44ce6f180af6ce48913beeb5f2802a384979c58ef62e86997ff00c74e050da4ca3eb59218df8b9697332a0ab56e16d60080e01dfdb108a8135786921950b80410bd44ebe8c2859bf9c4e06f678403f760b5694a61cffe1dd570ed06deb3b7bd980d92e689312c7b922b22e05e51b521913b2bf6fa34167d89a54c0ac6be3588f0c703eaa202e76bfd74b8c7f5f55fae2a87a2d7ffe73400daceb714772b6b3c1c2d854c441b76bc98f5ca2742ab0337e7010d3d08ba672555382df7d2cbf1c3f57494db46a028e554ff36347b5f701557021647c64eb807531cfe21a1d037abfa3b62531fa7082409b02eb421f994afbafb9dadbcec8d3f89815af21a1bd1292c88fc6c5295db9a7a60b5681a2d46db0a47ad8f1fbf5766716568b508cbb1a3dff81d12b29062273a324b27952f896023af439186ad99f560a5ea396df046dbcef41ec5a923531ef4611fc685350a0456288f47c709cc3400017f95265c050bb2b377e3f621163bbc37ce60475f83663fffffb95c32b441be135d404a05664c77aeac57e77031a0f1acde0f30d82a85c68cf0469aaed3c128ed5d070dde4dbc2525c4a78f1d71f76b2a6c61da6432d669ef1302616e2f1cf1d5cc94abd796fc0efaeab5813d431caa1a4505a05093e86ef0211ed33f13002a3036b84f5b51f9246166d88199599e2be797d357dad0721ce1a9f67438f7e68bcad6d1994772cd2a0777e148a6d1817cefb2c03ad1c2e67a4be4e781008167f688f7a3c8a4582a8b1cff03d50f16a9522c2c5aa15caed226f114cbc07fa3d3273be503e3f991abda763b7a419166cf34a8511003a9de64fca6f33dd2391f832836eb716803b4a0e39cd91e6bd3a6efdffb80a8dd941a164bdf0d78dc8fce6c8f899c9695afb54ec0bcde23d70199eb48855ddc7f70d934be8a97b8d5729e6c559dbcde383aa4df90b629f295f12a34e1f153133df2f771dacffbe7960e1c872da857cffc9d27720c25d9f3c7d66612b9e561b06087d508541c3bddfb77d2be8cac53af014e89d1b0c3506230e458c5cb9d5f15e1d2a5943179201f7ddb9744a38930bba2698896a35b2fca8b81e1d0db40711ebef84b3fa6211bcb627dd85d8be0ad07231739441b1078cc8aede796d373a2c9a6f7bf4aaa17ecbd76ed2dc83a264436a223a37ab7f90e5b67f4203a4cac02a39efa2be0485a98d088eac3b7e28e0f71e914458b1c5f419808dde45588d67482a2399270d8b8c27915b9db378c92b18066205a63ee96e87280cbb90d7a7982fefbc368952c78bb641365f5b232c1fdd465be1426d25beb73fe39fb899827859071722d9c4fcadc7c6fadfb51bb2cb2791c44134c9347da802779eeb0708e2437bf19d4480411a419b7f1c42ccfceaa6ffeb13ecfe35fb8cac80dbea4fabefe72aec3a5157e3e9dc7e986024a3dfcf2affda72c351dfe29246ad8ae2dc9a1fef88c6571fd5a020316d6c7aca7bd4db35670ec4b895ef934675b9db1c921e0553d3e781ad6f293cda2d3f5c7759d86c19f79cd07cbdda7480721cbfd1c58b818b32bc3276eba314ff8ce043778b00985ead200a7570ed2da5b037afbb6ba56fcf4d795fb7ce2b0d2ee607eec519848dcf28db02de973e2e93e6e061fc0c7ec277ccb396a18fe03a19d5fc3b9e750eeaf00d9ba9364c50290ce846a9e30879f5fbce68f4fd0df6a0a257a8c8bf63105702deb5844a2efc155d7afa2cdf07ec2662581c03fb31e0746faf5b42e132cd9592ce5875efa35ffa9d69078ac76967020a0fdb60ae6be5a0df29d1febeadb82ce576985bc6fae474eda20bbf45078097e3fd8f1109a0bd413a6fc64d23e059b7e86fb389c7f2ca4c925cd05bc97943d562f188b6ad1d5e479b4d40391a0ae3298f7dc04c699b7b75dccf9128cc8c25d27eee8d0e9135ab67453ca4a56ed28a8e49f4780c6c44e5076b997105a47071d087a54c03ddc8933c13066376a0c06b1df2f80a5d862cbb47c3f741702d10f2aeaa7f6c9986a807aadf3caee9ed99a4a9889a94c8cd33a1f1ffb94331bfb3355705e4b20098a49b3f44dc4b8d99289eb76930d7657bbd4f0bad6bcbf7d71756847952adaf80aa590235d91b52feb2a02d78d20d941a0bba1aa0815d7b6f679069e4dc654c305d46a80c6bbd52791ff05706b6f8e0e056a42197ca3fe252a0d1035825622d0acc8d5c466ccb8303f0e47a34c70537140cd7aacc1e908e8cc5f872f6061aff0c625c8e22c30c8ae0c2c819e9dbdf7a1904276620b72abc508d9b2e8dcd9d1eed645d9a9a34d71a3edba4fd1e922a49d991c838b2c57f2a4c8209eea5a109e307d9d7a9339918bde6f3c605291d18d03f9cd03dfa175689efa8656a39c6ba9bd7a3c7f1ceb6cc2475a1c2176907442ae648562e029f38254ace658908d12097ea54ef56e248767fa8d28d839df5f86bd138de807db655f2d6b8e10e25acaeb03bcbc9bedf82d78ad81263e0859db2a53e5682d7f285ff0ab52030764f070596478c14dae5416f72b60682a02991a072e7f92932dae30c2788f007b33e16f18a0a2b0c56646fe8c78b17d89699f0818584872cbad5e4b46ab9cd94f8aef7f804e422258584b361f58b0fc52a25c9018dd874768154dc0c5e3a9546bd782d5110c7bc8a89d771c5d6b1eef005ede621ec5873bc0084e709f4406a4d9bc6976ba8e50e4cbd08568a22f72e6b4a51642d8cd395f518448dd45bda69e9e3d14eb74928a40ef1c0bbd71641edd1d39a4760863d49e5ced1e85c9318a9e217dc2be24f07e8d6938c8d381082b7800f6e181757cce73915866bcbf4a281e3ae4d432b868c173fc9ce0e6c99ef79fe7caa7cf5a19b30b23719240314e9513f1fe066446bcce2946c3ed1da4c3cba9de39ef938b8ac49a7d870f700df1988d715688944811a07f48c347b54d60cbdf61c80df4d821e874287cdf641e67a83468f47b6714dbfe3fc075705a23f6b25d4e4f666fc3da16467db09a9908f5ca7d73074d49fd269a8911bae31afb4a6ca9988bb05c0966ab71c3b59be8caf35ed38a50d131c6003084827e9ffc24be68ae9229abd43dd719e78ebe7d90847736bd15bcfecc6bc32ce7c129affe29a85dd55968d154421f6e7090e02834a34c672aa89f6dfec6966de16680e2fcdc1b6816d4c2fd54a3d5a338ef9e55ea5501c09b20296320cf735d7a6f4df6bde3eb99ff020424729eebc16e7bd6465b794bbe034ca53f84d90f5da4541d38d1fb16425ec14c4e6945d975d8e083cc253762bc140998be721c0ac647ddcc8eb7de304651ae823b54f1470912906ad17b362629b0b4f87cdf76f1a30f97e1d294195c3f5725ee101b2532b336edf6efa6c8a5b6c75d2744bdc8e0dfe28803fc292fc365ab43e138c046d779129cbf574b364ee5320c0e9041296c858e7a1bce6a8bfe0746d3db97b347b1c2dc0532d750ff1cc9e4cf62b0290f273aa5ae5fd1c38a156c8b6a27880ed0250e6aced6042b63f0d081752d497613868b4897f0dc348228e09b50d17cc88768453cf53d1499a4203c2934412be459bb5c859b890ab41735960efaeefe9f6e1698e92a1eb597e3126b8f8e6f55f4133e8a95f8fdfe9e4e4096c8453d16e17e086fe4e2b82cb41eb83856e7e32e371e1f2ba0b70eaefcaebd0eb08f67cfc8d677bedfc0b9120379afa634be9887049ba16ad1ef3792b232a1b1b4a971eaf48b4124cead8deaaee7adda41f97b3f2e03f2f74c80b2d4575b01eb82abf1589a8aad3611a3a7ea3f01062de9bb0b4a86821e5ed5038f482d07c4da992a0063bf19eebf797430a46e48e557fd911af9f028969b816131aa9d48f51e0c17f9bb26f2667a4e12a1d9f99c0124915f6907f4a9b7d2137a5bbde977f893bfd44c772f424ff84d2d50f211d247e3389f7601474de4b0d7b9252e2079a3137f0c24fe5e893b4d1e567498ac4d72516545cd070d661668fcf862d3ef9fcde3385640d7efa97dbe5a7e7363f83eb297cce367dc306a67c3ebf3d0c06ebda2846e2da21890197285e45d816914d4e1d7dbc12906f7759f4e100c834f4ab4adc437dad98d2c5cd8b8fb2b4f4f2f54d31c0fe1068f4efa11a8b56d8eed2391b0ecb23f35841268b1e670e024661224b57042c7c194d1476600967fe5f04bb30222cf3f099a26b2edfc9d81d386f05e70c5dda122d0a25d78ef5b2b51461a252e1f4893491e6bc2503ac58f03d1330463bb740bd66bca7a2cbe17311f59234020a9b3b65d4d53f68621a7ceb11139b078908d36a82a45c0a8d069f493da3fbc68ea799e1bc094f699277ea751a5170038f6e6834fb64a4926e3ed20f93e7d5c355ee70e0c0100d25bd91d32f5f5ac0540e6202917682545937487b04e909fa4d60ae7ae301a516994d741deaaf9a0841dbd0ac4260958a12bc76b9b0f4a9cca25f95ee8356cc9d7cf34abed7d9a7954a83412ae2d57450d0b04eba2088cb7839c2e389ac983d64a14f96e14864f4fe2d586d0fea21cea8ae9b2ac9c2bb67edf033686871372134c0fa6b548677f2642e5662f902c96b34518f7327585c7455a585b0427702f1a092fcf00b5ae1475983538a5feb915705d9671383ad23b620da1cbdf966e33c3aa2f6aa96530cb20bd61180f1523c412ae0dd934683a8c37fc54e9c229be5ae5296e830409242ddc7201c76e6556058adb23dc9910f9e2ca1a5a00555bc33afb1cc9f0dad2a9771b31d3e579e861fd5bbb2bac72cd8675cadd7ab9c3b5fa046cfd5</script>
</div>
<script src="/lib/blog-encrypt.js"></script><link href="/css/blog-encrypt.css" rel="stylesheet" type="text/css">]]></content>
<categories>
<category>散文</category>
</categories>
<tags>
<tag>散文</tag>
</tags>
</entry>
<entry>
<title>MacOS分享全局代理给局域网其他设备</title>
<url>/2020/01/31/MacOS%E5%88%86%E4%BA%AB%E5%85%A8%E5%B1%80%E4%BB%A3%E7%90%86%E7%BB%99%E5%B1%80%E5%9F%9F%E7%BD%91%E5%85%B6%E4%BB%96%E8%AE%BE%E5%A4%87/</url>
<content><![CDATA[<h1 id="为什么要这么做"><a href="#为什么要这么做" class="headerlink" title="为什么要这么做"></a>为什么要这么做</h1><p>在出租屋里的路由器配置了SSR,所有设备科学上网已经很方便了。回到老家后,出现了一些需要科学上网的场景却又很难实现,比如玩switch游戏时需要科学上网,懵逼了。于是想到了自己的MBP有SSR,能开启全局代理然后分享出来吗?</p>
<h1 id="正题"><a href="#正题" class="headerlink" title="正题"></a>正题</h1><h2 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h2><p>这个方法实现了的效果是,同一局域网内的设备使用MBP上的全局代理,这里不说SSR怎么配置。</p>
<p>下载安装<a href="http://www.privoxy.org/sf-download-mirror/" target="_blank" rel="noopener">privoxy</a>,注意对应平台。根据环境,我使用的是<code>Privoxy 3.0.26 64 bit.pkg</code>这个包。</p>
<p>下载安装完毕后,修改一些需要自定义的配置,打开<code>/usr/local/etc/privoxy</code>目录下的<code>config</code>文件,搜索<code>forward-socks5t</code>,并将端口号改为自己SSR里配置的端口(下面的1086是笔者使用的端口)</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line"># Examples:</span><br><span class="line">#</span><br><span class="line"># From the company example.com, direct connections are made to</span><br><span class="line"># all "internal" domains, but everything outbound goes through</span><br><span class="line"># their ISP's proxy by way of example.com's corporate SOCKS 4A</span><br><span class="line"># gateway to the Internet.</span><br><span class="line">#</span><br><span class="line"># forward-socks4a / socks-gw.example.com:1080 www-cache.isp.example.net:8080</span><br><span class="line"># forward .example.com .</span><br><span class="line">#</span><br><span class="line"># A rule that uses a SOCKS 4 gateway for all destinations but no</span><br><span class="line"># HTTP parent looks like this:</span><br><span class="line">#</span><br><span class="line"># forward-socks4 / socks-gw.example.com:1080 .</span><br><span class="line">#</span><br><span class="line"># To chain Privoxy and Tor, both running on the same system, you</span><br><span class="line"># would use something like:</span><br><span class="line">#</span><br><span class="line"> forward-socks5t / 127.0.0.1:1086 .</span><br><span class="line">#</span><br><span class="line"># Note that if you got Tor through one of the bundles, you may</span><br><span class="line"># have to change the port from 9050 to 9150 (or even another</span><br><span class="line"># one). For details, please check the documentation on the Tor</span><br><span class="line"># website.</span><br></pre></td></tr></table></figure>
<p>继续搜索<code>listen-address</code>,将<code>127.0.0.1</code>修改为<code>0.0.0.0</code>,端口任意修改为未占用的(笔者使用的是2134)</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line"># Example:</span><br><span class="line">#</span><br><span class="line"># Suppose you are running Privoxy on a machine which has the</span><br><span class="line"># address 192.168.0.1 on your local private network</span><br><span class="line"># (192.168.0.0) and has another outside connection with a</span><br><span class="line"># different address. You want it to serve requests from inside</span><br><span class="line"># only:</span><br><span class="line">#</span><br><span class="line"># listen-address 192.168.0.1:8118</span><br><span class="line">#</span><br><span class="line"># Suppose you are running Privoxy on an IPv6-capable machine and</span><br><span class="line"># you want it to listen on the IPv6 address of the loopback</span><br><span class="line"># device:</span><br><span class="line">#</span><br><span class="line"># listen-address [::1]:8118</span><br><span class="line">#</span><br><span class="line">listen-address 0.0.0.0:2134</span><br><span class="line">#</span><br></pre></td></tr></table></figure>
<h2 id="开启服务"><a href="#开启服务" class="headerlink" title="开启服务"></a>开启服务</h2><figure class="highlight jboss-cli"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 进入 Privoxy 开启关闭脚本的文件</span></span><br><span class="line"><span class="keyword">cd</span> <span class="string">/Applications/Privoxy</span></span><br><span class="line"><span class="comment"># 开启 Privoxy:</span></span><br><span class="line">sudo <span class="string">./startPrivoxy.sh</span></span><br><span class="line"><span class="comment"># 关闭 Privoxy:</span></span><br><span class="line">sudo <span class="string">./stopPrivoxy.sh</span></span><br></pre></td></tr></table></figure>
<h2 id="设备共享"><a href="#设备共享" class="headerlink" title="设备共享"></a>设备共享</h2><p>将需要共享全局代理的设备连入与MBP同一网络,然后修改无线局域网配置。将MBP的IP地址和<code>listen-address</code>配置的端口填入HTTP代理中</p>
<div align=center> <img src=https://kinkenyuen.oss-cn-shenzhen.aliyuncs.com/images/设置代理.jpg width=50% /> <br> <div style="color:orange; border-bottom: 1px solid #d9d9d9; display: inline-block; color: #999; padding: 2px;">设置代理</div> </div>
<div align=center> <img src=https://kinkenyuen.oss-cn-shenzhen.aliyuncs.com/images/使用代理.jpg width=50% /> <br> <div style="color:orange; border-bottom: 1px solid #d9d9d9; display: inline-block; color: #999; padding: 2px;">使用代理</div> </div>
]]></content>
<categories>
<category>MacOS</category>
</categories>
<tags>
<tag>MacOS</tag>
</tags>
</entry>
<entry>
<title>进程与线程</title>
<url>/2020/01/21/%E8%BF%9B%E7%A8%8B%E4%B8%8E%E7%BA%BF%E7%A8%8B/</url>
<content><![CDATA[<h1 id="进程和线程"><a href="#进程和线程" class="headerlink" title="进程和线程"></a>进程和线程</h1><p>进程和线程在操作系统中是比较重要的内容,面试中的基础提问也经常会出现,如进程和线程的区别等等,如果没有仔细梳理,还真的很难说清楚。本文仅列出进程和线程的基本内容及区别。</p>
<p>在一些操作系统的书籍中,通常会介绍到<strong>多任务系统</strong>,现代的操作系统基本都是支持 “多任务”,简单地说,多任务指的是操作系统可以同时运行多个任务。比如我现在一边在<code>MacDown</code>写东西,一边听音乐,一边在用浏览器看文章。</p>
<p>现在,多核CPU非常普及,在这之前使用的单核CPU,也是可以执行多任务,由于CPU执行代码都是顺序执行,那么单核CPU如何执行多任务?答案是操作系统按一定时间分配CPU给任务1、任务2…,任务之间交替使用CPU执行,由于CPU的执行速度快且任务之间的切换时间间隔很短,因此就有多个任务同时执行一样。</p>
<p>在多核CPU上能够实现真正的并行执行多任务,操作系统自动把多个任务轮流调度到CPU的每个核心上执行。</p>
<p>对操作系统而言,一个任务就是一个进程(Process)。进程有可能不止同时做一件事,比如音乐播放器可以一边播放音乐,一边搜索歌曲。在进程内部同时执行的多个“子任务”,它们是在多个线程(Thread)上执行。</p>
<p>所以,进程至少有一个线程。同样地,在单核CPU中,多个线程之间可以快速切换实现多线程并发;在多核CPU中,多个线程在不同核心上执行,实现了多线程并行。</p>
<table>
<thead>
<tr>
<th>CPU</th>
<th>进程</th>
<th>线程</th>
</tr>
</thead>
<tbody><tr>
<td>单核</td>
<td>多进程并发,多个进程时间片轮换</td>
<td>多线程并发,同一进程内的线程轮换</td>
</tr>
<tr>
<td>多核</td>
<td>并行,不同进程的线程使用不同核心</td>
<td>并行,多个线程使用不同的核心</td>
</tr>
</tbody></table>
<h1 id="进程"><a href="#进程" class="headerlink" title="进程"></a>进程</h1><p>程序本身只是指令、数据及其组织形式的描述,进程才是程序的真正运行实例。在分时系统中,进程是基本的运作单位,但是在当代多数面向线程设计的系统中,进程本身不是基本运行单位,而是线程的容器。进程内包含一个或多个线程(每一个线程都代表一个进程内的一个独立执行上下文)</p>
<h2 id="进程内容"><a href="#进程内容" class="headerlink" title="进程内容"></a>进程内容</h2><p>一个进程可以包括下列数据:</p>
<ul>
<li>程序可执行文件二进制在内存中的映像</li>
<li>分配到的内存(通常是虚拟内存)。虚拟内存中包括可执行代码、调用堆栈、堆栈</li>
<li>分配给该进程的资源的操作系统描述符,诸如文件描述符(Unix术语)或文件句柄(Windows)、数据源和数据终端。</li>
<li>安全特性,诸如进程拥有者和进程的权限集(可以容许的操作)。</li>
<li>处理器状态(内文),诸如寄存器内容、物理存储器定址等。当进程正在运行时,状态通常存储在寄存器,其他情况在存储器。</li>
</ul>
<h2 id="进程状态"><a href="#进程状态" class="headerlink" title="进程状态"></a>进程状态</h2><ul>
<li>新生(new):进程新产生中。</li>
<li>运行(running):正在运行。</li>
<li>等待(waiting):等待某事发生,例如等待用户输入完成。亦称“阻塞”(blocked)</li>
<li>就绪(ready):排队中,等待CPU。</li>
<li>结束(terminated):完成运行。</li>
</ul>
<p>各状态名称可能随不同操作系统而相异;对于单CPU系统(UP),任何时间可能有多个进程为等待、就绪,但必定仅有一个进程在运行。</p>
<p>注意: 进程的各个状态之间是不能随意切换的,例如当进程运行时因IO操作而阻塞,当IO操作完成后并不会直接恢复回运行态,而是转为就绪态等待CPU的调度。</p>
<h1 id="线程"><a href="#线程" class="headerlink" title="线程"></a>线程</h1><p>大部分情况下,线程被包含在进程之中,是进程中的实际运行单位。一条线程是指进程中一个单一顺序的指令流。在多核CPU中,一个进程中可以并发多个线程,每条线程并行执行不同任务。<strong>同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。但同一进程中的多个线程有各自的调用栈(call stack),自己的寄存器环境(register context),自己的线程本地存储(thread-local storage)</strong>。</p>
<h2 id="线程状态"><a href="#线程状态" class="headerlink" title="线程状态"></a>线程状态</h2><ul>
<li>产生(spawn)</li>
<li>中断(block)</li>
<li>非中断(unblock)</li>
<li>结束(finish)</li>
</ul>
<h1 id="联系与区别"><a href="#联系与区别" class="headerlink" title="联系与区别"></a>联系与区别</h1><ul>
<li>进程比线程携带更多状态信息,而一个进程中的多个线程共享进程状态和其他资源</li>
<li>进程有独立的地址空间,而线程共享进程的地址空间</li>
<li>进程通过系统提供的进程间通信机制进行交互</li>
<li>进程中的线程间上下文切换通常比进程之间上下文切换要快</li>
</ul>
<h1 id="相关文章"><a href="#相关文章" class="headerlink" title="相关文章"></a>相关文章</h1><p><a href="https://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html" target="_blank" rel="noopener">进程与线程的一个简单解释</a></p>
<p><a href="http://www.qnx.com/developers/docs/6.4.1/neutrino/getting_started/s1_procs.html" target="_blank" rel="noopener">Processes and Threads</a></p>
]]></content>
<categories>
<category>操作系统</category>
</categories>
<tags>
<tag>操作系统</tag>
</tags>
</entry>
<entry>
<title>内存分配</title>
<url>/2020/01/21/%E5%86%85%E5%AD%98%E5%88%86%E9%85%8D/</url>
<content><![CDATA[<h1 id="内存分配"><a href="#内存分配" class="headerlink" title="内存分配"></a>内存分配</h1><p>在早期的计算机中,程序是直接运行在物理内存上,也就是说,程序运行时所访问的地址都是物理地址,如果计算机只运行一个程序且该程序所需的内存空间不超过物理内存大小,就不会有问题。现在计算机需要同时运行多个程序,那么如何将有限的物理内存分配给多个程序使用?</p>
<h1 id="直接分配的弊端"><a href="#直接分配的弊端" class="headerlink" title="直接分配的弊端"></a>直接分配的弊端</h1><p>假设一台计算机有64MB内存,程序A运行需要50MB,程序运行需要10MB,需要同时运行这两个程序,比较直接的做法就是将0MB~50MB分配给A,50MB~60MB分配给B。</p>
<p>这样的分配策略会带来很多问题:</p>
<ul>
<li><strong>地址空间不隔离</strong> 所有程序都直接访问物理地址,程序之间使用的地址空间共享物理内存,很容易发生恶意程序改写其他程序内存数据的情况;另外本身有bug的程序也有可能影响到其他程序的执行。这造成了程序运行不稳定的情况。</li>
<li><strong>程序运行时地址不确定</strong> 在程序装入运行时,需要分配一块足够大的空闲区域,而这个位置不确定,那么在程序编写时,指令的跳转需要你自己计算得出绝对地址,这是十分麻烦的。</li>
<li><strong>内存使用效率低</strong> 执行一个程序就将整个程序加载到内存,若需要继续同时执行另外的程序,则会出现内存不足,这时只能将内存中现有的数据换出到磁盘,磁盘、内存之间的大容量的换出换入必会导致效率低下</li>
</ul>
<h1 id="如何解决直接分配的弊端"><a href="#如何解决直接分配的弊端" class="headerlink" title="如何解决直接分配的弊端"></a>如何解决直接分配的弊端</h1><h2 id="解决地址空间不隔离和程序运行时地址不确定"><a href="#解决地址空间不隔离和程序运行时地址不确定" class="headerlink" title="解决地址空间不隔离和程序运行时地址不确定"></a>解决地址空间不隔离和程序运行时地址不确定</h2><p>从程序执行的角度看,我们不希望它介入到复杂得内存分配过程中,我们希望一个程序在执行的时候只需要一个简单得执行环境(独立单一的地址空间、单一的CPU,不用关心其他程序)。</p>
<p>可以把地址空间想象成一个很大的数组,数组大小取决于地址空间的地址长度,如64位的地址空间为<code>2^64 = 18446744073709551616</code>,一般用十六进制表示<code>0x0000000000000000~0xFFFFFFFFFFFFFFFF</code></p>
<p>地址空间分为<strong>物理地址空间</strong>和<strong>虚拟地址空间</strong>。</p>
<p>可以把物理地址空间想象成物理内存,它是实实在在存在,存在于计算机中。物理地址空间范围与设备的内存大小相关。</p>
<p>虚拟地址是指人们想象出来的并不存在的,每个进程都有自己独立的虚拟空间,且每个进程只能访问自己的地址空间。</p>
<p>使用虚拟地址空间和分段解决地址空间不隔离和程序运行时地址不确定</p>
<div align=center> <img src="https://kinkenyuen.oss-cn-shenzhen.aliyuncs.com/images/虚拟内存分段映射.png" width="800" /> <div style="color:orange; border-bottom: 1px solid #d9d9d9; display: inline-block; color: #999; padding: 2px;">虚拟内存分段映射</div> </div>
<p>虚拟地址和物理地址通过映射函数来转换,通常由CPU转换,当程序访问超出虚拟地址空间的地址时,硬件会判断出非法访问并拒绝访问。由此,编写程序只需要在虚拟地址空间内即可(实质上是程序编写完才有一个虚拟地址空间)。</p>
<p>内存分段没有解决内存使用效率问题,因为换出数据到磁盘仍需要整页换出,为了解决这个问题,使用更细粒度的划分——分页</p>
<h2 id="使用分页解决内存使用效率低"><a href="#使用分页解决内存使用效率低" class="headerlink" title="使用分页解决内存使用效率低"></a>使用分页解决内存使用效率低</h2><p>根据程序的局部性原理,当一个程序执行时,并不是所有数据都需要在一个时刻使用,因此可以按需将内容装载到内存中。</p>
<p>目前大部分操作系统都使用4KB大小的页。按照这个页大小将需要执行的程序虚拟内存空间分成多页,并把需要使用的页映射到内存,这就能使多个程序的分页同时装载到物理内存中,提高了内存使用效率。当程序执行到不在物理内存的分页时,就会出现<strong>页错误(Page Fault)</strong>,然后操作系统将进程需要的页装载映射到物理内存。</p>
<p>页保护,每个页可以设置权限属性,谁可以修改、访问等,只有<strong>操作系统</strong>有权限修改这些属性。</p>
]]></content>
<categories>
<category>操作系统</category>
</categories>
<tags>
<tag>操作系统</tag>
</tags>
</entry>
<entry>
<title>探索autoreleasepool</title>
<url>/2020/01/17/%E6%8E%A2%E7%B4%A2autoreleasepool/</url>
<content><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>本文纯属是根据前人对<code>autoreleasepool</code>的分析学习和苹果文档、源码的一次学习笔记,内容大部分来自引用。本人所做的工作仅是按照前人的笔记手动实践一遍梳理原理并记录供日后方便回顾。</p>
<p>通常来说,研究<code>Objective-C</code>必备的源码</p>
<p><a href="https://opensource.apple.com/source/objc4/" target="_blank" rel="noopener">objc4</a></p>
<h1 id="引出autoreleasepool"><a href="#引出autoreleasepool" class="headerlink" title="引出autoreleasepool"></a>引出autoreleasepool</h1><p>iOS应用程序在默认创建时,<code>main</code>函数的内容都有一个<code>autorelease</code>块包裹函数体</p>
<figure class="highlight objc"><table><tr><td class="code"><pre><span class="line"><span class="keyword">int</span> main(<span class="keyword">int</span> argc, <span class="keyword">char</span> * argv[]) {</span><br><span class="line"> <span class="keyword">@autoreleasepool</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">UIApplicationMain</span>(argc, argv, <span class="literal">nil</span>, <span class="built_in">NSStringFromClass</span>([AppDelegate <span class="keyword">class</span>]));</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>从iOS内存管理的内容可以得知,这个自动释放池块对应着主线程,伴随着整个应用程序生命周期,当我们手动退出应用程序,整个自动释放池内的对象都将被释放,因此不会出现内存泄漏。</p>
<p>另外,从<code>objc4</code>源码对<code>autorelease pool</code>实现中的注释中可以获得一些相关信息</p>
<figure class="highlight dart"><table><tr><td class="code"><pre><span class="line"><span class="comment"><span class="markdown">/<span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span>*</span></span></span><br><span class="line"><span class="comment"><span class="markdown"> Autorelease pool implementation</span></span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"><span class="markdown"> A thread's autorelease pool is </span>a<span class="markdown"> stack of pointers. </span></span></span><br><span class="line"><span class="comment"><span class="markdown"> Each pointer is either </span>an<span class="markdown"> object to release, or POOL_BOUNDARY which is </span></span></span><br><span class="line"><span class="comment"><span class="markdown"><span class="code"> </span></span>an<span class="markdown"> autorelease pool boundary.</span></span></span><br><span class="line"><span class="comment"><span class="markdown"> A pool token is </span>a<span class="markdown"> pointer to </span>the<span class="markdown"> POOL_BOUNDARY for that pool. When </span></span></span><br><span class="line"><span class="comment"><span class="markdown"><span class="code"> </span></span>the<span class="markdown"> pool is popped, every object hotter than </span>the<span class="markdown"> sentinel is released.</span></span></span><br><span class="line"><span class="comment"><span class="markdown"> The stack is divided into </span>a<span class="markdown"> doubly-linked list of pages. Pages </span>are<span class="markdown"> added </span></span></span><br><span class="line"><span class="comment"><span class="markdown"><span class="code"> and deleted as necessary. </span></span></span></span><br><span class="line"><span class="comment"><span class="markdown"> Thread-local storage points to </span>the<span class="markdown"> hot page, where newly autoreleased </span></span></span><br><span class="line"><span class="comment"><span class="markdown"><span class="code"> objects </span></span>are<span class="markdown"> stored. </span></span></span><br><span class="line"><span class="comment"><span class="markdown"><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span><span class="strong">*****</span>/</span></span></span><br></pre></td></tr></table></figure>
<p>以下是我生硬的翻译</p>
<figure class="highlight sas"><table><tr><td class="code"><pre><span class="line">一个线程的自动释放池是一个保存很多指针的栈。</span><br><span class="line">每一个指针指向的要么是需要释放的对象,要么是自动释放池的边界POOL_BOUNDARY</span><br><span class="line">自动释放池的token指向该池的POOL_BOUNDARY,当自动释放池销毁时,所有比POOL_BOUNDARY(边界对象)高的对象都会被released</span><br><span class="line">自动释放池栈由双向链表构成,链表结点是一个<span class="meta">page</span>(对应下文的AutoreleasePoolPage),<span class="meta">page</span>可以按需添加或删除</span><br><span class="line">线程本地存储指向hotpage,所谓hot <span class="meta">page</span>是指最近有autoreleased对象被存储进来的<span class="meta">page</span></span><br></pre></td></tr></table></figure>
<h1 id="研究autoreleasepool"><a href="#研究autoreleasepool" class="headerlink" title="研究autoreleasepool"></a>研究autoreleasepool</h1><p>从顶层代码往下研究,逐步追溯到原理实现层。</p>
<p>使用命令用编译器将<code>main.m</code>转换为底层处理代码</p>
<figure class="highlight sh"><table><tr><td class="code"><pre><span class="line">clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.2.sdk main.m</span><br></pre></td></tr></table></figure>
<p>查看生成的<code>main.cpp</code>,得到以下关键代码</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> __<span class="title">AtAutoreleasePool</span> {</span></span><br><span class="line"> __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}</span><br><span class="line"> ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}</span><br><span class="line"> <span class="keyword">void</span> * atautoreleasepoolobj;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span> * argv[])</span> </span>{</span><br><span class="line"> <span class="comment">/* @autoreleasepool */</span> { __AtAutoreleasePool __autoreleasepool; </span><br><span class="line"> <span class="keyword">return</span> UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(<span class="keyword">void</span> *)objc_msgSend)((id)objc_getClass(<span class="string">"AppDelegate"</span>), sel_registerName(<span class="string">"class"</span>))));</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>从中可以得到的信息是,<code>@autoreleasepool</code>转换成了一个C++结构体实例,而该结构体为<code>__AtAutoreleasePool</code></p>
<p><code>__AtAutoreleasePool</code>结构体中分别有构造函数<code>__AtAutoreleasePool ()</code>和析构函数<code>~__AtAutoreleasePool()</code>以及<code>atautoreleasepoolobj</code>成员变量</p>
<p>结合转换代码,根据局部变量超出作用域的规则,可手动将<code>main</code>函数改写成以下形式</p>
<figure class="highlight objc"><table><tr><td class="code"><pre><span class="line"><span class="keyword">int</span> main(<span class="keyword">int</span> argc, <span class="keyword">char</span> * argv[]) {</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">void</span> * atautoreleasepoolobj = objc_autoreleasePoolPush();</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">UIApplicationMain</span>(argc, argv, <span class="literal">nil</span>, <span class="built_in">NSStringFromClass</span>([AppDelegate <span class="keyword">class</span>]));</span><br><span class="line"> </span><br><span class="line"> objc_autoreleasePoolPop(atautoreleasepoolobj);</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>得到以上代码后,我们就可以顺藤摸瓜,沿着<code>objc_autoreleasePoolPush()</code>以及<code>objc_autoreleasePoolPop()</code>继续深入</p>
<p>由于是<code>objc</code>前缀的函数,我们可以想到从<code>objc4</code>的源码中搜索,搜索发现如下</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> * <span class="title">objc_autoreleasePoolPush</span><span class="params">(<span class="keyword">void</span>)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> AutoreleasePoolPage::push();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">objc_autoreleasePoolPop</span><span class="params">(<span class="keyword">void</span> *ctxt)</span> </span>{</span><br><span class="line"> AutoreleasePoolPage::pop(ctxt);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>同样在源码中搜索可以发现<code>push()</code>和<code>pop()</code>都是C++类<code>AutoreleasePoolPage</code>内的静态方法,接下来分析该类及其两个函数</p>
<h2 id="AutoreleasePoolPage"><a href="#AutoreleasePoolPage" class="headerlink" title="AutoreleasePoolPage"></a>AutoreleasePoolPage</h2><p>根据源码中<code>autoreleasepool</code>实现的注释可知,每一个自动释放池由结点为<code>AutoreleasePoolPage</code>的双向链表组成,在源码中得到<code>AutoreleasePoolPage</code>类的定义</p>
<figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">AutoreleasePoolPage</span> {</span></span><br><span class="line"> <span class="comment">// EMPTY_POOL_PLACEHOLDER is stored in TLS when exactly one pool is </span></span><br><span class="line"> <span class="comment">// pushed and it has never contained any objects. This saves memory </span></span><br><span class="line"> <span class="comment">// when the top level (i.e. libdispatch) pushes and pops pools but </span></span><br><span class="line"> <span class="comment">// never uses them.</span></span><br><span class="line"><span class="meta"># <span class="meta-keyword">define</span> EMPTY_POOL_PLACEHOLDER ((id*)1)</span></span><br><span class="line"></span><br><span class="line"><span class="meta"># <span class="meta-keyword">define</span> POOL_BOUNDARY nil</span></span><br><span class="line"> <span class="keyword">static</span> <span class="keyword">pthread_key_t</span> <span class="keyword">const</span> key = AUTORELEASE_POOL_KEY;</span><br><span class="line"> <span class="keyword">static</span> <span class="keyword">uint8_t</span> <span class="keyword">const</span> SCRIBBLE = <span class="number">0xA3</span>; <span class="comment">// 0xA3A3A3A3 after releasing</span></span><br><span class="line"> <span class="keyword">static</span> <span class="keyword">size_t</span> <span class="keyword">const</span> SIZE = </span><br><span class="line">#<span class="keyword">if</span> PROTECT_AUTORELEASEPOOL</span><br><span class="line"> PAGE_MAX_SIZE; <span class="comment">// must be multiple of vm page size</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">else</span></span></span><br><span class="line"> PAGE_MAX_SIZE; <span class="comment">// size and alignment, power of 2</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"> <span class="keyword">static</span> <span class="keyword">size_t</span> <span class="keyword">const</span> COUNT = SIZE / <span class="keyword">sizeof</span>(id);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">magic_t</span> <span class="keyword">const</span> magic;</span><br><span class="line"> id *next;</span><br><span class="line"> <span class="keyword">pthread_t</span> <span class="keyword">const</span> thread;</span><br><span class="line"> AutoreleasePoolPage * <span class="keyword">const</span> parent;</span><br><span class="line"> AutoreleasePoolPage *child;</span><br><span class="line"> <span class="keyword">uint32_t</span> <span class="keyword">const</span> depth;</span><br><span class="line"> <span class="keyword">uint32_t</span> hiwat;</span><br><span class="line"></span><br><span class="line"> ...</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<ul>
<li>PAGE_MAX_SIZE page的大小以及对齐,2的幂 (此处为4096字节,也就是4KB,虚拟内存的一页)</li>
<li>magic 用于对当前AutoreleasePoolPage完整性的校验</li>
<li>thread 保存当前page所在的线程</li>
<li>parent、child 双向链表使用的指针</li>
</ul>
<hr>
<p>虚拟化一个page,它的结构如下</p>
<div align=center> <img src="https://kinkenyuen.oss-cn-shenzhen.aliyuncs.com/images/page结构.png" width="800" /> <div style="color:orange; border-bottom: 1px solid #d9d9d9; display: inline-block; color: #999; padding: 2px;">page结构</div> </div>
<p>接下来是对该结构的一些解释</p>
<figure class="highlight c++"><table><tr><td class="code"><pre><span class="line">AutoreleasePoolPage(AutoreleasePoolPage *newParent) </span><br><span class="line"> : magic(), next(<span class="built_in">begin</span>()), thread(pthread_self()),</span><br><span class="line"> parent(newParent), child(nil), </span><br><span class="line"> depth(parent ? <span class="number">1</span>+parent->depth : <span class="number">0</span>), </span><br><span class="line"> hiwat(parent ? parent->hiwat : <span class="number">0</span>)</span><br><span class="line">{ </span><br><span class="line"> <span class="keyword">if</span> (parent) {</span><br><span class="line"> parent->check();</span><br><span class="line"> assert(!parent->child);</span><br><span class="line"> parent->unprotect();</span><br><span class="line"> parent->child = <span class="keyword">this</span>;</span><br><span class="line"> parent->protect();</span><br><span class="line"> }</span><br><span class="line"> protect();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function">id * <span class="title">begin</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> (id *) ((<span class="keyword">uint8_t</span> *)<span class="keyword">this</span>+<span class="keyword">sizeof</span>(*<span class="keyword">this</span>));</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function">id * <span class="title">end</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> (id *) ((<span class="keyword">uint8_t</span> *)<span class="keyword">this</span>+SIZE);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>根据类实例的构造函数和<code>begin()</code>、<code>end()</code>实例方法,<code>next</code>指针指向类实例所占内存的下一位置。</p>
<p>可以发现前人的文章提到了一个<strong>哨兵对象</strong>,但在最新版的<code>objc4</code>里已经找不到,取而代之的是<br><code>POOL_BOUNDARY</code>,这里我暂且称之为<strong>边界对象</strong>,同样地,它是个<code>nil</code>的别名。</p>
<p>这个<code>POOL_BOUNDARY</code>在<code>page</code>压入第一个对象指针时(也是<code>page</code>初始化的时候)被压入,并且返回这个<strong>边界对象</strong>。</p>
<figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">inline</span> <span class="keyword">void</span> *<span class="title">push</span><span class="params">()</span> </span>{</span><br><span class="line"> id *dest;</span><br><span class="line"> <span class="keyword">if</span> (DebugPoolAllocation) {</span><br><span class="line"> <span class="comment">// Each autorelease pool starts on a new pool page.</span></span><br><span class="line"> dest = autoreleaseNewPage(POOL_BOUNDARY);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> dest = autoreleaseFast(POOL_BOUNDARY);</span><br><span class="line"> }</span><br><span class="line"> assert(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);</span><br><span class="line"> <span class="keyword">return</span> dest;</span><br><span class="line">}</span><br><span class="line"> </span><br><span class="line"><span class="keyword">static</span> __attribute__((noinline)) <span class="function">id *<span class="title">autoreleaseNewPage</span><span class="params">(id obj)</span> </span>{</span><br><span class="line"> AutoreleasePoolPage *page = hotPage();</span><br><span class="line"> <span class="keyword">if</span> (page) <span class="keyword">return</span> autoreleaseFullPage(obj, page);</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">return</span> autoreleaseNoPage(obj);</span><br><span class="line">}</span><br><span class="line"> </span><br><span class="line"><span class="keyword">static</span> __attribute__((noinline)) <span class="function">id *<span class="title">autoreleaseNoPage</span><span class="params">(id obj)</span> </span>{</span><br><span class="line"> <span class="comment">// "No page" could mean no pool has been pushed</span></span><br><span class="line"> <span class="comment">// or an empty placeholder pool has been pushed and has no contents yet</span></span><br><span class="line"> assert(!hotPage());</span><br><span class="line"></span><br><span class="line"> <span class="keyword">bool</span> pushExtraBoundary = <span class="literal">false</span>;</span><br><span class="line"> <span class="keyword">if</span> (haveEmptyPoolPlaceholder()) {</span><br><span class="line"> <span class="comment">// We are pushing a second pool over the empty placeholder pool</span></span><br><span class="line"> <span class="comment">// or pushing the first object into the empty placeholder pool.</span></span><br><span class="line"> <span class="comment">// Before doing that, push a pool boundary on behalf of the pool </span></span><br><span class="line"> <span class="comment">// that is currently represented by the empty placeholder.</span></span><br><span class="line"> pushExtraBoundary = <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (obj != POOL_BOUNDARY && DebugMissingPools) {</span><br><span class="line"> <span class="comment">// We are pushing an object with no pool in place, </span></span><br><span class="line"> <span class="comment">// and no-pool debugging was requested by environment.</span></span><br><span class="line"> _objc_inform(<span class="string">"MISSING POOLS: (%p) Object %p of class %s "</span></span><br><span class="line"> <span class="string">"autoreleased with no pool in place - "</span></span><br><span class="line"> <span class="string">"just leaking - break on "</span></span><br><span class="line"> <span class="string">"objc_autoreleaseNoPool() to debug"</span>, </span><br><span class="line"> pthread_self(), (<span class="keyword">void</span>*)obj, object_getClassName(obj));</span><br><span class="line"> objc_autoreleaseNoPool(obj);</span><br><span class="line"> <span class="keyword">return</span> nil;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (obj == POOL_BOUNDARY && !DebugPoolAllocation) {</span><br><span class="line"> <span class="comment">// We are pushing a pool with no pool in place,</span></span><br><span class="line"> <span class="comment">// and alloc-per-pool debugging was not requested.</span></span><br><span class="line"> <span class="comment">// Install and return the empty pool placeholder.</span></span><br><span class="line"> <span class="keyword">return</span> setEmptyPoolPlaceholder();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// We are pushing an object or a non-placeholder'd pool.</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// Install the first page.</span></span><br><span class="line"> AutoreleasePoolPage *page = <span class="keyword">new</span> AutoreleasePoolPage(nil);</span><br><span class="line"> setHotPage(page);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// Push a boundary on behalf of the previously-placeholder'd pool.</span></span><br><span class="line"> <span class="keyword">if</span> (pushExtraBoundary) {</span><br><span class="line"> page->add(POOL_BOUNDARY);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// Push the requested object or pool.</span></span><br><span class="line"> <span class="keyword">return</span> page->add(obj);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function">id *<span class="title">add</span><span class="params">(id obj)</span> </span>{</span><br><span class="line"> assert(!full());</span><br><span class="line"> unprotect();</span><br><span class="line"> id *ret = next; <span class="comment">// faster than `return next-1` because of aliasing</span></span><br><span class="line"> *next++ = obj;</span><br><span class="line"> protect();</span><br><span class="line"> <span class="keyword">return</span> ret;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>因此<code>atautoreleasepoolobj</code>就是<code>POOL_BOUNDARY</code></p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="keyword">void</span> * atautoreleasepoolobj = objc_autoreleasePoolPush();</span><br></pre></td></tr></table></figure>
<p>对<code>AutoreleasePoolPage</code>的结构有了一定的了解后,接着对<code>push()</code>和<code>pop()</code>进行梳理</p>
<h3 id="objc-autoreleasePoolPush"><a href="#objc-autoreleasePoolPush" class="headerlink" title="objc_autoreleasePoolPush"></a>objc_autoreleasePoolPush</h3><p>回顾该函数</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> *<span class="title">objc_autoreleasePoolPush</span><span class="params">(<span class="keyword">void</span>)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> AutoreleasePoolPage::push();</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>它调用<code>AutoreleasePoolPage</code>的类方法<code>push</code></p>
<figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">inline</span> <span class="keyword">void</span> *<span class="title">push</span><span class="params">()</span> </span>{</span><br><span class="line"> id *dest;</span><br><span class="line"> <span class="keyword">if</span> (DebugPoolAllocation) {</span><br><span class="line"> <span class="comment">// Each autorelease pool starts on a new pool page.</span></span><br><span class="line"> dest = autoreleaseNewPage(POOL_BOUNDARY);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> dest = autoreleaseFast(POOL_BOUNDARY);</span><br><span class="line"> }</span><br><span class="line"> assert(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);</span><br><span class="line"> <span class="keyword">return</span> dest;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>在这里会进入一个比较关键的方法<code>autoreleaseFast</code>, 并传入边界对象</p>
<figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">inline</span> id *<span class="title">autoreleaseFast</span><span class="params">(id obj)</span> </span>{</span><br><span class="line"> AutoreleasePoolPage *page = hotPage();</span><br><span class="line"> <span class="keyword">if</span> (page && !page->full()) {</span><br><span class="line"> <span class="keyword">return</span> page->add(obj);</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (page) {</span><br><span class="line"> <span class="keyword">return</span> autoreleaseFullPage(obj, page);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> autoreleaseNoPage(obj);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>有三个分支,分别为</p>
<ul>
<li>存在hotpage且page不满<ul>
<li>调用add方法将对象指针压入AutoreleasePoolPage结构中</li>
</ul>
</li>
<li>存在hotpage但page已满<ul>
<li>调用autoreleaseFullPage新建一个结点page</li>
<li>调用add方法将对象指针压入AutoreleasePoolPage结构中</li>
</ul>
</li>
<li>无hotpage<ul>
<li>调用autoreleaseNoPage 创建一个page,并设置其为hotpage</li>
<li>调用add方法将对象指针压入AutoreleasePoolPage结构中</li>
</ul>
</li>
</ul>
<blockquote>
<p>hotpage可以理解为当前正在使用、活跃的AutoreleasePoolPage</p>
</blockquote>
<p><code>add</code>方法很简单,具体操作就是将对象指针放入<code>page</code>栈中,并移动<code>next</code>指针</p>
<h3 id="objc-autoreleasePoolPop"><a href="#objc-autoreleasePoolPop" class="headerlink" title="objc_autoreleasePoolPop"></a>objc_autoreleasePoolPop</h3><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">inline</span> <span class="keyword">void</span> <span class="title">pop</span><span class="params">(<span class="keyword">void</span> *token)</span> </span>{</span><br><span class="line"> AutoreleasePoolPage *page;</span><br><span class="line"> id *<span class="built_in">stop</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (token == (<span class="keyword">void</span>*)EMPTY_POOL_PLACEHOLDER) {</span><br><span class="line"> <span class="comment">// Popping the top-level placeholder pool.</span></span><br><span class="line"> <span class="keyword">if</span> (hotPage()) {</span><br><span class="line"> <span class="comment">// Pool was used. Pop its contents normally.</span></span><br><span class="line"> <span class="comment">// Pool pages remain allocated for re-use as usual.</span></span><br><span class="line"> pop(coldPage()-><span class="built_in">begin</span>());</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// Pool was never used. Clear the placeholder.</span></span><br><span class="line"> setHotPage(nil);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> page = pageForPointer(token);</span><br><span class="line"> <span class="built_in">stop</span> = (id *)token;</span><br><span class="line"> <span class="keyword">if</span> (*<span class="built_in">stop</span> != POOL_BOUNDARY) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">stop</span> == page-><span class="built_in">begin</span>() && !page->parent) {</span><br><span class="line"> <span class="comment">// Start of coldest page may correctly not be POOL_BOUNDARY:</span></span><br><span class="line"> <span class="comment">// 1. top-level pool is popped, leaving the cold page in place</span></span><br><span class="line"> <span class="comment">// 2. an object is autoreleased with no pool</span></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// Error. For bincompat purposes this is not </span></span><br><span class="line"> <span class="comment">// fatal in executables built with old SDKs.</span></span><br><span class="line"> <span class="keyword">return</span> badPop(token);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (PrintPoolHiwat) printHiwat();</span><br><span class="line"></span><br><span class="line"> page->releaseUntil(<span class="built_in">stop</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// memory: delete empty children</span></span><br><span class="line"> <span class="keyword">if</span> (DebugPoolAllocation && page->empty()) {</span><br><span class="line"> <span class="comment">// special case: delete everything during page-per-pool debugging</span></span><br><span class="line"> AutoreleasePoolPage *parent = page->parent;</span><br><span class="line"> page->kill();</span><br><span class="line"> setHotPage(parent);</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (DebugMissingPools && page->empty() && !page->parent) {</span><br><span class="line"> <span class="comment">// special case: delete everything for pop(top) </span></span><br><span class="line"> <span class="comment">// when debugging missing autorelease pools</span></span><br><span class="line"> page->kill();</span><br><span class="line"> setHotPage(nil);</span><br><span class="line"> } </span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (page->child) {</span><br><span class="line"> <span class="comment">// hysteresis: keep one empty child if page is more than half full</span></span><br><span class="line"> <span class="keyword">if</span> (page->lessThanHalfFull()) {</span><br><span class="line"> page->child->kill();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (page->child->child) {</span><br><span class="line"> page->child->child->kill();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>自动释放池析构时调用上述<code>pop</code>方法,传入的参数为<strong>边界对象</strong>,该静态方法共做了三件主要事情</p>
<ol>
<li>使用pageForPointer获取当前边界对象所在的page</li>
<li>将stop标记设置为当前边界对象的位置,调用releaseUntil,直到stop位置</li>
<li>调用child的kill方法清理链表中的子结点</li>
</ol>
<p><code>releaseUntil</code>方法遍历栈,获取放在<code>pool</code>对象的指针,调用<code>objc_release</code>函数释放对象所占内存</p>
<h2 id="autorelease方法"><a href="#autorelease方法" class="headerlink" title="autorelease方法"></a>autorelease方法</h2><p>根据<code>objc4</code>得出<code>autorelease</code>方法的调用栈,如下</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line">autorelease -> rootAutorelease -> rootAutorelease2 -> AutoreleasePoolPage::autorelease -> autoreleaseFast</span><br></pre></td></tr></table></figure>
<p>最终会调用<code>autoreleaseFast</code>方法,将对象指针压入当前<code>AutoreleasePoolPage</code>中</p>
<h2 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h2><p>梳理了一遍自动释放池的实现和<code>autorelease</code>方法,对iOS管理模型又加深了一点理解</p>
<ul>
<li>自动释放池是由 AutoreleasePoolPage 以双向链表的方式实现的</li>
<li>当对象调用 autorelease 方法时,会将对象加入 AutoreleasePoolPage 的栈中</li>
<li>调用 AutoreleasePoolPage::pop 方法会向栈中的对象发送 release 消息</li>
</ul>
<h2 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h2><p><a href="https://draveness.me/autoreleasepool" target="_blank" rel="noopener">自动释放池的前世今生 —- 深入解析 autoreleasepool</a></p>
<p><a href="http://blog.sunnyxx.com/2014/10/15/behind-autorelease/" target="_blank" rel="noopener">黑幕背后的Autorelease</a></p>
]]></content>
<categories>
<category>iOS</category>
</categories>
<tags>
<tag>iOS</tag>
</tags>
</entry>
<entry>
<title>使用futurerestore恢复到低版本iOS系统</title>
<url>/2020/01/16/%E4%BD%BF%E7%94%A8futurerestore%E6%81%A2%E5%A4%8D%E5%88%B0%E4%BD%8E%E7%89%88%E6%9C%ACiOS%E7%B3%BB%E7%BB%9F/</url>
<content><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>一般而言,iOS设备上的固件恢复需要配合Apple服务器进行校验,Apple停止公开验证某个固件版本时,iOS设备就不能从高版本恢复到停止验证的版本。</p>
<h1 id="前置条件"><a href="#前置条件" class="headerlink" title="前置条件"></a>前置条件</h1><ol>
<li>具备解锁nvram,写入generator的可能</li>
<li>根据设备的唯一码备份了对应的<code>SHSH2</code>文件</li>
<li>当前最新固件SEP兼容需要降级的目标版本固件</li>
</ol>
<h2 id="generator-amp-nonce"><a href="#generator-amp-nonce" class="headerlink" title="generator&nonce"></a>generator&nonce</h2><p>generator是记录在shsh2文件中的一串值,这串值对应着一个nonce。nonce是一个只能使用一次的随机数。它在认证协议中用于阻止重放攻击。</p>
<h2 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h2><blockquote>
<p>iOS/iTunes 在更新设备固件的过程中,会将设备的 ECID,系统版本等信息,以及一个一次使用的 Nonce 发送给 Apple 的验证服务器,服务器在校验通过后,会返回校验结果给 iOS/iTunes,结果使用非对称算法加密,在没有私钥的情况下无法解密,也无法伪造。</p>
</blockquote>
<blockquote>
<p>但是,我们可以将校验结果保存下来,之后 Apple 不再提供此版本校验的时候(假设不考虑 SEP 兼容性),在越狱后通过 nvram 固定 nonce 为此校验结果使用的,来重放校验过程,实现 iOS 系统降级/更新到不提供验证的版本。</p>
</blockquote>
<p>简单理解,备份的shsh2文件对应着一个nonce,在恢复到低版本的时候,先固定shsh2对应的nonce,然后绕过校验进行固件恢复</p>
<h1 id="实例"><a href="#实例" class="headerlink" title="实例"></a>实例</h1><h2 id="环境-amp-工具"><a href="#环境-amp-工具" class="headerlink" title="环境&工具"></a>环境&工具</h2><ul>
<li>使用CheckRa1n越狱后的iPhone 6 iOS 12.4.4</li>
<li>MacOS 10.15</li>
<li>nonce固定工具Generator Auto Setter</li>
<li><a href="https://github.com/tihmstar/futurerestore" target="_blank" rel="noopener">futurerestore</a>(latest version is recommend)</li>
</ul>
<h2 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h2><p>简单而言分为两步,第一固定nonce,第二连接设备使用futurerestore刷写</p>
<h3 id="固定nonce"><a href="#固定nonce" class="headerlink" title="固定nonce"></a>固定nonce</h3><p>设备越狱后添加repo:<a href="halo-michale.github.io/repo/">halo-michale.github.io/repo/</a>,安装插件Generator Auto Setter,默认会写入G值0x1111111111111111,我需要降到12.4,找到自己的shsh2文件里的G值为0xadcedaa4dc76c6f8,于是ssh root到越狱手机</p>
<figure class="highlight sh"><table><tr><td class="code"><pre><span class="line">Ken:~ root<span class="comment"># setgenerator 0xadcedaa4dc76c6f8</span></span><br></pre></td></tr></table></figure>
<figure class="highlight sh"><table><tr><td class="code"><pre><span class="line">arm_pgshift: 12</span><br><span class="line">tfp0: 0xa03</span><br><span class="line">kbase: 0xfffffff00f404000</span><br><span class="line">kslide: 0x8400000</span><br><span class="line">sec_cstring_start: 0xfffffff00f607aa0, sec_cstring_sz: 0x24f9f9</span><br><span class="line">sec_text_start: 0xfffffff00fa68000, sec_text_sz: 0x12ab540</span><br><span class="line">allproc: 0xfffffff010e546e8</span><br><span class="line">our_task: 0xfffffff08bc7d920</span><br><span class="line">nonce_serv: 0x1307</span><br><span class="line">nonce_conn: 0xb07</span><br><span class="line">ipc_port: 0xfffffff08d7f32c8</span><br><span class="line">nonce_object: 0xfffffff08a3b89a0</span><br><span class="line">boot_nonce_os_symbol: 0xfffffff08a330340</span><br><span class="line">nvram_serv: 0xb0f</span><br><span class="line">ipc_port: 0xfffffff08b667df0</span><br><span class="line">nvram_object: 0xfffffff08a291d00</span><br><span class="line">of_dict: 0xfffffff08a3bca50</span><br><span class="line">os_dict_cnt: 0xc</span><br><span class="line">os_dict_entry_ptr: 0xfffffff08b593e60</span><br><span class="line">key: 0xfffffff08a342aa0, value: 0xfffffff08e697c90</span><br><span class="line">key: 0xfffffff08a3423c0, value: 0xfffffff08bff84e0</span><br><span class="line">key: 0xfffffff08a345e60, value: 0xfffffff08a3bc7e0</span><br><span class="line">key: 0xfffffff08a333f00, value: 0xfffffff08a3bc960</span><br><span class="line">key: 0xfffffff08a3459c0, value: 0xfffffff08a3bc870</span><br><span class="line">key: 0xfffffff08a353120, value: 0xfffffff08a3bcab0</span><br><span class="line">key: 0xfffffff08a353060, value: 0xfffffff08a3bc690</span><br><span class="line">key: 0xfffffff08a353fa0, value: 0xfffffff08a3bc6c0</span><br><span class="line">key: 0xfffffff08a353f00, value: 0xfffffff08a3ddf90</span><br><span class="line">key: 0xfffffff08a353f40, value: 0xfffffff08a3bca80</span><br><span class="line">key: 0xfffffff08a353200, value: 0xfffffff08a3531e0</span><br><span class="line">key: 0xfffffff08a330340, value: 0xfffffff08be631c0</span><br><span class="line">os_string: 0xfffffff08be631c0</span><br><span class="line">string_ptr: 0xfffffff08b6a5e40</span><br><span class="line">Set nonce to 0xadcedaa4dc76c6f8</span><br></pre></td></tr></table></figure>
<h3 id="恢复固件"><a href="#恢复固件" class="headerlink" title="恢复固件"></a>恢复固件</h3><figure class="highlight sh"><table><tr><td class="code"><pre><span class="line">./futurerestore -t 2375331941521446_iPhone7,2_12.4-16G77_31decc9d1a18ca4192f886be692e2b6d5b6118d7.shsh2 --latest-sep --latest-baseband iPhone_4.7_12.4_16G77_Restore.ipsw</span><br></pre></td></tr></table></figure>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">➜ Downgrade ./futurerestore -t 2375331941521446_iPhone7,2_12.4-16G77_31decc9d1a18ca4192f886be692e2b6d5b6118d7.shsh2 --latest-sep --latest-baseband iPhone_4.7_12.4_16G77_Restore.ipsw</span><br><span class="line">Version: 81b98e0425e17250cc83d5badaf9a8cc6399f481 - 245</span><br><span class="line">Libipatcher version: 3159a387584e352f690cca859e013c3a4683f3e8 - 69</span><br><span class="line">Odysseus support: yes</span><br><span class="line">[INFO] 64-bit device detected</span><br><span class="line">futurerestore init done</span><br><span class="line">reading signing ticket 2375331941521446_iPhone7,2_12.4-16G77_31decc9d1a18ca4192f886be692e2b6d5b6118d7.shsh2 is done</span><br><span class="line">Found device iPhone7,2 n61ap</span><br><span class="line">user specified to use latest signed SEP (WARNING, THIS CAN CAUSE A NON-WORKING RESTORE)</span><br><span class="line">[TSSC] opening firmware.json</span><br><span class="line">[DOWN] downloading file https://api.ipsw.me/v2.1/firmwares.json/condensed</span><br><span class="line">[TSSC] selecting latest iOS: 12.4.4</span><br><span class="line">[TSSC] got firmware URL for iOS 12.4.4 build 16G140</span><br><span class="line">[TSSC] opening Buildmanifest for iPhone7,2_12.4.4</span><br><span class="line">100 [===================================================================================================>]</span><br><span class="line">downloading SEP</span><br><span class="line"></span><br><span class="line">100 [===================================================================================================>]</span><br><span class="line">[TSSC] opening /tmp/futurerestore/sepManifest.plist</span><br><span class="line">[TSSR] User specified not to request a baseband ticket.</span><br><span class="line">Request URL set to https://gs.apple.com/TSS/controller?action=2</span><br><span class="line">Sending TSS request attempt 1... response successfully received</span><br><span class="line">user specified to use latest signed baseband (WARNING, THIS CAN CAUSE A NON-WORKING RESTORE)</span><br><span class="line">downloading baseband</span><br><span class="line"></span><br><span class="line">100 [===================================================================================================>]</span><br><span class="line">[TSSC] opening /tmp/futurerestore/basebandManifest.plist</span><br><span class="line">[TSSR] User specified to request only a baseband ticket.</span><br><span class="line">Request URL set to https://gs.apple.com/TSS/controller?action=2</span><br><span class="line">Sending TSS request attempt 1... response successfully received</span><br><span class="line">Found device in Normal mode</span><br><span class="line">Entering recovery mode...</span><br><span class="line">INFO: device serial number is F78PGK5UG5MT</span><br><span class="line">Found device in Recovery mode</span><br><span class="line">Identified device as n61ap, iPhone7,2</span><br><span class="line">Extracting BuildManifest from iPSW</span><br><span class="line">Product version: 12.4</span><br><span class="line">Product build: 16G77 Major: 16</span><br><span class="line">Device supports IMG4: true</span><br><span class="line">Got ApNonce from device: 31 de cc 9d 1a 18 ca 41 92 f8 86 be 69 2e 2b 6d 5b 61 18 d7</span><br><span class="line">checking APTicket to be valid for this restore...</span><br><span class="line">Verified ECID in APTicket matches device ECID</span><br><span class="line">checking APTicket to be valid for this restore...</span><br><span class="line">Verified ECID in APTicket matches device ECID</span><br><span class="line">Verified APTicket to be valid for this restore</span><br><span class="line">Variant: Customer Erase Install (IPSW)</span><br><span class="line">This restore will erase your device data.</span><br><span class="line">Extracting filesystem from iPSW</span><br><span class="line">[==================================================] 100.0%</span><br><span class="line">Extracting iBEC.n61.RELEASE.im4p...</span><br><span class="line">Personalizing IMG4 component iBEC...</span><br><span class="line">Sending iBEC (731534 bytes)...</span><br><span class="line">waiting for device to reconnect...</span><br><span class="line">Getting SepNonce in recovery mode... ea 37 e5 69 22 48 c6 ac 9f f8 1d 3e 78 67 97 87 e9 f9 ce aa</span><br><span class="line">Getting ApNonce in recovery mode... 31 de cc 9d 1a 18 ca 41 92 f8 86 be 69 2e 2b 6d 5b 61 18 d7</span><br><span class="line">[WARNING] Setting bgcolor to green! If you don't see a green screen, then your device didn't boot iBEC correctly</span><br><span class="line">Recovery Mode Environment:</span><br><span class="line">iBoot build-version=iBoot-4513.270.14</span><br><span class="line">iBoot build-style=RELEASE</span><br><span class="line">Sending RestoreLogo...</span><br><span class="line">Extracting applelogo@2x~iphone.im4p...</span><br><span class="line">Personalizing IMG4 component RestoreLogo...</span><br><span class="line">Sending RestoreLogo (12334 bytes)...</span><br><span class="line">Extracting 048-78047-092.dmg.trustcache...</span><br><span class="line">Personalizing IMG4 component RestoreTrustCache...</span><br><span class="line">Sending RestoreTrustCache (9681 bytes)...</span><br><span class="line">ramdisk-size=0x10000000</span><br><span class="line">Extracting 048-78047-092.dmg...</span><br><span class="line">Personalizing IMG4 component RestoreRamDisk...</span><br><span class="line">Sending RestoreRamDisk (91608779 bytes)...</span><br><span class="line">Extracting DeviceTree.n61ap.im4p...</span><br><span class="line">Personalizing IMG4 component RestoreDeviceTree...</span><br><span class="line">Sending RestoreDeviceTree (125713 bytes)...</span><br><span class="line">Extracting kernelcache.release.iphone7...</span><br><span class="line">Personalizing IMG4 component RestoreKernelCache...</span><br><span class="line">Sending RestoreKernelCache (14069235 bytes)...</span><br><span class="line">Trying to fetch new signing tickets</span><br><span class="line">Request URL set to https://gs.apple.com/TSS/controller?action=2</span><br><span class="line">Sending TSS request attempt 1... response successfully received</span><br><span class="line">Received signing tickets</span><br><span class="line">About to restore device...</span><br><span class="line">Waiting for device...</span><br><span class="line">Device fa7290e4f299aff884b5eb6febce2643d11d092d is now connected in restore mode...</span><br><span class="line">Connecting now...</span><br><span class="line">Connected to com.apple.mobile.restored, version 15</span><br><span class="line">Device fa7290e4f299aff884b5eb6febce2643d11d092d has successfully entered restore mode</span><br><span class="line">Hardware Information:</span><br><span class="line">BoardID: 6</span><br><span class="line">ChipID: 28672</span><br><span class="line">UniqueChipID: 2375331941521446</span><br><span class="line">ProductionMode: true</span><br><span class="line">Starting FDR listener thread</span><br><span class="line">About to send RootTicket...</span><br><span class="line">Sending RootTicket now...</span><br><span class="line">Done sending RootTicket</span><br><span class="line">Waiting for NAND (28)</span><br><span class="line">About to send NORData...</span><br><span class="line">Found firmware path Firmware/all_flash</span><br><span class="line">Getting firmware manifest from build identity</span><br><span class="line">Extracting LLB.n61.RELEASE.im4p...</span><br><span class="line">Personalizing IMG4 component LLB...</span><br><span class="line">Extracting applelogo@2x~iphone.im4p...</span><br><span class="line">Personalizing IMG4 component AppleLogo...</span><br><span class="line">Extracting batterycharging0@2x~iphone.im4p...</span><br><span class="line">Personalizing IMG4 component BatteryCharging0...</span><br><span class="line">Extracting batterycharging1@2x~iphone.im4p...</span><br><span class="line">Personalizing IMG4 component BatteryCharging1...</span><br><span class="line">Extracting batteryfull@2x~iphone.im4p...</span><br><span class="line">Personalizing IMG4 component BatteryFull...</span><br><span class="line">Extracting batterylow0@2x~iphone.im4p...</span><br><span class="line">Personalizing IMG4 component BatteryLow0...</span><br><span class="line">Extracting batterylow1@2x~iphone.im4p...</span><br><span class="line">Personalizing IMG4 component BatteryLow1...</span><br><span class="line">Extracting glyphplugin@1334~iphone-lightning.im4p...</span><br><span class="line">Personalizing IMG4 component BatteryPlugin...</span><br><span class="line">Extracting DeviceTree.n61ap.im4p...</span><br><span class="line">Personalizing IMG4 component DeviceTree...</span><br><span class="line">Extracting recoverymode@1334~iphone-lightning.im4p...</span><br><span class="line">Personalizing IMG4 component RecoveryMode...</span><br><span class="line">Extracting iBoot.n61.RELEASE.im4p...</span><br><span class="line">Personalizing IMG4 component iBoot...</span><br><span class="line">Personalizing IMG4 component RestoreSEP...</span><br><span class="line">Personalizing IMG4 component SEP...</span><br><span class="line">Sending NORData now...</span><br><span class="line">Done sending NORData</span><br><span class="line">Checking filesystems (15)</span><br><span class="line">Checking filesystems (15)</span><br><span class="line">About to send FDR Trust data...</span><br><span class="line">Sending FDR Trust data now...</span><br><span class="line">Done sending FDR Trust Data</span><br><span class="line">Unmounting filesystems (29)</span><br><span class="line">Unmounting filesystems (29)</span><br><span class="line">Unmounting filesystems (29)</span><br><span class="line">Unmounting filesystems (29)</span><br><span class="line">Creating partition map (11)</span><br><span class="line">Creating filesystem (12)</span><br><span class="line">About to send filesystem...</span><br><span class="line">Connected to ASR</span><br><span class="line">Validating the filesystem</span><br><span class="line">Filesystem validated</span><br><span class="line">Sending filesystem now...</span><br><span class="line">[==================================================] 100.0%</span><br><span class="line">Done sending filesystem</span><br><span class="line">Verifying restore (14)</span><br><span class="line">[==================================================] 100.0%</span><br><span class="line">Checking filesystems (15)</span><br><span class="line">Checking filesystems (15)</span><br><span class="line">Checking filesystems (15)</span><br><span class="line">Checking filesystems (15)</span><br><span class="line">Mounting filesystems (16)</span><br><span class="line">Mounting filesystems (16)</span><br><span class="line">Mounting filesystems (16)</span><br><span class="line">About to send KernelCache...</span><br><span class="line">Extracting kernelcache.release.iphone7...</span><br><span class="line">Personalizing IMG4 component KernelCache...</span><br><span class="line">Sending KernelCache now...</span><br><span class="line">Done sending KernelCache</span><br><span class="line">Installing kernelcache (27)</span><br><span class="line">About to send DeviceTree...</span><br><span class="line">Extracting DeviceTree.n61ap.im4p...</span><br><span class="line">Personalizing IMG4 component DeviceTree...</span><br><span class="line">Sending DeviceTree now...</span><br><span class="line">Done sending DeviceTree</span><br><span class="line">Certifying Savage (61)</span><br><span class="line">Flashing firmware (18)</span><br><span class="line">[==================================================] 100.0%</span><br><span class="line">Unknown operation (36)</span><br><span class="line">About to send FUD data...</span><br><span class="line">Found FUD component 'RestoreTrustCache'</span><br><span class="line">Extracting 048-78047-092.dmg.trustcache...</span><br><span class="line">Personalizing IMG4 component RestoreTrustCache...</span><br><span class="line">Found FUD component 'StaticTrustCache'</span><br><span class="line">Extracting 048-76490-092.dmg.trustcache...</span><br><span class="line">Personalizing IMG4 component StaticTrustCache...</span><br><span class="line">Sending FUD data now...</span><br><span class="line">Done sending FUD data</span><br><span class="line">Updating gas gauge software (47)</span><br><span class="line">Updating gas gauge software (47)</span><br><span class="line">Updating Stockholm (55)</span><br><span class="line">Unknown operation (36)</span><br><span class="line">About to send FUD data...</span><br><span class="line">Found FUD component 'RestoreTrustCache'</span><br><span class="line">Extracting 048-78047-092.dmg.trustcache...</span><br><span class="line">Personalizing IMG4 component RestoreTrustCache...</span><br><span class="line">Found FUD component 'StaticTrustCache'</span><br><span class="line">Extracting 048-76490-092.dmg.trustcache...</span><br><span class="line">Personalizing IMG4 component StaticTrustCache...</span><br><span class="line">Sending FUD data now...</span><br><span class="line">Done sending FUD data</span><br><span class="line">Updating baseband (19)</span><br><span class="line">About to send BasebandData...</span><br><span class="line">Sending Baseband TSS request...</span><br><span class="line">Request URL set to https://gs.apple.com/TSS/controller?action=2</span><br><span class="line">Sending TSS request attempt 1... response successfully received</span><br><span class="line">Received Baseband SHSH blobs</span><br><span class="line">Sending BasebandData now...</span><br><span class="line">Done sending BasebandData</span><br><span class="line">Updating Baseband in progress...</span><br><span class="line">About to send BasebandData...</span><br><span class="line">Sending BasebandData now...</span><br><span class="line">Done sending BasebandData</span><br><span class="line">Updating Baseband completed.</span><br><span class="line">Updating SE Firmware (59)</span><br><span class="line">Fixing up /var (17)</span><br><span class="line">Creating system key bag (50)</span><br><span class="line">Modifying persistent boot-args (25)</span><br><span class="line">Unmounting filesystems (29)</span><br><span class="line">Unmounting filesystems (29)</span><br><span class="line">Unmounting filesystems (29)</span><br><span class="line">Unmounting filesystems (29)</span><br><span class="line">Got status message</span><br><span class="line">Status: Restore Finished</span><br><span class="line">Cleaning up...</span><br><span class="line">DONE</span><br><span class="line">Done: restoring succeeded.</span><br></pre></td></tr></table></figure>
]]></content>
<categories>
<category>iOS</category>
</categories>
<tags>
<tag>iOS</tag>
</tags>
</entry>
<entry>
<title>iOS内存管理</title>
<url>/2020/01/16/iOS%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86/</url>
<content><![CDATA[<h1 id="内存使用"><a href="#内存使用" class="headerlink" title="内存使用"></a>内存使用</h1><p>应用的内存消耗主要分为两部分:栈大小和堆大小</p>
<h2 id="栈大小"><a href="#栈大小" class="headerlink" title="栈大小"></a>栈大小</h2><p>应用中的每个线程都有专用的栈空间,栈可以在线程存在期间自由使用。线程的最大栈空间很小,因此有很多限制</p>
<ul>
<li>限制递归调用的最大方法数</li>
<li>方法中使用的参数个数和内部变量个数</li>
<li>视图层级的最大深度</li>
<li>…</li>
</ul>
<h2 id="堆大小"><a href="#堆大小" class="headerlink" title="堆大小"></a>堆大小</h2><p>每个进程的所有线程共享一个堆。OC对象如NSString、UIImage、视图等都会消耗堆内存。<strong>大多数情况下,OC对象存放在堆中</strong>,<strong>与通过类创建的对象相关的所有数据也都存放在堆中</strong>。</p>
<h1 id="内存管理模型"><a href="#内存管理模型" class="headerlink" title="内存管理模型"></a>内存管理模型</h1><p>苹果LLVM官方文档使用术语<code>持有关系</code>和<code>引用计数</code>来描述OC对象的内存管理。</p>
<p>如果一个对象处于被持有状态,那么它占用的内存就不能被回收。</p>
<p>当一个对象在某个方法内部创建时,那么该方法就持有这个对象。如果对象从方法中返回,则方法调用者声称建立了持有关系。对象”赋值”给其他变量,对应的变量同样声称建立了持有关系。</p>
<p>一个对象的持有者数量称之为引用计数,当持有者与被持有对象建立持有关系时,被持有对象的引用计数增加1,同理,解除持有关系时,引用计数减少1,当引用计数降为0时,该对象被释放,对应相关的内存会被回收。</p>
<p>管理形式分为手动引用计数(<code>manual reference counting,MRC</code>)与自动引用计数(<code>automatic reference counting, ARC</code>)</p>
<h2 id="MRC"><a href="#MRC" class="headerlink" title="MRC"></a>MRC</h2><p>虽然MRC现在已经十分罕见,但理解MRC对内存管理有很大帮助</p>
<figure class="highlight objc"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">@interface</span> <span class="title">MyObject</span> : <span class="title">NSObject</span></span></span><br><span class="line"><span class="keyword">@end</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">@implementation</span> <span class="title">MyObject</span></span></span><br><span class="line">- (<span class="keyword">void</span>)dealloc</span><br><span class="line">{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"%s"</span>,__func__);</span><br><span class="line"> [<span class="keyword">super</span> dealloc];</span><br><span class="line">}</span><br><span class="line"><span class="keyword">@end</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">@interface</span> <span class="title">ViewController</span> ()</span></span><br><span class="line"><span class="keyword">@end</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">@implementation</span> <span class="title">ViewController</span></span></span><br><span class="line"></span><br><span class="line">- (<span class="keyword">void</span>)viewDidLoad {</span><br><span class="line"> [<span class="keyword">super</span> viewDidLoad];</span><br><span class="line"> </span><br><span class="line"> MyObject *obj = [[MyObject alloc] init];<span class="comment">//1</span></span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"retainCount : %lu"</span>,obj.retainCount); </span><br><span class="line"> MyObject *objRetained = [obj <span class="keyword">retain</span>];<span class="comment">//2</span></span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"retainCount : %lu"</span>,obj.retainCount);</span><br><span class="line"> [objRetained release];<span class="comment">//3</span></span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"retainCount : %lu"</span>,obj.retainCount);</span><br><span class="line"> [obj release];<span class="comment">//4</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">@end</span></span><br></pre></td></tr></table></figure>
<figure class="highlight angelscript"><table><tr><td class="code"><pre><span class="line"><span class="number">2020</span><span class="number">-01</span><span class="number">-16</span> <span class="number">11</span>:<span class="number">21</span>:<span class="number">35.447154</span>+<span class="number">0800</span> block[<span class="number">999</span>:<span class="number">19673</span>] retainCount : <span class="number">1</span></span><br><span class="line"><span class="number">2020</span><span class="number">-01</span><span class="number">-16</span> <span class="number">11</span>:<span class="number">21</span>:<span class="number">35.447242</span>+<span class="number">0800</span> block[<span class="number">999</span>:<span class="number">19673</span>] retainCount : <span class="number">2</span></span><br><span class="line"><span class="number">2020</span><span class="number">-01</span><span class="number">-16</span> <span class="number">11</span>:<span class="number">21</span>:<span class="number">35.447270</span>+<span class="number">0800</span> block[<span class="number">999</span>:<span class="number">19673</span>] retainCount : <span class="number">1</span></span><br><span class="line"><span class="number">2020</span><span class="number">-01</span><span class="number">-16</span> <span class="number">11</span>:<span class="number">21</span>:<span class="number">35.447411</span>+<span class="number">0800</span> block[<span class="number">999</span>:<span class="number">19673</span>] -[MyObject dealloc]</span><br></pre></td></tr></table></figure>
<ol>
<li>创建对象,obj建立了持有关系,引用计数为1</li>
<li>objRetained建立了持有关系,引用计数增加为2</li>
<li>objRetained解除了持有关系,引用计数降为1</li>
<li>obj解除持有关系,引用计数降为0,对象调用析构方法dealloc</li>
</ol>
<hr>
<p>方法中的引用计数</p>
<figure class="highlight objc"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">@interface</span> <span class="title">MyObject</span> : <span class="title">NSObject</span></span></span><br><span class="line">- (<span class="built_in">NSString</span> *)name;</span><br><span class="line"><span class="keyword">@end</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">@implementation</span> <span class="title">MyObject</span></span></span><br><span class="line"></span><br><span class="line">- (<span class="built_in">NSString</span> *)name {</span><br><span class="line"> <span class="built_in">NSString</span> *result = [[<span class="built_in">NSString</span> alloc] initWithFormat:<span class="string">@"%@\n"</span>,<span class="string">@"kinken"</span>];<span class="comment">//1</span></span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">- (<span class="keyword">void</span>)dealloc</span><br><span class="line">{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"%s"</span>,__func__);</span><br><span class="line"> [<span class="keyword">super</span> dealloc];</span><br><span class="line">}</span><br><span class="line"><span class="keyword">@end</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">@interface</span> <span class="title">ViewController</span> ()</span></span><br><span class="line"><span class="keyword">@end</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">@implementation</span> <span class="title">ViewController</span></span></span><br><span class="line"></span><br><span class="line">- (<span class="keyword">void</span>)viewDidLoad {</span><br><span class="line"> [<span class="keyword">super</span> viewDidLoad];</span><br><span class="line"> </span><br><span class="line"> MyObject *obj = [[MyObject alloc] init];</span><br><span class="line"> <span class="built_in">NSString</span> *name = [obj name];<span class="comment">//2</span></span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"obj's name : %@"</span>,name);</span><br><span class="line"> [name release];<span class="comment">//3</span></span><br><span class="line"> [obj release];</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="number">2020</span><span class="number">-01</span><span class="number">-16</span> <span class="number">12</span><span class="string">:43:50.861654+0800</span> <span class="string">block[1026:28263]</span> <span class="string">obj's</span> <span class="attr">name :</span> <span class="string">kinken</span></span><br><span class="line"><span class="number">2020</span><span class="number">-01</span><span class="number">-16</span> <span class="number">12</span><span class="string">:43:50.861756+0800</span> <span class="string">block[1026:28263]</span> <span class="string">-[MyObject</span> <span class="string">dealloc]</span></span><br></pre></td></tr></table></figure>
<ol>
<li>首次创建对象,<code>name</code>指向内存的引用计数为1,<code>name</code>方法持有<code>NSString</code>对象</li>
<li>通过<code>name</code>指向的内存引用计数仍然为1。在<code>viewDidLoad</code>方法中通过调用<code>[obj name]</code>引用了对象,获得了该对象,此时不应该再次持有(<code>retain</code>)。(<code>name</code>方法内部通过<code>alloc</code>方法创建对象已经<code>retain</code>建立持有关系)</li>
<li>使用完毕,解除持有关系,引用计数为0</li>
</ol>
<p><code>viewDidLoad</code>并不知道<code>name</code>方法是创建了一个新的对象还是重用旧的对象。但是它知道对象的引用计数加1后会被返回,因此这里没有继续持有(<code>retain</code>)<code>name</code>。</p>
<p>我的理解是当对象被作为返回值返回时,内部会建立一个新的持有关系(当前例子中<code>Myobject</code>类的<code>name</code>方法内部创建<code>NSString</code>类的对象并持有),你不需要再次<code>retain</code>来再次持有,只需要在使用完毕后手动<code>release</code>对象。</p>
<h2 id="Autoreleasing-Objects"><a href="#Autoreleasing-Objects" class="headerlink" title="Autoreleasing Objects"></a>Autoreleasing Objects</h2><p>自动释放对象(<code>Autoreleasing Objects</code>),让你解除对一个对象的持有关系,但延后对它的销毁。当在方法中创建一个对象并且需要将其返回时,自动释放就非常有用。自动释放可以帮助MRC管理对象的生命周期。</p>
<p>沿用上一节的例子,从表面上看,没有什么能够表示<code>name</code>方法持有了返回的字符串对象,因此在<code>viewDidLoad</code>方法内也不应该<code>release</code>返回的字符串,这有可能会导致内存泄漏(释放一块未知的内存区域)。而加入<code>[name release]</code>的原因是我们事先知道name方法内部是通过<code>alloc</code>创建了对象并持有(<code>retain</code>)。</p>
<p>类似于这种<strong>创建对象并持有</strong>的情况有以下</p>
<ul>
<li>alloc</li>
<li>new </li>
<li>copy</li>
<li>mutableCopy</li>
</ul>
<p>那么,在不知道方法内部是如何创建对象的情况下,外部的方法调用者如何正确管理返回值对象的生命周期</p>
<p>有两种可能的解决方案</p>
<ul>
<li>不使用alloc或相关的方法</li>
<li>对返回的对象使用使用autorelease延时释放</li>
</ul>
<p>第一种</p>
<figure class="highlight objc"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">@interface</span> <span class="title">MyObject</span> : <span class="title">NSObject</span></span></span><br><span class="line">- (<span class="built_in">NSString</span> *)name;</span><br><span class="line"><span class="keyword">@end</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">@implementation</span> <span class="title">MyObject</span></span></span><br><span class="line"></span><br><span class="line">- (<span class="built_in">NSString</span> *)name {</span><br><span class="line"> <span class="built_in">NSString</span> *result = [<span class="built_in">NSString</span> stringWithFormat:<span class="string">@"%@"</span>,<span class="string">@"kinken"</span>];<span class="comment">//1</span></span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">- (<span class="keyword">void</span>)dealloc</span><br><span class="line">{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"%s"</span>,__func__);</span><br><span class="line"> [<span class="keyword">super</span> dealloc];</span><br><span class="line">}</span><br><span class="line"><span class="keyword">@end</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">@interface</span> <span class="title">ViewController</span> ()</span></span><br><span class="line"><span class="keyword">@end</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">@implementation</span> <span class="title">ViewController</span></span></span><br><span class="line"></span><br><span class="line">- (<span class="keyword">void</span>)viewDidLoad {</span><br><span class="line"> [<span class="keyword">super</span> viewDidLoad];</span><br><span class="line"> </span><br><span class="line"> MyObject *obj = [[MyObject alloc] init];</span><br><span class="line"> <span class="built_in">NSString</span> *name = [obj name];</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"obj's name : %@"</span>,name);</span><br><span class="line"> <span class="comment">//2</span></span><br><span class="line"> [obj release];</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<ol>
<li>不使用alloc方法</li>
<li>实际上stringWithFormat返回的是一个autorelease对象,返回的时候已经解除持有关系,只是还没有销毁</li>
</ol>
<p>第一种方案实际上是第二种方案的一个例子</p>
<hr>
<p>第二种</p>
<p><code>NSObject</code>协议定义了<code>autorelease</code>方法,可在从方法中返回对象时使用它返回一个<code>autorelease</code>对象</p>
<figure class="highlight objc"><table><tr><td class="code"><pre><span class="line">- (<span class="built_in">NSString</span> *)name {</span><br><span class="line"> <span class="built_in">NSString</span> *result = [[[<span class="built_in">NSString</span> alloc] initWithFormat:<span class="string">@"%@\n"</span>,<span class="string">@"kinken"</span>] autorelease];</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<ul>
<li>通过alloc创建对象并持有</li>
<li>autorelease表示解除持有关系,但是延时释放,即允许方法的调用者在对象被释放之前使用该对象</li>
</ul>
<h2 id="Autorelease-Pool-Blocks"><a href="#Autorelease-Pool-Blocks" class="headerlink" title="Autorelease Pool Blocks"></a>Autorelease Pool Blocks</h2><p>自动释放池块(<code>Autorelease Pool Blocks</code>)是允许你解除对一个对象的持有关系,但可避免对象立即被销毁回收的一个工具。</p>
<p>它能确保在块内创建的对象在块完成时被回收。这在创建了多个对象的场景中非常有用,因为在块中可以提早释放不再需要的对象,从而降低内存用量。</p>
<p>iOS中常见的代码</p>
<figure class="highlight objc"><table><tr><td class="code"><pre><span class="line"><span class="keyword">int</span> main(<span class="keyword">int</span> argc, <span class="keyword">char</span> * argv[]) {</span><br><span class="line"> <span class="keyword">@autoreleasepool</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">UIApplicationMain</span>(argc, argv, <span class="literal">nil</span>, <span class="built_in">NSStringFromClass</span>([AppDelegate <span class="keyword">class</span>]));</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>用<code>@autoreleasepool{}</code>表示自动释放池块,块中收到<code>autorelease</code>消息的所有对象都会在<code>autoreleasepool</code>块结束时收到<code>release</code>消息。每调用一次<code>autorelease</code>会向对象发送一次<code>release</code>消息,这意味着如果一 个对象收到了不止一次的 <code>autorelease</code>消息,那它也会多次收到<code>release</code>消息。</p>
<p><code>main</code>方法的代码中可以发现,整个应用的代码都在自动释放池块中,这意味着应用程序关闭退出后,所有的<code>autorelease</code>对象最后都会被回收,不会导致内存泄漏。</p>
<hr>
<p>嵌套<code>autoreleasepool</code>块</p>
<figure class="highlight objc"><table><tr><td class="code"><pre><span class="line"><span class="keyword">@autoreleasepool</span> { </span><br><span class="line"> <span class="comment">// 一些代码 </span></span><br><span class="line"> <span class="keyword">@autoreleasepool</span> {</span><br><span class="line"> <span class="comment">// 更多代码 </span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>这样操作的目的是<strong>提前执行对象的销毁回收</strong></p>
<blockquote>
<p>Cocoa 框架希望代码能在 autoreleasepool 块内执行,否则 autorelease 对象将无法被 释放,从而导致应用发生内存泄漏。</p>
</blockquote>
<blockquote>
<p>AppKit 和 UIKit 框架将事件 - 循环的迭代放入了 autoreleasepool 块中。因此,通常 不需要你自己再创建 autoreleasepool 块了。</p>
</blockquote>
<hr>
<p>说说手动创建<code>autoreleasepool</code>的情况</p>
<ul>
<li>当有需要创建很多临时对象的循环时,在循环中使用<code>autoreleasepool</code>提早释放内存,降低内存使用总和峰值</li>
<li>当你手动创建一个线程时,每个线程都需要有它自己的<code>autoreleasepool</code>栈。主线程用自己的<code>autoreleasepool</code>启动,因为它来自统一生成的代码(类似于上述的main)。然而,对于任何自定义的线程,在线程执行开始的时候,必须创建线程对应的<code>autoreleasepool</code>,否则会有内存泄漏。</li>
<li>当你编写一个不是基于UI框架的程序,如命令行工具</li>
</ul>
<p>情况一,循环中的<code>autoreleasepool</code></p>
<figure class="highlight objc"><table><tr><td class="code"><pre><span class="line"><span class="comment">//代码块1</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">@autoreleasepool</span> {</span><br><span class="line"> <span class="built_in">NSUInteger</span> *userCount = userDatabase.userCount;</span><br><span class="line"> <span class="keyword">for</span>(<span class="built_in">NSUInteger</span> *i = <span class="number">0</span>; i < userCount; i++) {</span><br><span class="line"> Person *p = [userDatabase userAtIndex:i];</span><br><span class="line"> <span class="built_in">NSString</span> *fname = p.fname; <span class="keyword">if</span>(fname == <span class="literal">nil</span>) {</span><br><span class="line"> fname = [<span class="keyword">self</span> askUserForFirstName];</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">NSString</span> *lname = p.lname; <span class="keyword">if</span>(lname == <span class="literal">nil</span>) {</span><br><span class="line"> lname = [<span class="keyword">self</span> askUserForLastName];</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//...</span></span><br><span class="line"> [userDatabase updateUser:p];</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<figure class="highlight objc"><table><tr><td class="code"><pre><span class="line"><span class="comment">//代码块2</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">@autoreleasepool</span> {</span><br><span class="line"> <span class="built_in">NSUInteger</span> *userCount = userDatabase.userCount; </span><br><span class="line"> <span class="keyword">for</span>(<span class="built_in">NSUInteger</span> *i = <span class="number">0</span>; i < userCount; i++) {</span><br><span class="line"> <span class="keyword">@autoreleasepool</span> {</span><br><span class="line"> Person *p = [userDatabase userAtIndex:i];</span><br><span class="line"> <span class="built_in">NSString</span> *fname = p.fname; <span class="keyword">if</span>(fname == <span class="literal">nil</span>) {</span><br><span class="line"> fname = [<span class="keyword">self</span> askUserForFirstName];</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">NSString</span> *lname = p.lname; <span class="keyword">if</span>(lname == <span class="literal">nil</span>) {</span><br><span class="line"> lname = [<span class="keyword">self</span> askUserForLastName];</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//...</span></span><br><span class="line"> [userDatabase updateUser:p];</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<ol>
<li>代码块1只有一个<code>autoreleasepool</code>,内存释放回收需要在所有的迭代完成之后才开始进行</li>
<li>代码块2在循环中内嵌<code>autoreleasepool</code>确保每次循环迭代完成后<strong>立即释放回收对象内存</strong>,从而减少内存最大用量</li>
</ol>
<p>情况二,自定义线程中必须创建自己的<code>autoreleasepool</code></p>
<figure class="highlight objc"><table><tr><td class="code"><pre><span class="line">- (<span class="keyword">void</span>)viewDidLoad {</span><br><span class="line"> [<span class="keyword">super</span> viewDidLoad];</span><br><span class="line"></span><br><span class="line"> <span class="built_in">NSThread</span> *newThread = [[<span class="built_in">NSThread</span> alloc] initWithTarget:<span class="keyword">self</span> selector:<span class="keyword">@selector</span>(newThreadStart:) object:<span class="literal">nil</span>];</span><br><span class="line"> [newThread start];</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">- (<span class="keyword">void</span>)newThreadStart:(<span class="keyword">id</span>)obj {</span><br><span class="line"> <span class="keyword">@autoreleasepool</span> {</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"newThreadStart"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="ARC"><a href="#ARC" class="headerlink" title="ARC"></a>ARC</h2><p>使用MRC需要开发人员在适当位置频繁使用<code>retain</code>、<code>release</code>和<code>autorelease</code>,一方面需要开发人员十分熟悉MRC的内存管理规则,另一方面需要花费很多精力编写重复的代码。ARC解决方案在这样的环境下提出,目前OC与Swift均使用ARC。</p>
<p>ARC是一种编译器特性,它分析对象在代码中的生命周期,并在编译期自动插入适合的内存管理代码(<code>retain</code>,<code>release</code>等)。同时编译期还会生成适合的<code>dealloc</code>方法对对象内存做清理工作。这大大减轻了开发人员的内存管理负担。</p>
<hr>
<p>为了规范ARC内存管理的模型,使用ARC需要遵循的一些规则</p>
<ul>
<li>不能实现或调用 retain、release、autorelease 或 retainCount 方法。这一限制不仅针 对对象,对选择器同样有效。因此,[obj release]或@selector(retain)是编译时的错误。</li>
<li>可以实现 dealloc 方法,但不能调用它们。不仅不能调用其他对象的 dealloc 方法,也<br>不能调用超类。[super dealloc] 是编译时的错误。但你仍然可以对 Core Foundation 类型的对象调用 CFRetain、CFRelease 等相关方法。</li>
<li>不能调用 NSAllocateObject 和 NSDeallocateObject 方法。应使用 alloc 方法创建对象, 运行时负责回收对象</li>
<li>不能在 C 语言的结构体内使用对象指针。</li>
<li>不能在 id 类型和 void * 类型之间自动转换。如果需要,那么你必须做显式转换。</li>
<li>不能使用 NSAutoreleasePool,要替换使用 autoreleasepool 块。</li>
<li>不能使用 NSZone 内存区域。</li>
<li>属性的访问器名称不能以 new 开头,以确保与 MRC 的互操作性。</li>
</ul>
<h3 id="引用类型"><a href="#引用类型" class="headerlink" title="引用类型"></a>引用类型</h3><ul>
<li>强引用,默认的引用类型。被强引用指向的对象内存不会被释放。强引用会对引用计数加1,从而延长对象的生命周期</li>
<li>弱引用,不会增加引用计数类型,因而不会延长对象的生命周期。</li>
</ul>
<h4 id="变量限定符"><a href="#变量限定符" class="headerlink" title="变量限定符"></a>变量限定符</h4><p>ARC为变量提供4种生命周期限定符</p>
<ul>
<li>__strong,默认限定符,无需显式引入。只要有强引用指向,对象就会长时间驻留在内存堆中。可以将__strong理解为retain调用的ARC版本</li>
<li>__weak,表示引用不会保持被引用对象的存货。当没有强引用指向对象,弱引用会被置为nil。可将__weak 看作是 assign 操作符的 ARC 版本,只是对象被回收时,__weak具有安全性——指针将自动被设置为 nil。</li>
<li>__unsafe_unretained, 与 __weak 类似,只是当没有强引用指向对象时,__unsafe_unretained 不会被置为 nil。 可将其看作 assign 操作符的 ARC 版本。</li>
<li>__autoreleasing,用于由引用使用id *传递的消息参数。它预期了autorelease方法会在传递参数的方法中被调用</li>
</ul>
<p>第四点解释</p>
<figure class="highlight objc"><table><tr><td class="code"><pre><span class="line">- (<span class="built_in">BOOL</span>)moveItemAtPath:(<span class="built_in">NSString</span> *)srcPath toPath:(<span class="built_in">NSString</span> *)dstPath error:(<span class="built_in">NSError</span> **)error API_AVAILABLE(macos(<span class="number">10.5</span>), ios(<span class="number">2.0</span>), watchos(<span class="number">2.0</span>), tvos(<span class="number">9.0</span>));</span><br><span class="line"></span><br><span class="line">编译时实际是</span><br><span class="line"></span><br><span class="line">- (<span class="built_in">BOOL</span>)moveItemAtPath:(<span class="built_in">NSString</span> *)srcPath toPath:(<span class="built_in">NSString</span> *)dstPath error:(<span class="built_in">NSError</span> * __autoreleasing*)error API_AVAILABLE(macos(<span class="number">10.5</span>), ios(<span class="number">2.0</span>), watchos(<span class="number">2.0</span>), tvos(<span class="number">9.0</span>));</span><br></pre></td></tr></table></figure>
<p>变量限定符的使用情况</p>
<figure class="highlight objc"><table><tr><td class="code"><pre><span class="line">MyObject * __<span class="keyword">strong</span> obj1 = [[MyObject alloc] init];<span class="comment">//1</span></span><br><span class="line">MyObject * __<span class="keyword">weak</span> obj2 = [[MyObject alloc] init];<span class="comment">//2</span></span><br><span class="line">MyObject * __<span class="keyword">unsafe_unretained</span> obj3 = [[MyObject alloc] init];<span class="comment">//3</span></span><br><span class="line">MyObject * __autoreleasing obj = [[MyObject alloc] init];<span class="comment">//4</span></span><br></pre></td></tr></table></figure>
<ol>
<li>创建对象后引用计数为 1,并且对象在 obj1引用期间不会被回收。</li>
<li>创建对象后引用计数为 0,对象会被立即释放,且 obj2 将被设置为 nil。</li>
<li>创建对象后引用计数为 1,对象会被立即释放,但 obj3不会被设置为 nil。</li>
<li>创建对象后引用计数为 1,当方法返回时对象会被自动释放。</li>
</ol>
<h4 id="属性限定符"><a href="#属性限定符" class="headerlink" title="属性限定符"></a>属性限定符</h4><p>属性声明根据引用类型新增两个持有关系限定符:<code>strong</code>和<code>weak</code>。另外<code>assign</code>的语义也更新了。总的来说共有6个限定符。</p>
<ul>
<li>strong,默认,指定了__strong强引用关系</li>
<li>weak,指定__weak弱引用关系</li>
<li>assign,MRC环境是默认的持有关系限定符,ARC环境是__unsafe_unretained关系</li>
<li>copy,指定__strong的强引用关系同时,还表示使用setter中的copy语义(对象的copy)</li>
<li>retain,指定__strong强引用关系</li>
<li>unsafe_unretained,指定了__unsafe_unretained关系</li>
</ul>
<p><code>assign</code>和<code>unsafe_unretained</code>只进行值复制而没有任何实质性的检查,所以它们仅用于值类型(BOOL、NSInteger等等)。避免将它们用于引用类型或指针类型</p>
<p>属性限定符的使用情况</p>
<figure class="highlight objc"><table><tr><td class="code"><pre><span class="line"><span class="keyword">@property</span> (<span class="keyword">nonatomic</span>, <span class="keyword">strong</span>) <span class="built_in">UIView</span> *myView;</span><br><span class="line"><span class="keyword">@property</span> (<span class="keyword">nonatomic</span>, <span class="keyword">weak</span>) <span class="keyword">id</span><<span class="built_in">UIApplicationDelegate</span>> delegate;</span><br><span class="line"><span class="keyword">@property</span> (<span class="keyword">nonatomic</span>, <span class="keyword">assign</span>) <span class="built_in">UIView</span> *myWrongView;<span class="comment">//1</span></span><br><span class="line"><span class="keyword">@property</span> (<span class="keyword">nonatomic</span>, <span class="keyword">assign</span>) <span class="built_in">BOOL</span> *myFlag;</span><br><span class="line"><span class="keyword">@property</span> (<span class="keyword">nonatomic</span>, <span class="keyword">copy</span>) <span class="built_in">NSString</span> *myName;</span><br><span class="line"><span class="keyword">@property</span> (<span class="keyword">nonatomic</span>, <span class="keyword">retain</span>) <span class="built_in">UIView</span> *mySecondView;<span class="comment">//2</span></span><br><span class="line"><span class="keyword">@property</span> (<span class="keyword">nonatomic</span>, <span class="keyword">unsafe_unretained</span>) <span class="built_in">UIView</span> *myThirdView;</span><br></pre></td></tr></table></figure>
<ol>
<li>错误将assign用于指针</li>
<li>老古董用法,现在基本不会出现</li>
</ol>
<h2 id="内存管理规则"><a href="#内存管理规则" class="headerlink" title="内存管理规则"></a>内存管理规则</h2><p>苹果<a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html#//apple_ref/doc/uid/20000994-BAJHFBGH" target="_blank" rel="noopener">官方文档</a>中描述,内存管理的四个基本规则</p>
<ul>
<li>你拥有所有自己创建的对象,如通过new、alloc、copy或mutableCopy创建</li>
<li>用MRC中的retain或ARC中的__strong引用来拥有任何对象的持有关系</li>
<li>当不需要某个对象时,使用MRC中的release来放弃对该对象的持有关系,而在ARC中无需任何特殊操作,持有关系会在对象失去最后的引用(通常为方法中的最后一行代码)时销毁回收。</li>
</ul>
<h1 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h1><ul>
<li>《High Performance iOS Apps》</li>
<li>《Objective-C高级编程》</li>
</ul>
]]></content>
<categories>
<category>iOS</category>
</categories>
<tags>
<tag>iOS</tag>
</tags>
</entry>
<entry>
<title>逆向获取Block对应函数入口和函数签名</title>
<url>/2020/01/14/%E9%80%86%E5%90%91%E8%8E%B7%E5%8F%96Block%E5%AF%B9%E5%BA%94%E5%87%BD%E6%95%B0%E5%85%A5%E5%8F%A3%E5%92%8C%E5%87%BD%E6%95%B0%E7%AD%BE%E5%90%8D/</url>
<content><![CDATA[<h1 id="逆向获取block对应函数入口和函数签名"><a href="#逆向获取block对应函数入口和函数签名" class="headerlink" title="逆向获取block对应函数入口和函数签名"></a>逆向获取block对应函数入口和函数签名</h1><p>在逆向分析App的时候,有时会遇到某个关键方法中传入一个block参数来做回调,class-dump无法解析出block的类型以及函数签名。了解过block本质及其内存模型后,就可以通过lldb动态调试来获取目标信息</p>
<h1 id="block的内存结构"><a href="#block的内存结构" class="headerlink" title="block的内存结构"></a>block的内存结构</h1><p>在LLVM文档中,找到block的实现规范<a href="http://clang.llvm.org/docs/Block-ABI-Apple.html" target="_blank" rel="noopener">Block Implementation Specification</a>,找到block内存结构的定义</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Block_literal_1</span> {</span></span><br><span class="line"> <span class="keyword">void</span> *isa; <span class="comment">// initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock</span></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> (*invoke)(<span class="keyword">void</span> *, ...);</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">Block_descriptor_1</span> {</span></span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">long</span> <span class="keyword">int</span> reserved; <span class="comment">// NULL</span></span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">long</span> <span class="keyword">int</span> <span class="built_in">size</span>; <span class="comment">// sizeof(struct Block_literal_1)</span></span><br><span class="line"> <span class="comment">// optional helper functions</span></span><br><span class="line"> <span class="keyword">void</span> (*copy_helper)(<span class="keyword">void</span> *dst, <span class="keyword">void</span> *src); <span class="comment">// IFF (1<<25)</span></span><br><span class="line"> <span class="keyword">void</span> (*dispose_helper)(<span class="keyword">void</span> *src); <span class="comment">// IFF (1<<25)</span></span><br><span class="line"> <span class="comment">// required ABI.2010.3.16</span></span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">char</span> *signature; <span class="comment">// IFF (1<<30)</span></span><br><span class="line"> } *descriptor;</span><br><span class="line"> <span class="comment">// imported variables</span></span><br><span class="line">};</span><br></pre></td></tr></table></figure>
<p>其中block对应的实现函数地址入口和函数签名分别在<code>void (*invoke)(void *, ...);</code>和<code>descriptor</code>中的<code>const char *signature;</code>中保存</p>
<h1 id="实例"><a href="#实例" class="headerlink" title="实例"></a>实例</h1><p>某app中的某个带block参数的方法</p>
<p><code>- (void)parseRequest:(id)arg1 result:(id)arg2 completion:(id)arg3;</code></p>
<p>祭出debugserver和lldb</p>
<br>
<div align=center> <img src="https://kinkenyuen.oss-cn-shenzhen.aliyuncs.com/images/debugserver启动.png" width="800" /> <div style="color:orange; border-bottom: 1px solid #d9d9d9; display: inline-block; color: #999; padding: 2px;">debugserver启动</div> </div>
<br>
<div align=center> <img src="https://kinkenyuen.oss-cn-shenzhen.aliyuncs.com/images/LLDB connect.png" width="800" /> <div style="color:orange; border-bottom: 1px solid #d9d9d9; display: inline-block; color: #999; padding: 2px;">LLDB connect</div> </div>
<br>
<div align=center> <img src="https://kinkenyuen.oss-cn-shenzhen.aliyuncs.com/images/定位目标方法内存地址.png" width="800" /> <div style="color:orange; border-bottom: 1px solid #d9d9d9; display: inline-block; color: #999; padding: 2px;">定位目标方法内存地址</div> </div>
<br>
<div align=center> <img src="https://kinkenyuen.oss-cn-shenzhen.aliyuncs.com/images/断点并触发.png" width="800" /> <div style="color:orange; border-bottom: 1px solid #d9d9d9; display: inline-block; color: #999; padding: 2px;">断点并触发</div> </div>
<br>
<div align=center> <img src="https://kinkenyuen.oss-cn-shenzhen.aliyuncs.com/images/获取block参数对象.png" width="800" /> <div style="color:orange; border-bottom: 1px solid #d9d9d9; display: inline-block; color: #999; padding: 2px;">获取block参数对象</div> </div>
<br>
<div align=center> <img src="https://kinkenyuen.oss-cn-shenzhen.aliyuncs.com/images/block内存实例.png" width="800" /> <div style="color:orange; border-bottom: 1px solid #d9d9d9; display: inline-block; color: #999; padding: 2px;">block内存实例</div> </div>
<br>
<div align=center> <img src="https://kinkenyuen.oss-cn-shenzhen.aliyuncs.com/images/检查block是否有函数签名.png" width="800" /> <div style="color:orange; border-bottom: 1px solid #d9d9d9; display: inline-block; color: #999; padding: 2px;">检查block是否有函数签名</div> </div>
<br>
<div align=center> <img src="https://kinkenyuen.oss-cn-shenzhen.aliyuncs.com/images/检查block是否有copy和dispose函数指针.png" width="800" /><div style="color:orange; border-bottom: 1px solid #d9d9d9; display: inline-block; color: #999; padding: 2px;">检查block是否有copy和dispose函数指针</div> </div>
<br>
<div align=center> <img src="https://kinkenyuen.oss-cn-shenzhen.aliyuncs.com/images/descriptor布局.png" width="800" /> <div style="color:orange; border-bottom: 1px solid #d9d9d9; display: inline-block; color: #999; padding: 2px;">descriptor布局</div> </div>
<br>
<div align=center> <img src="https://kinkenyuen.oss-cn-shenzhen.aliyuncs.com/images/函数签名信息.png" width="800" /> <div style="color:orange; border-bottom: 1px solid #d9d9d9; display: inline-block; color: #999; padding: 2px;">函数签名信息</div> </div>
<br>
<p>最后得到的函数签名字符编码,可在官方<a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html" target="_blank" rel="noopener">Type Encoding</a>中找到</p>
<p>从图中可以看出,block参数个数为3,block 的返回值为<code>void</code>,第一个参数为block自身,通常在block语法参数列表省略,第二个参数为<code>NSArray</code>类型,第三个参数为<code>BOOL</code>类型</p>
<h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="http://www.swiftyper.com/2016/12/16/debuging-objective-c-blocks-in-lldb/" target="_blank" rel="noopener">通过逆向深入理解 Block 的内存模型</a></p>
]]></content>
<categories>
<category>逆向</category>
</categories>
<tags>
<tag>逆向</tag>
</tags>
</entry>
<entry>
<title>Block</title>
<url>/2020/01/11/Block/</url>
<content><![CDATA[<h1 id="block"><a href="#block" class="headerlink" title="block"></a>block</h1><p>block是OC对闭包的实现,闭包的定义如下:</p>
<blockquote>
<p>在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。</p>
</blockquote>
<p>block用来实现匿名函数的特性,是一种特殊数据类型,可以定义为变量、作为参数、作为返回值,一般用来保存一段代码,在需要的时候回调,在iOS中广泛使用,如GCD、动画变换、网络回调等等</p>
<h1 id="block基础"><a href="#block基础" class="headerlink" title="block基础"></a>block基础</h1><p>表达式</p>
<p><code>returnType (^blockName)(parameterTypes)</code></p>
<h2 id="block变量声明"><a href="#block变量声明" class="headerlink" title="block变量声明"></a>block变量声明</h2><figure class="highlight objc"><table><tr><td class="code"><pre><span class="line"><span class="comment">//声明无返回值,参数为空,名为aBlock的block变量</span></span><br><span class="line"><span class="keyword">void</span>(^aBlock)(<span class="keyword">void</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">//声明无返回值,参数为一个NSString对象,名为aBlock的block变量</span></span><br><span class="line"><span class="keyword">void</span>(^aBlock)(<span class="built_in">NSString</span> *aString);</span><br><span class="line"></span><br><span class="line"><span class="comment">//其中形参名可省略</span></span><br><span class="line"><span class="keyword">void</span>(^aBlock)(<span class="built_in">NSString</span> *);</span><br></pre></td></tr></table></figure>
<h2 id="block变量赋值"><a href="#block变量赋值" class="headerlink" title="block变量赋值"></a>block变量赋值</h2><p>本质 : block变量指向block结构体实例</p>
<figure class="highlight armasm"><table><tr><td class="code"><pre><span class="line"><span class="keyword">block变量 </span>= ^(参数列表){</span><br><span class="line"> //<span class="meta">code</span> </span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<figure class="highlight objc"><table><tr><td class="code"><pre><span class="line">aBlock = ^(<span class="built_in">NSString</span> *aString){</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"aString:%@"</span>,aString);</span><br><span class="line">};</span><br></pre></td></tr></table></figure>
<blockquote>
<p>block变量的赋值格式可以是: <code>block变量 = ^返回值类型(参数列表){函数体};</code>不过通常情况下都将返回值类型省略,因为编译器可以从存储代码块的变量中确定返回值的类型</p>
</blockquote>
<h2 id="声明block变量的同时进行赋值"><a href="#声明block变量的同时进行赋值" class="headerlink" title="声明block变量的同时进行赋值"></a>声明block变量的同时进行赋值</h2><p>在Xcode中输入<code>inlineBlock</code>代码提示如下:</p>
<figure class="highlight objc"><table><tr><td class="code"><pre><span class="line">returnType(^blockName)(parameterTypes) = ^(parameters) {</span><br><span class="line"> statements</span><br><span class="line">};</span><br></pre></td></tr></table></figure>
<figure class="highlight objc"><table><tr><td class="code"><pre><span class="line"><span class="keyword">void</span>(^aBlock)(<span class="built_in">NSString</span> *) = ^(<span class="built_in">NSString</span> *aString) {</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"aString:%@"</span>,aString);</span><br><span class="line">};</span><br></pre></td></tr></table></figure>
<h2 id="调用block"><a href="#调用block" class="headerlink" title="调用block"></a>调用block</h2><p>通常传入某类实例内部以供回调,一般不会在同一处给block变量赋值以及调用,开发中常用作传递信息(也就是常说的回调),此处仅用于展示。</p>
<figure class="highlight objc"><table><tr><td class="code"><pre><span class="line"><span class="keyword">void</span>(^aBlock)(<span class="built_in">NSString</span> *) = ^(<span class="built_in">NSString</span> *aString) {</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"aString:%@"</span>,aString);</span><br><span class="line">};</span><br><span class="line"> </span><br><span class="line">aBlock(<span class="string">@"kinken"</span>);</span><br></pre></td></tr></table></figure>
<h2 id="block类型定义"><a href="#block类型定义" class="headerlink" title="block类型定义"></a>block类型定义</h2><p>使用<code>typedef</code>使block的类型定义变得更加清晰,在Xcode输入<code>typedefBlock</code>代码提示快捷键</p>
<p><code>typedef returnType(^name)(arguments);</code></p>
<p><code>typedef void(^aBlockType)(NSString *);</code></p>
<p><code>aBlockType</code>即表示<code>void(^)(NSString *)</code>类型</p>
<figure class="highlight objc"><table><tr><td class="code"><pre><span class="line">aBlockType aBlock = ^(<span class="built_in">NSString</span> *aString) {</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"aString:%@"</span>,aString);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="block作函数参数"><a href="#block作函数参数" class="headerlink" title="block作函数参数"></a>block作函数参数</h2><h3 id="C函数参数"><a href="#C函数参数" class="headerlink" title="C函数参数"></a>C函数参数</h3><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="comment">//一个使用block作为c函数形参</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">blockPara</span><span class="params">(NSString *(^aBlock)(NSString *))</span> </span>{</span><br><span class="line"> NSLog(@<span class="string">"log:%@"</span>,aBlock(@<span class="string">"ken"</span>));</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//定义block变量并赋值</span></span><br><span class="line">NSString * (^aBlock)(NSString *) = ^(NSString *aString) {</span><br><span class="line"> <span class="keyword">return</span> aString;</span><br><span class="line">};</span><br><span class="line"> </span><br><span class="line"><span class="comment">//调用c函数,传入block</span></span><br><span class="line">blockPara(aBlock);</span><br></pre></td></tr></table></figure>
<h3 id="OC函数参数"><a href="#OC函数参数" class="headerlink" title="OC函数参数"></a>OC函数参数</h3><figure class="highlight objc"><table><tr><td class="code"><pre><span class="line">- (<span class="keyword">void</span>)blockPara:(<span class="built_in">NSString</span> * (^)(<span class="built_in">NSString</span> *))aBlock {</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"log:%@"</span>,aBlock(<span class="string">@"ken"</span>));</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">- (<span class="keyword">void</span>)touchesBegan:(<span class="built_in">NSSet</span><<span class="built_in">UITouch</span> *> *)touches withEvent:(<span class="built_in">UIEvent</span> *)event {</span><br><span class="line"> <span class="built_in">NSString</span> * (^aBlock)(<span class="built_in">NSString</span> *) = ^(<span class="built_in">NSString</span> *aString) {</span><br><span class="line"> <span class="keyword">return</span> aString;</span><br><span class="line"> };</span><br><span class="line"> </span><br><span class="line"> [<span class="keyword">self</span> blockPara:aBlock];</span><br><span class="line"> <span class="comment">//或者这样直接传block块,比较常用</span></span><br><span class="line"> [<span class="keyword">self</span> blockPara:^<span class="built_in">NSString</span> *(<span class="built_in">NSString</span> *aString) {</span><br><span class="line"> <span class="keyword">return</span> aString;</span><br><span class="line"> }];</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="block内访问外部局部变量"><a href="#block内访问外部局部变量" class="headerlink" title="block内访问外部局部变量"></a>block内访问外部局部变量</h2><p>在block中可以访问block之外的局部变量,默认情况下,block访问的仅仅是变量的瞬时值,即在声明block变量并赋值之后,访问的局部变量值已经确定,若在block调用前修改局部变量的值,不会影响到block内访问的那个值</p>
<figure class="highlight objc"><table><tr><td class="code"><pre><span class="line">- (<span class="keyword">void</span>)touchesBegan:(<span class="built_in">NSSet</span><<span class="built_in">UITouch</span> *> *)touches withEvent:(<span class="built_in">UIEvent</span> *)event {</span><br><span class="line"> <span class="keyword">int</span> var = <span class="number">100</span>;</span><br><span class="line"> <span class="keyword">void</span>(^aBlock)(<span class="keyword">void</span>) = ^(<span class="keyword">void</span>) {</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"%d"</span>,var);</span><br><span class="line"> };</span><br><span class="line"> var = <span class="number">0</span>;</span><br><span class="line"> </span><br><span class="line"> aBlock();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//控制台</span></span><br><span class="line"><span class="number">2019</span><span class="number">-02</span><span class="number">-15</span> <span class="number">17</span>:<span class="number">34</span>:<span class="number">10.061709</span>+<span class="number">0800</span> Test[<span class="number">4963</span>:<span class="number">231428</span>] <span class="number">100</span></span><br></pre></td></tr></table></figure>
<h2 id="block内修改外部局部变量"><a href="#block内修改外部局部变量" class="headerlink" title="block内修改外部局部变量"></a>block内修改外部局部变量</h2><p>在block中不能直接修改外部局部变量,会报一个<code>Variable is not assignable (missing __block type specifier)</code>错误。</p>
<figure class="highlight objc"><table><tr><td class="code"><pre><span class="line">- (<span class="keyword">void</span>)touchesBegan:(<span class="built_in">NSSet</span><<span class="built_in">UITouch</span> *> *)touches withEvent:(<span class="built_in">UIEvent</span> *)event {</span><br><span class="line"> <span class="keyword">int</span> var = <span class="number">100</span>;</span><br><span class="line"> <span class="keyword">void</span>(^aBlock)(<span class="keyword">void</span>) = ^(<span class="keyword">void</span>) {</span><br><span class="line"> var++;<span class="comment">//Variable is not assignable (missing __block type specifier)</span></span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"var:%d"</span>,var);</span><br><span class="line"> };</span><br><span class="line"> var = <span class="number">0</span>;</span><br><span class="line"> </span><br><span class="line"> aBlock();</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>若需要在block内部使用并修改block外部的局部变量值,需要使用<code>__block</code>修饰符修饰该局部变量。原因可以在<a href="#jump">__block修饰符</a>找到</p>
<figure class="highlight objc"><table><tr><td class="code"><pre><span class="line">- (<span class="keyword">void</span>)touchesBegan:(<span class="built_in">NSSet</span><<span class="built_in">UITouch</span> *> *)touches withEvent:(<span class="built_in">UIEvent</span> *)event {</span><br><span class="line"> __block <span class="keyword">int</span> var = <span class="number">100</span>;</span><br><span class="line"> <span class="keyword">void</span>(^aBlock)(<span class="keyword">void</span>) = ^(<span class="keyword">void</span>) {</span><br><span class="line"> var++;</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"var:%d"</span>,var);</span><br><span class="line"> };</span><br><span class="line"> var = <span class="number">0</span>;</span><br><span class="line"> </span><br><span class="line"> aBlock();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//控制台</span></span><br><span class="line"><span class="number">2019</span><span class="number">-02</span><span class="number">-15</span> <span class="number">19</span>:<span class="number">03</span>:<span class="number">19.104961</span>+<span class="number">0800</span> Test[<span class="number">6996</span>:<span class="number">285534</span>] var:<span class="number">1</span></span><br></pre></td></tr></table></figure>
<h2 id="block内访问与修改全局变量"><a href="#block内访问与修改全局变量" class="headerlink" title="block内访问与修改全局变量"></a>block内访问与修改全局变量</h2><figure class="highlight objc"><table><tr><td class="code"><pre><span class="line"><span class="keyword">int</span> var = <span class="number">100</span>;</span><br><span class="line"></span><br><span class="line">- (<span class="keyword">void</span>)touchesBegan:(<span class="built_in">NSSet</span><<span class="built_in">UITouch</span> *> *)touches withEvent:(<span class="built_in">UIEvent</span> *)event {</span><br><span class="line"> <span class="keyword">void</span>(^aBlock)(<span class="keyword">void</span>) = ^(<span class="keyword">void</span>) {</span><br><span class="line"> var++;</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"var:%d"</span>,var);</span><br><span class="line"> };</span><br><span class="line"> aBlock();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//控制台</span></span><br><span class="line"><span class="number">2019</span><span class="number">-02</span><span class="number">-15</span> <span class="number">19</span>:<span class="number">08</span>:<span class="number">06.800896</span>+<span class="number">0800</span> Test[<span class="number">7132</span>:<span class="number">289302</span>] var:<span class="number">101</span></span><br></pre></td></tr></table></figure>
<p>block中能直接访问并修改全局变量,原因在于<code>程序装载到内存后,全局变量存储在全局数据区,生命周期直至程序结束</code>,当然这仅是很浅显的原因,具体涉及到block是<strong>如何捕获变量</strong></p>
<h2 id="block内访问与修改静态变量"><a href="#block内访问与修改静态变量" class="headerlink" title="block内访问与修改静态变量"></a>block内访问与修改静态变量</h2><figure class="highlight objc"><table><tr><td class="code"><pre><span class="line"><span class="comment">//static int var = 100;</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">@implementation</span> <span class="title">ViewController</span></span></span><br><span class="line"></span><br><span class="line">- (<span class="keyword">void</span>)touchesBegan:(<span class="built_in">NSSet</span><<span class="built_in">UITouch</span> *> *)touches withEvent:(<span class="built_in">UIEvent</span> *)event {</span><br><span class="line"> <span class="keyword">static</span> <span class="keyword">int</span> var = <span class="number">100</span>;</span><br><span class="line"> <span class="keyword">void</span>(^aBlock)(<span class="keyword">void</span>) = ^(<span class="keyword">void</span>) {</span><br><span class="line"> var++;</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"var:%d"</span>,var);</span><br><span class="line"> };</span><br><span class="line"> var = <span class="number">0</span>;</span><br><span class="line"> aBlock();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">@end</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//控制台</span></span><br><span class="line"><span class="number">2019</span><span class="number">-02</span><span class="number">-15</span> <span class="number">19</span>:<span class="number">10</span>:<span class="number">59.184450</span>+<span class="number">0800</span> Test[<span class="number">7208</span>:<span class="number">291713</span>] var:<span class="number">1</span></span><br></pre></td></tr></table></figure>
<p>block中能直接访问并修改静态变量,原因同样涉及到block是<strong>如何捕获变量</strong></p>
<h2 id="block内捕获OC对象"><a href="#block内捕获OC对象" class="headerlink" title="block内捕获OC对象"></a>block内捕获OC对象</h2><figure class="highlight objc"><table><tr><td class="code"><pre><span class="line">- (<span class="keyword">void</span>)touchesBegan:(<span class="built_in">NSSet</span><<span class="built_in">UITouch</span> *> *)touches withEvent:(<span class="built_in">UIEvent</span> *)event {</span><br><span class="line"> <span class="built_in">NSMutableArray</span> *mArray = [[<span class="built_in">NSMutableArray</span> alloc] init];</span><br><span class="line"> <span class="keyword">void</span>(^aBlock)(<span class="keyword">void</span>) = ^(<span class="keyword">void</span>) {</span><br><span class="line"> <span class="built_in">NSObject</span> *obj = [[<span class="built_in">NSObject</span> alloc] init];</span><br><span class="line"> [mArray addObject:obj];</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"array: %@"</span>,mArray);</span><br><span class="line"> };</span><br><span class="line"> aBlock();</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>这样是可行的,因为此处block捕获的是<code>结构体实例指针</code>,虽然对局部变量mArray赋值会产生编译错误,但使用捕获的值不会有任何问题。以下对局部变量mArray赋值则会产生错误</p>
<figure class="highlight objc"><table><tr><td class="code"><pre><span class="line">- (<span class="keyword">void</span>)touchesBegan:(<span class="built_in">NSSet</span><<span class="built_in">UITouch</span> *> *)touches withEvent:(<span class="built_in">UIEvent</span> *)event {</span><br><span class="line"> <span class="built_in">NSMutableArray</span> *mArray = [[<span class="built_in">NSMutableArray</span> alloc] init];</span><br><span class="line"> <span class="keyword">void</span>(^aBlock)(<span class="keyword">void</span>) = ^(<span class="keyword">void</span>) {</span><br><span class="line"> mArray = [<span class="built_in">NSMutableArray</span> array]; <span class="comment">//error</span></span><br><span class="line"> };</span><br><span class="line"> aBlock();</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="block捕获C数组"><a href="#block捕获C数组" class="headerlink" title="block捕获C数组"></a>block捕获C数组</h2><p>block内捕获C数组必须以指针形式捕获,<strong>因为block没有实现对C数组的捕获</strong></p>
<figure class="highlight objc"><table><tr><td class="code"><pre><span class="line">- (<span class="keyword">void</span>)touchesBegan:(<span class="built_in">NSSet</span><<span class="built_in">UITouch</span> *> *)touches withEvent:(<span class="built_in">UIEvent</span> *)event {</span><br><span class="line"><span class="comment">// char *word = "Hello"; //correct</span></span><br><span class="line"> <span class="keyword">char</span> word[] = <span class="string">"Hello"</span>; <span class="comment">//error</span></span><br><span class="line"> <span class="keyword">void</span>(^aBlock)(<span class="keyword">void</span>) = ^(<span class="keyword">void</span>) {</span><br><span class="line"> printf(<span class="string">"word : %s"</span>,word);</span><br><span class="line"> };</span><br><span class="line"> aBlock();</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h1 id="block底层"><a href="#block底层" class="headerlink" title="block底层"></a>block底层</h1><h2 id="代码转换"><a href="#代码转换" class="headerlink" title="代码转换"></a>代码转换</h2><p>编译器(Xcode使用clang)在处理block语法时,会将其转换为普通的C语言代码来处理,而编译器本身支持通过命令选项将block语法转换为C/C++源代码表示,因此可从此转换入手分析。</p>
<figure class="highlight objc"><table><tr><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">void</span> (^aBlock)(<span class="keyword">void</span>) = ^ {</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"block body"</span>);</span><br><span class="line"> };</span><br><span class="line"> </span><br><span class="line"> aBlock();</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><code>clang -rewrite-objc main.m</code></p>
<p>通过转换生成的代码很多,关注以下转换代码</p>
<figure class="highlight c"><table><tr><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><span class="line"></span><br><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"> __main_block_impl_0(<span class="keyword">void</span> *fp, struct __main_block_desc_0 *desc, <span class="keyword">int</span> flags=<span class="number">0</span>) {</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><span class="line"></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">void</span> __main_block_func_0(struct __main_block_impl_0 *__cself) {</span><br><span class="line"> NSLog((NSString *)&__NSConstantStringImpl__var_folders_ny_1k2gy9p127s6z2jr11ffpx780000gn_T_main_2363f0_mi_0);</span><br><span class="line">}</span><br><span class="line"></span><br><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="keyword">sizeof</span>(struct __main_block_impl_0)};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">const</span> <span class="keyword">char</span> * argv[])</span> </span>{</span><br><span class="line"> <span class="keyword">void</span> (*aBlock)(<span class="keyword">void</span>) = ((<span class="keyword">void</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><br><span class="line"> ((<span class="keyword">void</span> (*)(__block_impl *))((__block_impl *)aBlock)->FuncPtr)((__block_impl *)aBlock);</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>
<h2 id="分析"><a href="#分析" class="headerlink" title="分析"></a>分析</h2><h3 id="数据结构与实例化"><a href="#数据结构与实例化" class="headerlink" title="数据结构与实例化"></a>数据结构与实例化</h3><p>block语法块转换如下:</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line">^ { NSLog(@<span class="string">"block body"</span>);};</span><br></pre></td></tr></table></figure>
<figure class="highlight c"><table><tr><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><br><span class="line"> NSLog((NSString *)&__NSConstantStringImpl__var_folders_ny_1k2gy9p127s6z2jr11ffpx780000gn_T_main_2363f0_mi_0);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>可以看出<strong>block语法块内的函数体</strong>实际上被作为<strong>C函数</strong>处理</p>
<ul>
<li>函数名由编译器clang根据block语法块出现在的函数(此处为<code>main</code>)与出现顺序给出</li>
<li>函数参数<code>__cself</code>是<code>__main_block_impl_0</code>类型的结构体指针,它相当于C++实例方法中指向实例自身变量<code>this</code>或OC实例方法中指向对象自身的变量<code>self</code>,<code>__cself</code>则指向block语法块结构体实例(指向block值的变量)</li>
</ul>
<hr>
<p>上述提到block语法块结构体实例,指的是一个block语法块底层是通过<strong>结构体定义</strong>以及<strong>实例化</strong>来处理</p>
<p>回顾main函数看block语法块</p>
<figure class="highlight objc"><table><tr><td class="code"><pre><span class="line"><span class="keyword">void</span> (^aBlock)(<span class="keyword">void</span>) = ^ {</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"block body"</span>);</span><br><span class="line">};</span><br></pre></td></tr></table></figure>
<p>对应转换代码:</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="keyword">void</span> (*aBlock)(<span class="keyword">void</span>) = ((<span class="keyword">void</span> (*)())&__main_block_impl_0((<span class="keyword">void</span> *)__main_block_func_0, &__main_block_desc_0_DATA));</span><br></pre></td></tr></table></figure>
<p>不难看出,此处实例化了一个<code>__main_block_impl_0</code>结构体并将地址(指针)赋值给aBlock变量(也就是上述提到的<code>__cself</code>指针,他们指向同一个结构体实例)</p>
<p>回顾关注的转换代码,可将三个结构体定义合并成一个,如下:</p>
<figure class="highlight c"><table><tr><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="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 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_impl_0(<span class="keyword">void</span> *fp, struct __main_block_desc_0 *desc, <span class="keyword">int</span> flags=<span class="number">0</span>) {</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>
<table>
<thead>
<tr>
<th>字段</th>
<th>含义</th>
</tr>
</thead>
<tbody><tr>
<td>isa</td>
<td>isa指针,实现实例对象相关的功能,OC对象都有该指针</td>
</tr>
<tr>
<td>Flags</td>
<td>用于按 bit 位表示一些 block 的附加信息,block copy的时候会用到</td>
</tr>
<tr>
<td>Reserved</td>
<td>保留字段,用于今后版本升级</td>
</tr>
<tr>
<td>FuncPtr</td>
<td>函数指针,指向具体的 block 实现的函数调用地址</td>
</tr>
<tr>
<td>Block_size</td>
<td>block实例的大小,也就是<code>__main_block_impl_0</code>结构体的大小</td>
</tr>
</tbody></table>
<p>这是编译器生成的对block描述的数据结构体,更详细的定义可在Apple开源代码<a href="https://opensource.apple.com/source/libclosure/libclosure-65/Block_private.h" target="_blank" rel="noopener">Block_private.h</a>中找到</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Block_private.h</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * SPI for Blocks</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * Copyright (c) 2008-2010 Apple Inc. All rights reserved.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * @APPLE_LLVM_LICENSE_HEADER@</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifndef</span> _BLOCK_PRIVATE_H_</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> _BLOCK_PRIVATE_H_</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><Availability.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><AvailabilityMacros.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><TargetConditionals.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdbool.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdint.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdio.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><Block.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">if</span> __cplusplus</span></span><br><span class="line"><span class="keyword">extern</span> <span class="string">"C"</span> {</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// Values for Block_layout->flags to describe block objects</span></span><br><span class="line"><span class="keyword">enum</span> {</span><br><span class="line"> BLOCK_DEALLOCATING = (<span class="number">0x0001</span>), <span class="comment">// runtime</span></span><br><span class="line"> BLOCK_REFCOUNT_MASK = (<span class="number">0xfffe</span>), <span class="comment">// runtime</span></span><br><span class="line"> BLOCK_NEEDS_FREE = (<span class="number">1</span> << <span class="number">24</span>), <span class="comment">// runtime</span></span><br><span class="line"> BLOCK_HAS_COPY_DISPOSE = (<span class="number">1</span> << <span class="number">25</span>), <span class="comment">// compiler</span></span><br><span class="line"> BLOCK_HAS_CTOR = (<span class="number">1</span> << <span class="number">26</span>), <span class="comment">// compiler: helpers have C++ code</span></span><br><span class="line"> BLOCK_IS_GC = (<span class="number">1</span> << <span class="number">27</span>), <span class="comment">// runtime</span></span><br><span class="line"> BLOCK_IS_GLOBAL = (<span class="number">1</span> << <span class="number">28</span>), <span class="comment">// compiler</span></span><br><span class="line"> BLOCK_USE_STRET = (<span class="number">1</span> << <span class="number">29</span>), <span class="comment">// compiler: undefined if !BLOCK_HAS_SIGNATURE</span></span><br><span class="line"> BLOCK_HAS_SIGNATURE = (<span class="number">1</span> << <span class="number">30</span>), <span class="comment">// compiler</span></span><br><span class="line"> BLOCK_HAS_EXTENDED_LAYOUT=(<span class="number">1</span> << <span class="number">31</span>) <span class="comment">// compiler</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> BLOCK_DESCRIPTOR_1 1</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Block_descriptor_1</span> {</span></span><br><span class="line"> <span class="keyword">uintptr_t</span> reserved;</span><br><span class="line"> <span class="keyword">uintptr_t</span> <span class="built_in">size</span>;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> BLOCK_DESCRIPTOR_2 1</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Block_descriptor_2</span> {</span></span><br><span class="line"> <span class="comment">// requires BLOCK_HAS_COPY_DISPOSE</span></span><br><span class="line"> <span class="keyword">void</span> (*copy)(<span class="keyword">void</span> *dst, <span class="keyword">const</span> <span class="keyword">void</span> *src);</span><br><span class="line"> <span class="keyword">void</span> (*dispose)(<span class="keyword">const</span> <span class="keyword">void</span> *);</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> BLOCK_DESCRIPTOR_3 1</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Block_descriptor_3</span> {</span></span><br><span class="line"> <span class="comment">// requires BLOCK_HAS_SIGNATURE</span></span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">char</span> *signature;</span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">char</span> *layout; <span class="comment">// contents depend on BLOCK_HAS_EXTENDED_LAYOUT</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Block_layout</span> {</span></span><br><span class="line"> <span class="keyword">void</span> *isa;</span><br><span class="line"> <span class="keyword">volatile</span> <span class="keyword">int32_t</span> flags; <span class="comment">// contains ref count</span></span><br><span class="line"> <span class="keyword">int32_t</span> reserved; </span><br><span class="line"> <span class="keyword">void</span> (*invoke)(<span class="keyword">void</span> *, ...);</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">Block_descriptor_1</span> *<span class="title">descriptor</span>;</span></span><br><span class="line"> <span class="comment">// imported variables</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// Values for Block_byref->flags to describe __block variables</span></span><br><span class="line"><span class="keyword">enum</span> {</span><br><span class="line"> <span class="comment">// Byref refcount must use the same bits as Block_layout's refcount.</span></span><br><span class="line"> <span class="comment">// BLOCK_DEALLOCATING = (0x0001), // runtime</span></span><br><span class="line"> <span class="comment">// BLOCK_REFCOUNT_MASK = (0xfffe), // runtime</span></span><br><span class="line"></span><br><span class="line"> BLOCK_BYREF_LAYOUT_MASK = (<span class="number">0xf</span> << <span class="number">28</span>), <span class="comment">// compiler</span></span><br><span class="line"> BLOCK_BYREF_LAYOUT_EXTENDED = ( <span class="number">1</span> << <span class="number">28</span>), <span class="comment">// compiler</span></span><br><span class="line"> BLOCK_BYREF_LAYOUT_NON_OBJECT = ( <span class="number">2</span> << <span class="number">28</span>), <span class="comment">// compiler</span></span><br><span class="line"> BLOCK_BYREF_LAYOUT_STRONG = ( <span class="number">3</span> << <span class="number">28</span>), <span class="comment">// compiler</span></span><br><span class="line"> BLOCK_BYREF_LAYOUT_WEAK = ( <span class="number">4</span> << <span class="number">28</span>), <span class="comment">// compiler</span></span><br><span class="line"> BLOCK_BYREF_LAYOUT_UNRETAINED = ( <span class="number">5</span> << <span class="number">28</span>), <span class="comment">// compiler</span></span><br><span class="line"></span><br><span class="line"> BLOCK_BYREF_IS_GC = ( <span class="number">1</span> << <span class="number">27</span>), <span class="comment">// runtime</span></span><br><span class="line"></span><br><span class="line"> BLOCK_BYREF_HAS_COPY_DISPOSE = ( <span class="number">1</span> << <span class="number">25</span>), <span class="comment">// compiler</span></span><br><span class="line"> BLOCK_BYREF_NEEDS_FREE = ( <span class="number">1</span> << <span class="number">24</span>), <span class="comment">// runtime</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Block_byref</span> {</span></span><br><span class="line"> <span class="keyword">void</span> *isa;</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">Block_byref</span> *<span class="title">forwarding</span>;</span></span><br><span class="line"> <span class="keyword">volatile</span> <span class="keyword">int32_t</span> flags; <span class="comment">// contains ref count</span></span><br><span class="line"> <span class="keyword">uint32_t</span> <span class="built_in">size</span>;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Block_byref_2</span> {</span></span><br><span class="line"> <span class="comment">// requires BLOCK_BYREF_HAS_COPY_DISPOSE</span></span><br><span class="line"> <span class="keyword">void</span> (*byref_keep)(struct Block_byref *dst, struct Block_byref *src);</span><br><span class="line"> <span class="keyword">void</span> (*byref_destroy)(struct Block_byref *);</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Block_byref_3</span> {</span></span><br><span class="line"> <span class="comment">// requires BLOCK_BYREF_LAYOUT_EXTENDED</span></span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">char</span> *layout;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// Extended layout encoding.</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Values for Block_descriptor_3->layout with BLOCK_HAS_EXTENDED_LAYOUT</span></span><br><span class="line"><span class="comment">// and for Block_byref_3->layout with BLOCK_BYREF_LAYOUT_EXTENDED</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// If the layout field is less than 0x1000, then it is a compact encoding </span></span><br><span class="line"><span class="comment">// of the form 0xXYZ: X strong pointers, then Y byref pointers, </span></span><br><span class="line"><span class="comment">// then Z weak pointers.</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// If the layout field is 0x1000 or greater, it points to a </span></span><br><span class="line"><span class="comment">// string of layout bytes. Each byte is of the form 0xPN.</span></span><br><span class="line"><span class="comment">// Operator P is from the list below. Value N is a parameter for the operator.</span></span><br><span class="line"><span class="comment">// Byte 0x00 terminates the layout; remaining block data is non-pointer bytes.</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">enum</span> {</span><br><span class="line"> BLOCK_LAYOUT_ESCAPE = <span class="number">0</span>, <span class="comment">// N=0 halt, rest is non-pointer. N!=0 reserved.</span></span><br><span class="line"> BLOCK_LAYOUT_NON_OBJECT_BYTES = <span class="number">1</span>, <span class="comment">// N bytes non-objects</span></span><br><span class="line"> BLOCK_LAYOUT_NON_OBJECT_WORDS = <span class="number">2</span>, <span class="comment">// N words non-objects</span></span><br><span class="line"> BLOCK_LAYOUT_STRONG = <span class="number">3</span>, <span class="comment">// N words strong pointers</span></span><br><span class="line"> BLOCK_LAYOUT_BYREF = <span class="number">4</span>, <span class="comment">// N words byref pointers</span></span><br><span class="line"> BLOCK_LAYOUT_WEAK = <span class="number">5</span>, <span class="comment">// N words weak pointers</span></span><br><span class="line"> BLOCK_LAYOUT_UNRETAINED = <span class="number">6</span>, <span class="comment">// N words unretained pointers</span></span><br><span class="line"> BLOCK_LAYOUT_UNKNOWN_WORDS_7 = <span class="number">7</span>, <span class="comment">// N words, reserved</span></span><br><span class="line"> BLOCK_LAYOUT_UNKNOWN_WORDS_8 = <span class="number">8</span>, <span class="comment">// N words, reserved</span></span><br><span class="line"> BLOCK_LAYOUT_UNKNOWN_WORDS_9 = <span class="number">9</span>, <span class="comment">// N words, reserved</span></span><br><span class="line"> BLOCK_LAYOUT_UNKNOWN_WORDS_A = <span class="number">0xA</span>, <span class="comment">// N words, reserved</span></span><br><span class="line"> BLOCK_LAYOUT_UNUSED_B = <span class="number">0xB</span>, <span class="comment">// unspecified, reserved</span></span><br><span class="line"> BLOCK_LAYOUT_UNUSED_C = <span class="number">0xC</span>, <span class="comment">// unspecified, reserved</span></span><br><span class="line"> BLOCK_LAYOUT_UNUSED_D = <span class="number">0xD</span>, <span class="comment">// unspecified, reserved</span></span><br><span class="line"> BLOCK_LAYOUT_UNUSED_E = <span class="number">0xE</span>, <span class="comment">// unspecified, reserved</span></span><br><span class="line"> BLOCK_LAYOUT_UNUSED_F = <span class="number">0xF</span>, <span class="comment">// unspecified, reserved</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// Runtime support functions used by compiler when generating copy/dispose helpers</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Values for _Block_object_assign() and _Block_object_dispose() parameters</span></span><br><span class="line"><span class="keyword">enum</span> {</span><br><span class="line"> <span class="comment">// see function implementation for a more complete description of these fields and combinations</span></span><br><span class="line"> BLOCK_FIELD_IS_OBJECT = <span class="number">3</span>, <span class="comment">// id, NSObject, __attribute__((NSObject)), block, ...</span></span><br><span class="line"> BLOCK_FIELD_IS_BLOCK = <span class="number">7</span>, <span class="comment">// a block variable</span></span><br><span class="line"> BLOCK_FIELD_IS_BYREF = <span class="number">8</span>, <span class="comment">// the on stack structure holding the __block variable</span></span><br><span class="line"> BLOCK_FIELD_IS_WEAK = <span class="number">16</span>, <span class="comment">// declared __weak, only used in byref copy helpers</span></span><br><span class="line"> BLOCK_BYREF_CALLER = <span class="number">128</span>, <span class="comment">// called from __block (byref) copy/dispose support routines.</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">enum</span> {</span><br><span class="line"> BLOCK_ALL_COPY_DISPOSE_FLAGS = </span><br><span class="line"> BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_BLOCK | BLOCK_FIELD_IS_BYREF |</span><br><span class="line"> BLOCK_FIELD_IS_WEAK | BLOCK_BYREF_CALLER</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// Runtime entry point called by compiler when assigning objects inside copy helper routines</span></span><br><span class="line">BLOCK_EXPORT <span class="keyword">void</span> _Block_object_assign(<span class="keyword">void</span> *destAddr, <span class="keyword">const</span> <span class="keyword">void</span> *object, <span class="keyword">const</span> <span class="keyword">int</span> flags);</span><br><span class="line"> <span class="comment">// BLOCK_FIELD_IS_BYREF is only used from within block copy helpers</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// runtime entry point called by the compiler when disposing of objects inside dispose helper routine</span></span><br><span class="line">BLOCK_EXPORT <span class="keyword">void</span> _Block_object_dispose(<span class="keyword">const</span> <span class="keyword">void</span> *object, <span class="keyword">const</span> <span class="keyword">int</span> flags);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// Other support functions</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// runtime entry to get total size of a closure</span></span><br><span class="line"><span class="function">BLOCK_EXPORT size_t <span class="title">Block_size</span><span class="params">(<span class="keyword">void</span> *aBlock)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// indicates whether block was compiled with compiler that sets the ABI related metadata bits</span></span><br><span class="line">BLOCK_EXPORT <span class="keyword">bool</span> _Block_has_signature(<span class="keyword">void</span> *aBlock)</span><br><span class="line"> __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);</span><br><span class="line"></span><br><span class="line"><span class="comment">// returns TRUE if return value of block is on the stack, FALSE otherwise</span></span><br><span class="line">BLOCK_EXPORT <span class="keyword">bool</span> _Block_use_stret(<span class="keyword">void</span> *aBlock)</span><br><span class="line"> __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Returns a string describing the block's parameter and return types.</span></span><br><span class="line"><span class="comment">// The encoding scheme is the same as Objective-C @encode.</span></span><br><span class="line"><span class="comment">// Returns NULL for blocks compiled with some compilers.</span></span><br><span class="line">BLOCK_EXPORT <span class="keyword">const</span> <span class="keyword">char</span> * _Block_signature(<span class="keyword">void</span> *aBlock)</span><br><span class="line"> __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Returns a string describing the block's GC layout.</span></span><br><span class="line"><span class="comment">// This uses the GC skip/scan encoding.</span></span><br><span class="line"><span class="comment">// May return NULL.</span></span><br><span class="line">BLOCK_EXPORT <span class="keyword">const</span> <span class="keyword">char</span> * _Block_layout(<span class="keyword">void</span> *aBlock)</span><br><span class="line"> __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Returns a string describing the block's layout.</span></span><br><span class="line"><span class="comment">// This uses the "extended layout" form described above.</span></span><br><span class="line"><span class="comment">// May return NULL.</span></span><br><span class="line">BLOCK_EXPORT <span class="keyword">const</span> <span class="keyword">char</span> * _Block_extended_layout(<span class="keyword">void</span> *aBlock)</span><br><span class="line"> __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_7_0);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Callable only from the ARR weak subsystem while in exclusion zone</span></span><br><span class="line">BLOCK_EXPORT <span class="keyword">bool</span> _Block_tryRetain(<span class="keyword">const</span> <span class="keyword">void</span> *aBlock)</span><br><span class="line"> __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Callable only from the ARR weak subsystem while in exclusion zone</span></span><br><span class="line">BLOCK_EXPORT <span class="keyword">bool</span> _Block_isDeallocating(<span class="keyword">const</span> <span class="keyword">void</span> *aBlock)</span><br><span class="line"> __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// the raw data space for runtime classes for blocks</span></span><br><span class="line"><span class="comment">// class+meta used for stack, malloc, and collectable based blocks</span></span><br><span class="line">BLOCK_EXPORT <span class="keyword">void</span> * _NSConcreteMallocBlock[<span class="number">32</span>]</span><br><span class="line"> __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);</span><br><span class="line">BLOCK_EXPORT <span class="keyword">void</span> * _NSConcreteAutoBlock[<span class="number">32</span>]</span><br><span class="line"> __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);</span><br><span class="line">BLOCK_EXPORT <span class="keyword">void</span> * _NSConcreteFinalizingBlock[<span class="number">32</span>]</span><br><span class="line"> __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);</span><br><span class="line">BLOCK_EXPORT <span class="keyword">void</span> * _NSConcreteWeakBlockVariable[<span class="number">32</span>]</span><br><span class="line"> __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);</span><br><span class="line"><span class="comment">// declared in Block.h</span></span><br><span class="line"><span class="comment">// BLOCK_EXPORT void * _NSConcreteGlobalBlock[32];</span></span><br><span class="line"><span class="comment">// BLOCK_EXPORT void * _NSConcreteStackBlock[32];</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// the intercept routines that must be used under GC</span></span><br><span class="line">BLOCK_EXPORT <span class="keyword">void</span> _Block_use_GC( <span class="keyword">void</span> *(*alloc)(<span class="keyword">const</span> <span class="keyword">unsigned</span> <span class="keyword">long</span>, <span class="keyword">const</span> <span class="keyword">bool</span> isOne, <span class="keyword">const</span> <span class="keyword">bool</span> isObject),</span><br><span class="line"> <span class="keyword">void</span> (*setHasRefcount)(<span class="keyword">const</span> <span class="keyword">void</span> *, <span class="keyword">const</span> <span class="keyword">bool</span>),</span><br><span class="line"> <span class="keyword">void</span> (*gc_assign_strong)(<span class="keyword">void</span> *, <span class="keyword">void</span> **),</span><br><span class="line"> <span class="keyword">void</span> (*gc_assign_weak)(<span class="keyword">const</span> <span class="keyword">void</span> *, <span class="keyword">void</span> *),</span><br><span class="line"> <span class="keyword">void</span> (*gc_memmove)(<span class="keyword">void</span> *, <span class="keyword">void</span> *, <span class="keyword">unsigned</span> <span class="keyword">long</span>));</span><br><span class="line"></span><br><span class="line"><span class="comment">// earlier version, now simply transitional</span></span><br><span class="line">BLOCK_EXPORT <span class="keyword">void</span> _Block_use_GC5( <span class="keyword">void</span> *(*alloc)(<span class="keyword">const</span> <span class="keyword">unsigned</span> <span class="keyword">long</span>, <span class="keyword">const</span> <span class="keyword">bool</span> isOne, <span class="keyword">const</span> <span class="keyword">bool</span> isObject),</span><br><span class="line"> <span class="keyword">void</span> (*setHasRefcount)(<span class="keyword">const</span> <span class="keyword">void</span> *, <span class="keyword">const</span> <span class="keyword">bool</span>),</span><br><span class="line"> <span class="keyword">void</span> (*gc_assign_strong)(<span class="keyword">void</span> *, <span class="keyword">void</span> **),</span><br><span class="line"> <span class="keyword">void</span> (*gc_assign_weak)(<span class="keyword">const</span> <span class="keyword">void</span> *, <span class="keyword">void</span> *));</span><br><span class="line"></span><br><span class="line">BLOCK_EXPORT <span class="keyword">void</span> _Block_use_RR( <span class="keyword">void</span> (*retain)(<span class="keyword">const</span> <span class="keyword">void</span> *),</span><br><span class="line"> <span class="keyword">void</span> (*<span class="built_in">release</span>)(<span class="keyword">const</span> <span class="keyword">void</span> *));</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Block_callbacks_RR</span> {</span></span><br><span class="line"> <span class="keyword">size_t</span> <span class="built_in">size</span>; <span class="comment">// size == sizeof(struct Block_callbacks_RR)</span></span><br><span class="line"> <span class="keyword">void</span> (*retain)(<span class="keyword">const</span> <span class="keyword">void</span> *);</span><br><span class="line"> <span class="keyword">void</span> (*<span class="built_in">release</span>)(<span class="keyword">const</span> <span class="keyword">void</span> *);</span><br><span class="line"> <span class="keyword">void</span> (*destructInstance)(<span class="keyword">const</span> <span class="keyword">void</span> *);</span><br><span class="line">};</span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">Block_callbacks_RR</span> <span class="title">Block_callbacks_RR</span>;</span></span><br><span class="line"></span><br><span class="line">BLOCK_EXPORT <span class="keyword">void</span> _Block_use_RR2(<span class="keyword">const</span> Block_callbacks_RR *callbacks);</span><br><span class="line"></span><br><span class="line"><span class="comment">// make a collectable GC heap based Block. Not useful under non-GC.</span></span><br><span class="line">BLOCK_EXPORT <span class="keyword">void</span> *_Block_copy_collectable(<span class="keyword">const</span> <span class="keyword">void</span> *aBlock);</span><br><span class="line"></span><br><span class="line"><span class="comment">// thread-unsafe diagnostic</span></span><br><span class="line">BLOCK_EXPORT <span class="keyword">const</span> <span class="keyword">char</span> *_Block_dump(<span class="keyword">const</span> <span class="keyword">void</span> *block);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// Obsolete</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// first layout</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Block_basic</span> {</span></span><br><span class="line"> <span class="keyword">void</span> *isa;</span><br><span class="line"> <span class="keyword">int</span> Block_flags; <span class="comment">// int32_t</span></span><br><span class="line"> <span class="keyword">int</span> Block_size; <span class="comment">// XXX should be packed into Block_flags</span></span><br><span class="line"> <span class="keyword">void</span> (*Block_invoke)(<span class="keyword">void</span> *);</span><br><span class="line"> <span class="keyword">void</span> (*Block_copy)(<span class="keyword">void</span> *dst, <span class="keyword">void</span> *src); <span class="comment">// iff BLOCK_HAS_COPY_DISPOSE</span></span><br><span class="line"> <span class="keyword">void</span> (*Block_dispose)(<span class="keyword">void</span> *); <span class="comment">// iff BLOCK_HAS_COPY_DISPOSE</span></span><br><span class="line"> <span class="comment">//long params[0]; // where const imports, __block storage references, etc. get laid down</span></span><br><span class="line">} __attribute__((deprecated));</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">if</span> __cplusplus</span></span><br><span class="line">}</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br></pre></td></tr></table></figure>
<p>对block的结构有了一定了解,再看<code>__main_block_impl_0</code>构造函数</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line">__main_block_impl_0(<span class="keyword">void</span> *fp, struct __main_block_desc_0 *desc, <span class="keyword">int</span> flags=<span class="number">0</span>) {</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></pre></td></tr></table></figure>
<p>参数<code>fp</code>对应block内部函数体实现的首地址(也就是block实现的入口地址)</p>
<p>参数<code>desc</code>为block描述结构体指针</p>
<p>参数<code>flags</code>可选,默认值为0</p>
<p>内部语句表明将block初始化为<code>_NSConcreteStackBlock</code>类型,并绑定block实现对应的C函数指针</p>
<p>以下实例化调用,参数1为block对应的C函数指针,参数2为以静态全局变量初始化的<code>__mian_block_desc_0</code>结构体指针</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line">((<span class="keyword">void</span> (*)())&__main_block_impl_0((<span class="keyword">void</span> *)__main_block_func_0, &__main_block_desc_0_DATA));</span><br></pre></td></tr></table></figure>
<h3 id="block调用原理"><a href="#block调用原理" class="headerlink" title="block调用原理"></a>block调用原理</h3><figure class="highlight c"><table><tr><td class="code"><pre><span class="line">aBlock();</span><br></pre></td></tr></table></figure>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line">((<span class="keyword">void</span> (*)(__block_impl *))((__block_impl *)aBlock)->FuncPtr)((__block_impl *)aBlock);</span><br><span class="line"></span><br><span class="line"><span class="comment">//去掉类型转换前缀</span></span><br><span class="line">aBlock->FuncPtr(aBlock);</span><br></pre></td></tr></table></figure>
<p>不难看出,block调用即是通过结构体实例指针aBlock访问结构体成员函数指针<code>FuncPtr</code>来调用,同时将自身(<code>__cself</code>指针)传入</p>
<h3 id="捕获变量"><a href="#捕获变量" class="headerlink" title="捕获变量"></a>捕获变量</h3><h4 id="捕获局部变量"><a href="#捕获局部变量" class="headerlink" title="捕获局部变量"></a>捕获局部变量</h4><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">const</span> <span class="keyword">char</span> * argv[])</span> </span>{</span><br><span class="line"> <span class="keyword">int</span> var = <span class="number">100</span>;</span><br><span class="line"> <span class="keyword">void</span> (^aBlock)(<span class="keyword">void</span>) = ^ {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"var : %d\n"</span>,var);</span><br><span class="line"> };</span><br><span class="line"> var = <span class="number">0</span>;</span><br><span class="line"> aBlock();</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>同样通过clang转换得到以下关键部分:</p>
<figure class="highlight c"><table><tr><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> var;</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> _var, <span class="keyword">int</span> flags=<span class="number">0</span>) : var(_var) {</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><span class="line"></span><br><span class="line"><span class="keyword">void</span> (*aBlock)(<span class="keyword">void</span>) = ((<span class="keyword">void</span> (*)())&__main_block_impl_0((<span class="keyword">void</span> *)__main_block_func_0, &__main_block_desc_0_DATA, var));</span><br></pre></td></tr></table></figure>
<p>从转换代码看出,block的结构体增加了一成员var,并且关键部分在于block结构体实例化时传入var是<strong>值传递</strong>。</p>
<p>总的来说,所谓”捕获局部变量值”,意思是解析block语法块的时候,block语法块内使用的局部变量值被保存到block的结构体实例中</p>
<h4 id="处理非局部变量"><a href="#处理非局部变量" class="headerlink" title="处理非局部变量"></a>处理非局部变量</h4><p>在2.9与2.10节中提到block访问与修改全局静态变量、全局变量、静态变量,以下同样通过转换代码查看:</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">int</span> static_global_val = <span class="number">100</span>;</span><br><span class="line"><span class="keyword">int</span> global_val = <span class="number">101</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">const</span> <span class="keyword">char</span> * argv[])</span> </span>{</span><br><span class="line"> <span class="keyword">static</span> <span class="keyword">int</span> static_val = <span class="number">102</span>;</span><br><span class="line"> <span class="keyword">void</span> (^aBlock)(<span class="keyword">void</span>) = ^ {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"static_global_val : %d\n"</span>,static_global_val);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"global_val : %d\n"</span>,global_val);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"static_val : %d\n"</span>,static_val);</span><br><span class="line"> };</span><br><span class="line"> static_global_val = <span class="number">0</span>;</span><br><span class="line"> global_val = <span class="number">1</span>;</span><br><span class="line"> static_val = <span class="number">2</span>;</span><br><span class="line"> aBlock();</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>
<figure class="highlight c"><table><tr><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> *static_val;</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> *_static_val, <span class="keyword">int</span> flags=<span class="number">0</span>) : static_val(_static_val) {</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><span class="line"></span><br><span class="line"><span class="keyword">void</span> (*aBlock)(<span class="keyword">void</span>) = ((<span class="keyword">void</span> (*)())&__main_block_impl_0((<span class="keyword">void</span> *)__main_block_func_0, &__main_block_desc_0_DATA, &static_val));</span><br><span class="line"></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">void</span> __main_block_func_0(struct __main_block_impl_0 *__cself) {</span><br><span class="line"> <span class="keyword">int</span> *static_val = __cself->static_val; <span class="comment">// bound by copy</span></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"static_global_val : %d\n"</span>,static_global_val);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"global_val : %d\n"</span>,global_val);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"static_val : %d\n"</span>,(*static_val));</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>block底层对静态全局变量、全局变量并没有将其添加到自身结构体定义中,这一点很容易理解,就像往常编写C程序,这两类变量在整个程序生命周期中都可以访问并赋值修改。<strong>静态变量在超出函数作用域后仍然可以使用,因此block捕获静态变量static_val的指针,并在需要访问或修改的时候通过<code>int *static_val = __cself->static_val</code>指针来操作。其实这在另一方面也解释了为什么局部变量不能这样做?原因在于局部变量超出作用域后就释放,block不能捕获其指针来进行访问或修改。</strong></p>
<p>要想在block内部访问或修改原来的局部变量,就需要使用<code>__block</code></p>
<h4 id="block修饰符"><a href="#block修饰符" class="headerlink" title="__block修饰符"></a><span id="jump">__block修饰符</span></h4><p>__block类似于static、auto修饰符,可以用于给变量设置其存储的区域。例如,auto表示将变量存储在栈中,static表示将变量存储在数据区。</p>
<p>同样地,将用__block修饰的变量实例代码转换,得到如下:</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">const</span> <span class="keyword">char</span> * argv[])</span> </span>{</span><br><span class="line"> __block <span class="keyword">int</span> __block_val = <span class="number">100</span>;</span><br><span class="line"> <span class="keyword">void</span> (^aBlock)(<span class="keyword">void</span>) = ^ {</span><br><span class="line"> __block_val = <span class="number">1</span>;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"__block_val : %d\n"</span>,__block_val);</span><br><span class="line"> };</span><br><span class="line"> aBlock();</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"__block_val : %d\n"</span>,__block_val);</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>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> __<span class="title">Block_byref___block_val_0</span> {</span></span><br><span class="line"> <span class="keyword">void</span> *__isa;</span><br><span class="line">__Block_byref___block_val_0 *__forwarding; <span class="comment">//指向自身的指针</span></span><br><span class="line"> <span class="keyword">int</span> __flags;</span><br><span class="line"> <span class="keyword">int</span> __size;</span><br><span class="line"> <span class="keyword">int</span> __block_val;</span><br><span class="line">};</span><br><span class="line"></span><br><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"> __Block_byref___block_val_0 *__block_val; <span class="comment">// by ref</span></span><br><span class="line"> __main_block_impl_0(<span class="keyword">void</span> *fp, struct __main_block_desc_0 *desc, __Block_byref___block_val_0 *___block_val, <span class="keyword">int</span> flags=<span class="number">0</span>) : __block_val(___block_val->__forwarding) {</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><span class="line"><span class="keyword">static</span> <span class="keyword">void</span> __main_block_func_0(struct __main_block_impl_0 *__cself) {</span><br><span class="line"> __Block_byref___block_val_0 *__block_val = __cself->__block_val; <span class="comment">// bound by ref</span></span><br><span class="line"></span><br><span class="line"> (__block_val->__forwarding->__block_val) = <span class="number">1</span>;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"__block_val : %d\n"</span>,(__block_val->__forwarding->__block_val));</span><br><span class="line"> }</span><br><span class="line"><span class="keyword">static</span> <span class="keyword">void</span> __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((<span class="keyword">void</span>*)&dst->__block_val, (<span class="keyword">void</span>*)src->__block_val, <span class="number">8</span><span class="comment">/*BLOCK_FIELD_IS_BYREF*/</span>);}</span><br><span class="line"></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">void</span> __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((<span class="keyword">void</span>*)src->__block_val, <span class="number">8</span><span class="comment">/*BLOCK_FIELD_IS_BYREF*/</span>);}</span><br><span class="line"></span><br><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"> <span class="keyword">void</span> (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);</span><br><span class="line"> <span class="keyword">void</span> (*dispose)(struct __main_block_impl_0*);</span><br><span class="line">} __main_block_desc_0_DATA = { <span class="number">0</span>, <span class="keyword">sizeof</span>(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">const</span> <span class="keyword">char</span> * argv[])</span> </span>{</span><br><span class="line"> <span class="comment">//初始化__block变量对应的结构体</span></span><br><span class="line"> __attribute__((__blocks__(byref))) __Block_byref___block_val_0 __block_val = {(<span class="keyword">void</span>*)<span class="number">0</span>,(__Block_byref___block_val_0 *)&__block_val, <span class="number">0</span>, <span class="keyword">sizeof</span>(__Block_byref___block_val_0), <span class="number">100</span>};</span><br><span class="line"> <span class="comment">//初始化block结构体,同时将__block变量对应的结构体指针传入保存</span></span><br><span class="line"> <span class="keyword">void</span> (*aBlock)(<span class="keyword">void</span>) = ((<span class="keyword">void</span> (*)())&__main_block_impl_0((<span class="keyword">void</span> *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref___block_val_0 *)&__block_val, <span class="number">570425344</span>));</span><br><span class="line"> ((<span class="keyword">void</span> (*)(__block_impl *))((__block_impl *)aBlock)->FuncPtr)((__block_impl *)aBlock);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"__block_val : %d\n"</span>,(__block_val.__forwarding->__block_val));</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>可以看出</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line">__block <span class="keyword">int</span> __block_val = <span class="number">100</span>;</span><br></pre></td></tr></table></figure>
<p>转换后,变成了初始化一个<code>__Block_byref___block_val_0</code>结构体变量<code>__block_val</code>,并将值100传入。在block结构体初始化时将<code>__block_val</code>指针传入保存,查看此处block结构体定义,新增了一个<code>__Block_byref___block_val_0</code>类型的结构体指针,再查看<code>__main_block_func_0</code>函数,使用了该指针来访问<code>__block_val</code>这个用__block修饰的变量。</p>
<p><strong>此处<code>__forwarding</code>指针十分巧妙,它的作用是使__block变量无论是在栈上或是在堆上都能够被block正确访问,原理在后续章节</strong></p>
<p><strong>在block结构体定义中,它并没有像捕获局部变量那样,将<code>__Block_byref___block_val_0</code>以结构体的形式定义,而是通过指针方式,这样做是为了让多个block可以访问同一<strong>block变量而无需逐一实例化多个</strong>block变量的结构体</strong></p>
<h3 id="存储域"><a href="#存储域" class="headerlink" title="存储域"></a>存储域</h3><p>上述示例的block与__block变量都在main函数内声明定义,它们的本质是:</p>
<table>
<thead>
<tr>
<th>名称</th>
<th>实质</th>
</tr>
</thead>
<tbody><tr>
<td>block</td>
<td>栈上的block结构体实例</td>
</tr>
<tr>
<td>__block变量</td>
<td>栈上__block变量结构体实例</td>
</tr>
</tbody></table>
<h4 id="block的存储域"><a href="#block的存储域" class="headerlink" title="block的存储域"></a>block的存储域</h4><p>从block结构体中的isa指针值可以发现<code>_NSConcreteStackBlock</code>,此类block在栈上实例化。按照block的存储域划分,有以下三种:</p>
<ul>
<li>_NSConcreteStackBlock</li>
<li>_NSConcreteGlobalBlock</li>
<li>_NSConcreteMallocBlock</li>
</ul>
<p>程序运行时它们的内存存储情况如下</p>
<div align=center> <img src="https://kinkenyuen.oss-cn-shenzhen.aliyuncs.com/images/block存储域.png" width=50% /> <br> <div style="color:orange; border-bottom: 1px solid #d9d9d9; display: inline-block; color: #999; padding: 2px;">block存储域</div> </div>
<h5 id="NSConcreteGlobalBlock"><a href="#NSConcreteGlobalBlock" class="headerlink" title="_NSConcreteGlobalBlock"></a>_NSConcreteGlobalBlock</h5><p>因为使用全局变量的地方不能使用局部变量,因此此类block不存在对局部变量的捕获,在程序中只需要一个实例,将全局block存放在数据区域显然是没有问题。<br>以下两种情况存在的block均为全局block</p>
<ul>
<li>block定义在全局区(类似于全局变量)</li>
<li>block定义的语法块中没有使用或捕获局部变量</li>
</ul>
<h5 id="NSConcreteMallocBlock"><a href="#NSConcreteMallocBlock" class="headerlink" title="_NSConcreteMallocBlock"></a>_NSConcreteMallocBlock</h5><p>栈block在超出变量作用域后,block结构体实例就被释放(<strong>block变量同理)。而<code>_NSConcreteMallocBlock</code>即将block结构体实例和</strong>block变量结构体实例从栈上复制到堆上,来使它们在超出作用域后,仍可以继续存在。</p>
<p>在ARC环境下,大多数情况下编译器会自动帮我们生成复制block到堆上的代码。至于底层是如何copy,这里不展开分析。</p>
<h4 id="block变量的存储域"><a href="#block变量的存储域" class="headerlink" title="__block变量的存储域"></a>__block变量的存储域</h4><p>在block捕获__block变量的情况下,__block变量会受block从栈复制到堆上影响,如下</p>
<table>
<thead>
<tr>
<th>__block的存储域</th>
<th>block从栈复制到堆上后的影响</th>
</tr>
</thead>
<tbody><tr>
<td>栈</td>
<td>从栈复制到堆并被block持有</td>
</tr>
<tr>
<td>堆</td>
<td>被block持有</td>
</tr>
</tbody></table>
<div align=center> <img src="https://kinkenyuen.oss-cn-shenzhen.aliyuncs.com/images/block拷贝时__block变量行为.jpg" width=50% /> <br> <div style="color:orange; border-bottom: 1px solid #d9d9d9; display: inline-block; color: #999; padding: 2px;">block拷贝时__block变量行为</div> </div>
<p>大多数情况下,block最先都在栈上实例化,__block变量也是在栈上实例化,当一个__block变量被多个block使用时,第一个被复制到堆上的block持有__block实例,之后剩下的block复制到堆上时,对__block实例增加引用计数。</p>
<p>block与__block变量在堆上的内存管理与OC对象的引用计数方式相同。</p>
<h3 id="forwarding的作用"><a href="#forwarding的作用" class="headerlink" title="__forwarding的作用"></a>__forwarding的作用</h3><p>在3.2.3.3 __block修饰符一节中出现的__forwarding指针,结合上面__block变量实例的内存行为分析就很好理解了。转换得到以下代码:</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">const</span> <span class="keyword">char</span> * argv[])</span> </span>{</span><br><span class="line"> __block <span class="keyword">int</span> __block_val = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">void</span> (^aBlock)(<span class="keyword">void</span>) = ^ {</span><br><span class="line"> __block_val++;</span><br><span class="line"> };</span><br><span class="line"> <span class="comment">//执行到此处,由于ARC环境,block从栈复制到堆上,__block变量也复制到堆上</span></span><br><span class="line"> <span class="comment">//__block变量在复制到堆上后,更新__forwarding值,使其指向堆上的__block变量实例</span></span><br><span class="line"> __block_val++;<span class="comment">//可以从下面的转换代码中看出,这里的自增实质是对堆上的__block变量结构体成员__block_val自增</span></span><br><span class="line"> aBlock();</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"__block_val : %d\n"</span>,__block_val);<span class="comment">//这里看似访问的是栈上的__block变量,实质也是访问堆上的</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>
<figure class="highlight c"><table><tr><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><br><span class="line"> __Block_byref___block_val_0 *__block_val = __cself->__block_val; <span class="comment">// bound by ref</span></span><br><span class="line"> (__block_val->__forwarding->__block_val)++;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">const</span> <span class="keyword">char</span> * argv[])</span> </span>{</span><br><span class="line"> __attribute__((__blocks__(byref))) __Block_byref___block_val_0 __block_val = {(<span class="keyword">void</span>*)<span class="number">0</span>,(__Block_byref___block_val_0 *)&__block_val, <span class="number">0</span>, <span class="keyword">sizeof</span>(__Block_byref___block_val_0), <span class="number">1</span>};</span><br><span class="line"> <span class="keyword">void</span> (*aBlock)(<span class="keyword">void</span>) = ((<span class="keyword">void</span> (*)())&__main_block_impl_0((<span class="keyword">void</span> *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref___block_val_0 *)&__block_val, <span class="number">570425344</span>));</span><br><span class="line"> (__block_val.__forwarding->__block_val)++;</span><br><span class="line"> ((<span class="keyword">void</span> (*)(__block_impl *))((__block_impl *)aBlock)->FuncPtr)((__block_impl *)aBlock);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"__block_val : %d\n"</span>,(__block_val.__forwarding->__block_val));</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>__forwarding</code>的处理,就可以确保block语法块内或外,访问的的__block变量均为同一个,大多数情况下是在堆中的实例。</p>
<h3 id="捕获对象"><a href="#捕获对象" class="headerlink" title="捕获对象"></a>捕获对象</h3><figure class="highlight objc"><table><tr><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="keyword">id</span> array = [[<span class="built_in">NSMutableArray</span> alloc] init];</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>默认情况下,array变量在超出作用域后就被释放,在堆上的NSMutableArray对象由于没有强引用也会被释放。<br>但是查看以下代码:</p>
<figure class="highlight objc"><table><tr><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">void</span> (^blk)(<span class="keyword">id</span>);</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">id</span> array = [[<span class="built_in">NSMutableArray</span> alloc] init];</span><br><span class="line"> blk = ^ (<span class="keyword">id</span> obj) {</span><br><span class="line"> [array addObject:obj];</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"array count : %ld"</span>,[array count]);</span><br><span class="line"> };</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> blk([[<span class="built_in">NSObject</span> alloc] init]);</span><br><span class="line"> blk([[<span class="built_in">NSObject</span> alloc] init]);</span><br><span class="line"> blk([[<span class="built_in">NSObject</span> alloc] init]);</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>代码运行正常,也就是array在超出其变量作用域后,仍能在block语法块内存在被block访问赋值,这意味着block引用了该array对象</p>
<p>转换代码</p>
<figure class="highlight c"><table><tr><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"> id <span class="built_in">array</span>;</span><br><span class="line"> __main_block_impl_0(<span class="keyword">void</span> *fp, struct __main_block_desc_0 *desc, id _array, <span class="keyword">int</span> flags=<span class="number">0</span>) : <span class="built_in">array</span>(_array) {</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><span class="line"></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">void</span> __main_block_func_0(struct __main_block_impl_0 *__cself, id obj) {</span><br><span class="line"> id <span class="built_in">array</span> = __cself-><span class="built_in">array</span>; <span class="comment">// bound by copy</span></span><br><span class="line"></span><br><span class="line"> ((<span class="keyword">void</span> (*)(id, SEL, ObjectType _Nonnull))(<span class="keyword">void</span> *)objc_msgSend)((id)<span class="built_in">array</span>, sel_registerName(<span class="string">"addObject:"</span>), (id)obj);</span><br><span class="line"> NSLog((NSString *)&__NSConstantStringImpl__var_folders_fv_st7q8xkx05g6szwd247tjc8c0000gn_T_main_d9cea7_mi_0,((NSUInteger (*)(id, SEL))(<span class="keyword">void</span> *)objc_msgSend)((id)<span class="built_in">array</span>, sel_registerName(<span class="string">"count"</span>)));</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">void</span> __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((<span class="keyword">void</span>*)&dst-><span class="built_in">array</span>, (<span class="keyword">void</span>*)src-><span class="built_in">array</span>, <span class="number">3</span><span class="comment">/*BLOCK_FIELD_IS_OBJECT*/</span>);}</span><br><span class="line"></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">void</span> __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((<span class="keyword">void</span>*)src-><span class="built_in">array</span>, <span class="number">3</span><span class="comment">/*BLOCK_FIELD_IS_OBJECT*/</span>);}</span><br><span class="line"></span><br><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"> <span class="keyword">void</span> (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);</span><br><span class="line"> <span class="keyword">void</span> (*dispose)(struct __main_block_impl_0*);</span><br><span class="line">} __main_block_desc_0_DATA = { <span class="number">0</span>, <span class="keyword">sizeof</span>(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">const</span> <span class="keyword">char</span> * argv[])</span> </span>{</span><br><span class="line"> <span class="keyword">void</span> (*blk)(id);</span><br><span class="line"> {</span><br><span class="line"> id <span class="built_in">array</span> = ((NSMutableArray *(*)(id, SEL))(<span class="keyword">void</span> *)objc_msgSend)((id)((NSMutableArray *(*)(id, SEL))(<span class="keyword">void</span> *)objc_msgSend)((id)objc_getClass(<span class="string">"NSMutableArray"</span>), sel_registerName(<span class="string">"alloc"</span>)), sel_registerName(<span class="string">"init"</span>));</span><br><span class="line"> blk = ((<span class="keyword">void</span> (*)(id))&__main_block_impl_0((<span class="keyword">void</span> *)__main_block_func_0, &__main_block_desc_0_DATA, <span class="built_in">array</span>, <span class="number">570425344</span>));</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> ((<span class="keyword">void</span> (*)(__block_impl *, id))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk, ((NSObject *(*)(id, SEL))(<span class="keyword">void</span> *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(<span class="keyword">void</span> *)objc_msgSend)((id)objc_getClass(<span class="string">"NSObject"</span>), sel_registerName(<span class="string">"alloc"</span>)), sel_registerName(<span class="string">"init"</span>)));</span><br><span class="line"> ((<span class="keyword">void</span> (*)(__block_impl *, id))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk, ((NSObject *(*)(id, SEL))(<span class="keyword">void</span> *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(<span class="keyword">void</span> *)objc_msgSend)((id)objc_getClass(<span class="string">"NSObject"</span>), sel_registerName(<span class="string">"alloc"</span>)), sel_registerName(<span class="string">"init"</span>)));</span><br><span class="line"> ((<span class="keyword">void</span> (*)(__block_impl *, id))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk, ((NSObject *(*)(id, SEL))(<span class="keyword">void</span> *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(<span class="keyword">void</span> *)objc_msgSend)((id)objc_getClass(<span class="string">"NSObject"</span>), sel_registerName(<span class="string">"alloc"</span>)), sel_registerName(<span class="string">"init"</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捕获的array成为了blcok结构体中的成员变量,实际上结构体中的array变量含有<strong>strong修饰符,编译器在ARC环境下隐藏了。在OC中,C结构体不能含有</strong>stroing修饰的变量,因为编译器不知道何时对C结构体进行初始化和释放,也就是说C结构体不归ARC管理。</p>
<p>但是OC的运行时库可以在恰当时机将block从栈复制到堆上以及释放堆上的block(block在ARC环境下编译器做的处理),由于这一原因,block的结构体中可以有<strong>strong或</strong>weak修饰的变量,编译器也能很好地对这些变量进行内存管理。</p>
<p>为了管理block结构体中的这些变量,使用了<code>__main_block_desc_0</code>结构体中新增的两个成员变量,分别为<code>copy</code>与<code>dispose</code>函数指针</p>
<p><code>copy</code>指针指向<code>__main_block_copy_0</code>函数,而<code>__main_block_copy_0</code>函数使用<code>_Block_object_assign</code>函数<strong>将array赋值到block结构体成员变量array中并持有该对象</strong><br><code>_Block_object_assign</code>相当于<code>retain</code>并赋值。这一步的表现即为block引用持有了对象array</p>
<p><code>dispose</code>指针指向<code>__main_block_dispose_0</code>函数,而<code>__main_block_dispose_0</code>函数使用<code>_Block_object_dispose</code>函数<strong>释放赋值在block结构体成员变量array中的对象</strong><br><code>_Block_object_dispose</code>相当于<code>release</code>实例方法</p>
<p>从转换代码中没有发现以上两个函数的调用处,原因是它们是<strong>在block从栈复制到堆以及堆上block释放时才会调用</strong>。</p>
<p>block从栈上复制到堆的时机</p>
<ul>
<li>调用block的copy实例方法</li>
<li>block作为函数返回值返回</li>
<li>将block复制给带__strong修饰符id类型的类或block类型成员变量时</li>
<li>在方法名中含有usingBlock的Cocoa框架方法或GCD的API中传递block时</li>
</ul>
<p>在以上情况下的block都会在底层调用<code>_Block_copy</code>函数从栈上复制到堆</p>
<p>回顾<code>__block修饰符</code>一节,其实转换代码也使用了<code>copy</code>和<code>dispose</code>函数,不同的是其中一个枚举参数</p>
<table>
<thead>
<tr>
<th>对象</th>
<th>BLOCK_FIELD_IS_OBJECT</th>
</tr>
</thead>
<tbody><tr>
<td>__block变量</td>
<td>BLOCK_FIELD_IS_BYREF</td>
</tr>
</tbody></table>
<p>更多的枚举类型可以在前面的<code>Block_private.h</code>中内容找到</p>
<h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p>《Objective-C高级编程》</p>
]]></content>
<categories>
<category>iOS</category>
</categories>
<tags>
<tag>iOS</tag>
</tags>
</entry>
<entry>
<title>类Reveal视图分析App——Lookin</title>
<url>/2020/01/04/%E7%B1%BBReveal%E8%A7%86%E5%9B%BE%E5%88%86%E6%9E%90App%E2%80%94%E2%80%94Lookin/</url>
<content><![CDATA[<h1 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h1><p><a href="https://lookin.work/" target="_blank" rel="noopener">Lookin</a>是一个免费的iOS UI层次分析App,相比Reveal,它不仅能获取视图控件对应的成员变量名称,而且可以做一些简单的UI之外的动态调试</p>
<h1 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h1><p>Mac端直接从官网下载App即可</p>
<h1 id="iPhone端"><a href="#iPhone端" class="headerlink" title="iPhone端"></a>iPhone端</h1><p>配合越狱设备使用更快捷,类似于Reveal,使用越狱插件为目标App注入<code>LookinServer.framework</code></p>
<p><a href="https://github.com/creantan/LookinLoader" target="_blank" rel="noopener">LookinLoader</a></p>
<div align=center> <img src="https://kinkenyuen.oss-cn-shenzhen.aliyuncs.com/images/Lookin.jpg" width=50% /> <br> <div style="color:orange; border-bottom: 1px solid #d9d9d9; display: inline-block; color: #999; padding: 2px;">Lookin</div> </div>]]></content>
<categories>
<category>iOS</category>
</categories>
<tags>
<tag>iOS</tag>
</tags>
</entry>
<entry>
<title>简单配置iTerm2与Zsh</title>
<url>/2020/01/04/%E7%AE%80%E5%8D%95%E9%85%8D%E7%BD%AEiTerm2%E4%B8%8EZsh/</url>
<content><![CDATA[<h1 id="iTerm2"><a href="#iTerm2" class="headerlink" title="iTerm2"></a>iTerm2</h1><p><a href="https://iterm2.com/" target="_blank" rel="noopener">iTerm2</a></p>
<p>官网下载直接安装即可</p>
<p>主题配置使用Dark Background</p>
<h2 id="安装Powerline字体"><a href="#安装Powerline字体" class="headerlink" title="安装Powerline字体"></a>安装Powerline字体</h2><p>为了终端下能正确的显示fancy字符,需要安装powerline字体,这样,这些fancy字符不至于显示为乱码。 GitHub上已经有制作好的Powerline字体,可以下载了直接安装到系统:</p>
<blockquote>
<p>Powerline字体下载: <a href="https://github.com/powerline/fonts" target="_blank" rel="noopener">https://github.com/powerline/fonts</a></p>
</blockquote>
<p>安装好之后,就可以选择一款你喜欢的Powerline字体了:<br><code>Preferences</code> -> <code>Profiles</code> -> <code>Text</code> -> <code>Font</code></p>
<h2 id="修改打开窗口的默认位置"><a href="#修改打开窗口的默认位置" class="headerlink" title="修改打开窗口的默认位置"></a>修改打开窗口的默认位置</h2><p><code>Preference</code>-><code>Profiles</code> -> <code>Window</code> -> <code>Style</code></p>
<h2 id="修改背景透明度"><a href="#修改背景透明度" class="headerlink" title="修改背景透明度"></a>修改背景透明度</h2><p><code>Preference</code>-><code>Profiles</code> -> <code>Window</code> -> <code>Transparency</code></p>
<p>快捷键<code>cmd + U</code>切换透明与非透明</p>
<h1 id="Zsh与Oh-My-Zsh"><a href="#Zsh与Oh-My-Zsh" class="headerlink" title="Zsh与Oh My Zsh"></a>Zsh与Oh My Zsh</h1><p>查看支持的shell</p>
<p><code>cat /etc/shells</code></p>
<figure class="highlight awk"><table><tr><td class="code"><pre><span class="line"><span class="comment"># List of acceptable shells for chpass(1).</span></span><br><span class="line"><span class="comment"># Ftpd will not allow users to connect who are not using</span></span><br><span class="line"><span class="comment"># one of these shells.</span></span><br><span class="line"></span><br><span class="line"><span class="regexp">/bin/</span>bash</span><br><span class="line"><span class="regexp">/bin/</span>csh</span><br><span class="line"><span class="regexp">/bin/</span>ksh</span><br><span class="line"><span class="regexp">/bin/</span>sh</span><br><span class="line"><span class="regexp">/bin/</span>tcsh</span><br><span class="line"><span class="regexp">/bin/</span>zsh</span><br></pre></td></tr></table></figure>
<p>Macos 系统默认安装了zsh</p>
<p>安装Oh My Zsh ,通过<a href="https://ohmyz.sh/" target="_blank" rel="noopener">官网</a>的脚本安装</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">sh -c <span class="string">"<span class="variable">$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)</span>"</span></span><br></pre></td></tr></table></figure>
<p>修改Oh My Zsh主题<br>默认主题为robbyrussell,可以通过修改 ~/.zshrc 文件配置</p>
<figure class="highlight ini"><table><tr><td class="code"><pre><span class="line"><span class="attr">ZSH_THEME</span>=<span class="string">"robbyrussell"</span></span><br></pre></td></tr></table></figure>
<h2 id="增强插件"><a href="#增强插件" class="headerlink" title="增强插件"></a>增强插件</h2><h3 id="命令提示"><a href="#命令提示" class="headerlink" title="命令提示"></a>命令提示</h3><p>克隆<code>zsh-autosuggestions</code>到<code>plugins</code>目录</p>
<figure class="highlight awk"><table><tr><td class="code"><pre><span class="line">git clone git:<span class="regexp">//gi</span>thub.com<span class="regexp">/zsh-users/</span>zsh-autosuggestions <span class="variable">$ZSH_CUSTOM</span><span class="regexp">/plugins/</span>zsh-autosuggestions</span><br></pre></td></tr></table></figure>
<p>修改<code>.zshrc</code>配置文件</p>
<figure class="highlight ini"><table><tr><td class="code"><pre><span class="line"><span class="attr">plugins</span>=(zsh-autosuggestions git)</span><br></pre></td></tr></table></figure>
<h3 id="语法高亮"><a href="#语法高亮" class="headerlink" title="语法高亮"></a>语法高亮</h3><p><code>homebrew</code>安装 <code>zsh-syntax-highlighting</code> 插件</p>
<figure class="highlight mipsasm"><table><tr><td class="code"><pre><span class="line"><span class="keyword">brew </span><span class="keyword">install </span>zsh-syntax-highlighting</span><br></pre></td></tr></table></figure>
<p>修改<code>.zshrc</code>配置文件,添加一行内容</p>
<figure class="highlight gradle"><table><tr><td class="code"><pre><span class="line"><span class="keyword">source</span> <span class="regexp">/usr/</span>local<span class="regexp">/share/</span>zsh-syntax-highlighting<span class="regexp">/zsh-syntax-highlighting.zsh</span></span><br></pre></td></tr></table></figure>
<h1 id="配合Go2Shell-快速打开终端"><a href="#配合Go2Shell-快速打开终端" class="headerlink" title="配合Go2Shell 快速打开终端"></a>配合Go2Shell 快速打开终端</h1><p><a href="https://zipzapmac.com/Go2Shell" target="_blank" rel="noopener">官网地址</a></p>
<p>下载后打开选择默认终端,然后安装到<code>Finder</code>菜单栏即可</p>
]]></content>
<categories>
<category>MacOS</category>
</categories>
<tags>
<tag>MacOS</tag>
</tags>
</entry>
<entry>
<title>Hello World <!-- 文章标题 --></title>
<url>/2020/01/02/hello-world/</url>
<content><![CDATA[<p>Welcome to <a href="https://hexo.io/" target="_blank" rel="noopener">Hexo</a>! This is your very first post. Check <a href="https://hexo.io/docs/" target="_blank" rel="noopener">documentation</a> for more info. If you get any problems when using Hexo, you can find the answer in <a href="https://hexo.io/docs/troubleshooting.html" target="_blank" rel="noopener">troubleshooting</a> or you can ask me on <a href="https://github.com/hexojs/hexo/issues" target="_blank" rel="noopener">GitHub</a>.</p>
<h2 id="Quick-Start"><a href="#Quick-Start" class="headerlink" title="Quick Start"></a>Quick Start</h2><h3 id="Create-a-new-post"><a href="#Create-a-new-post" class="headerlink" title="Create a new post"></a>Create a new post</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ hexo new <span class="string">"My New Post"</span></span><br></pre></td></tr></table></figure>
<p>More info: <a href="https://hexo.io/docs/writing.html" target="_blank" rel="noopener">Writing</a></p>
<h3 id="Run-server"><a href="#Run-server" class="headerlink" title="Run server"></a>Run server</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ hexo server</span><br></pre></td></tr></table></figure>
<p>More info: <a href="https://hexo.io/docs/server.html" target="_blank" rel="noopener">Server</a></p>
<h3 id="Generate-static-files"><a href="#Generate-static-files" class="headerlink" title="Generate static files"></a>Generate static files</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ hexo generate</span><br></pre></td></tr></table></figure>
<p>More info: <a href="https://hexo.io/docs/generating.html" target="_blank" rel="noopener">Generating</a></p>
<h3 id="Deploy-to-remote-sites"><a href="#Deploy-to-remote-sites" class="headerlink" title="Deploy to remote sites"></a>Deploy to remote sites</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ hexo deploy</span><br></pre></td></tr></table></figure>
<p>More info: <a href="https://hexo.io/docs/one-command-deployment.html" target="_blank" rel="noopener">Deployment</a></p>
]]></content>
<categories>
<category>教程 <!-- 分类 --></category>
</categories>
<tags>
<tag>模板 <!-- 标签 --></tag>
</tags>
</entry>
<entry>
<title>越狱插件开发-VideoDownloaderCN</title>
<url>/2019/12/31/%E8%B6%8A%E7%8B%B1%E6%8F%92%E4%BB%B6%E5%BC%80%E5%8F%91-VideoDownloaderCN/</url>
<content><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>原创文章搬运</p>
<p>学习逆向有一段时间了,想着写个iOS Jailbreak Tweak练练手,平时比较喜欢看视频,看到比较搞笑的视频想保存下载发给好友,无奈无法下载,于是有了这个<strong>VideoDownloaderCN</strong>插件。</p>
<p><strong>声明 : 插件仅用于技术研究</strong></p>
<div align=center>
<img src="https://kinkenyuen.oss-cn-shenzhen.aliyuncs.com/images/tweak.png" width=50% />
<br>
<div style="color:orange; border-bottom: 1px solid #d9d9d9;
display: inline-block;
color: #999;
padding: 2px;">插件界面
</div>
</div>
<h1 id="分析"><a href="#分析" class="headerlink" title="分析"></a>分析</h1><p>最近分析几个App上的视频播放,基本上就是一个View上有个播放组件,而这个View或者它的若干个nextResponder持有这个视频的url,于是有这样的思路:</p>
<ul>
<li>动态分析定位得到视频的URL</li>
<li>在View的构造方法内添加一个手势弹出下载</li>
<li>手势对应视频下载的方法实现,最后移动到系统相册</li>
</ul>
<p>分析方法用到:<strong>Cycript动态调试</strong>、<strong>Reveal界面分析</strong>、<strong>class-dump头文件</strong></p>
<p>分析过程不在这里描述,主要mark一下Tweak的构建工程.</p>
<h1 id="Theos"><a href="#Theos" class="headerlink" title="Theos"></a>Theos</h1><h2 id="安装配置"><a href="#安装配置" class="headerlink" title="安装配置"></a>安装配置</h2><p>网络上有非常多的教程,但是我推荐查看官方的.<a href="https://github.com/theos/theos/wiki/Installation-iOS" target="_blank" rel="noopener">Theos installation</a></p>
<h2 id="创建工程"><a href="#创建工程" class="headerlink" title="创建工程"></a>创建工程</h2><blockquote>
<p><code>nic.pl</code></p>
</blockquote>
<div align=center> <img src="https://kinkenyuen.oss-cn-shenzhen.aliyuncs.com/images/creat_tweak.png" width=600 /> <br> <div style="color:orange; border-bottom: 1px solid #d9d9d9; display: inline-block; color: #999; padding: 2px;">创建插件</div> </div>
<blockquote>
<p>目录介绍</p>
</blockquote>
<div align=center> <img src="https://kinkenyuen.oss-cn-shenzhen.aliyuncs.com/images/mulu.png" width=600 /> <br> <div style="color:orange; border-bottom: 1px solid #d9d9d9; display: inline-block; color: #999; padding: 2px;">目录介绍</div> </div>
<blockquote>
<p>Makefile</p>
</blockquote>
<div align=center> <img src="https://kinkenyuen.oss-cn-shenzhen.aliyuncs.com/images/makefile.png" width=600 /> <br> <div style="color:orange; border-bottom: 1px solid #d9d9d9; display: inline-block; color: #999; padding: 2px;">makefile</div> </div>
<blockquote>
<p>Tweak.xm</p>
</blockquote>
<div align=center> <img src="https://kinkenyuen.oss-cn-shenzhen.aliyuncs.com/images/tweakxm.png" width=600 /> <br> <div style="color:orange; border-bottom: 1px solid #d9d9d9; display: inline-block; color: #999; padding: 2px;">xm</div> </div>
<p>此处我使用了多个xm文件来区分每个App的注入代码,具体查看<a href="https://github.com/kinkenyuen/VideoDownloaderCN" target="_blank" rel="noopener">github</a></p>
<blockquote>
<p>*.plist</p>
</blockquote>
<div align=center> <img src="https://kinkenyuen.oss-cn-shenzhen.aliyuncs.com/images/plist.png" width="375" /> <br> <div style="color:orange; border-bottom: 1px solid #d9d9d9; display: inline-block; color: #999; padding: 2px;">plist</div> </div>
<h2 id="编译Tweak"><a href="#编译Tweak" class="headerlink" title="编译Tweak"></a>编译Tweak</h2><p>做好了基础配置以及编写好xm之后,就可以通过<code>make</code>命令编译,我遇到了一个错误,如下:</p>
<div align=center> <img src="https://kinkenyuen.oss-cn-shenzhen.aliyuncs.com/images/makeerror.png" width=600 /> <br> <div style="color:orange; border-bottom: 1px solid #d9d9d9; display: inline-block; color: #999; padding: 2px;">编译错误</div> </div>
<p>修改<code>Makefile</code>再次编译</p>
<div align=center> <img src="https://kinkenyuen.oss-cn-shenzhen.aliyuncs.com/images/xiugaimakefile.png" width=600 /> <br> <div style="color:orange; border-bottom: 1px solid #d9d9d9; display: inline-block; color: #999; padding: 2px;">修改编译设置</div> </div>
<div align=center> <img src="https://kinkenyuen.oss-cn-shenzhen.aliyuncs.com/images/makesuccess.png" width=600 /> <br> <div style="color:orange; border-bottom: 1px solid #d9d9d9; display: inline-block; color: #999; padding: 2px;">再次编译</div> </div>
<p><code>make package</code>生成deb</p>
<div align=center> <img src="https://kinkenyuen.oss-cn-shenzhen.aliyuncs.com/images/makepackage.png" width=600 /> <br> <div style="color:orange; border-bottom: 1px solid #d9d9d9; display: inline-block; color: #999; padding: 2px;">makepackage</div> </div>
<h2 id="安装Tweak"><a href="#安装Tweak" class="headerlink" title="安装Tweak"></a>安装Tweak</h2><p><code>make install</code>命令安装到设备,安装前需要配置一些必要参数,将以下两行参数配置到<code>Makefile</code>是一种方法,意思是通过本地USB方式,端口2222安装到手机,我的做法是配置好写在<code>.bash_profile</code>或<code>.zshrc</code>内,这样不用每次在<code>Makefile</code>内编写(重要提示:出现Error请详细检查theos配置,手机IP、端口是否映射,ssh是否正常登陆)</p>
<blockquote>
<p><code>export THEOS_DEVICE_IP=localhost</code></p>
<p><code>export THEOS_DEVICE_PORT=2222</code></p>
</blockquote>
<p>当然安装deb方法不止一种,当你打包出<code>deb</code>后</p>
<ol>
<li>可以使用<code>CyDown</code>这个插件开一个<code>ftp</code>,然后PC传<code>deb</code>过去,接着手机端打开<code>cydia</code>找到那个deb安装。</li>
<li>可以使用<code>scp</code>命令传到手机端,接着手机终端<code>dpkg -i</code>安装,具体做法请自行搜索详细教程。</li>
</ol>
<p>至此,一个Tweak在iOS越狱设备上</p>
<div align=center> <img src="https://kinkenyuen.oss-cn-shenzhen.aliyuncs.com/images/install.jpeg" width=600 /> <br> <div style="color:orange; border-bottom: 1px solid #d9d9d9; display: inline-block; color: #999; padding: 2px;">安装</div> </div>
<h1 id="插件设置项Preference-Bundle"><a href="#插件设置项Preference-Bundle" class="headerlink" title="插件设置项Preference Bundle"></a>插件设置项Preference Bundle</h1><p>一个<code>tweak</code>可能要设置一些选项,就像<code>App Store上</code>的<code>App</code>一样,在设置应用里面可以设置,在<code>theos</code>里,可以通过创建<code>Preference Bundle</code>来为插件提供设置界面,有点类似于<code>Xcode</code>里的<code>Setting Bundle</code>,<code>Preference Bundle</code>安装到手机后会在<code>/Library/PreferenceBundles/</code>目录生成一个对应的<code>bundle</code>,此<code>bundle</code>会基于<code>PreferenceLoader</code>注入到设置应用(<code>Setting.app</code>),而<code>PreferenceLoader</code>是由Dustin Howett开发的基于<code>Mobile Substrate</code>的工具,主要为插件在系统设置界面添加一个设置入口。</p>
<h2 id="为插件创建Preference-Bundle"><a href="#为插件创建Preference-Bundle" class="headerlink" title="为插件创建Preference Bundle"></a>为插件创建Preference Bundle</h2><p>一般的做法为:在原插件目录使用<code>theos</code>创建</p>
<div align=center> <img src="https://kinkenyuen.oss-cn-shenzhen.aliyuncs.com/images/createprefs.png" width=600 /> <br> <div style="color:orange; border-bottom: 1px solid #d9d9d9; display: inline-block; color: #999; padding: 2px;">创建PreferenceBundle</div> </div>
<div align=center> <img src="https://kinkenyuen.oss-cn-shenzhen.aliyuncs.com/images/prefssubproject.png" width=600 /> <br> <div style="color:orange; border-bottom: 1px solid #d9d9d9; display: inline-block; color: #999; padding: 2px;">PreferenceBundle sub project</div> </div>
<p>创建<code>preference bundle</code>后新生成目录下的文件介绍如下:</p>
<table>
<thead>
<tr>
<th>文件</th>
<th>作用/含义</th>
</tr>
</thead>
<tbody><tr>
<td>entry.plist</td>
<td>为插件在系统设置应用界面添加一个入口,一般修改<code>icon</code>与<code>label</code>即可</td>
</tr>
<tr>
<td>XXXRootListController</td>
<td>XXXRootListController必须继承PSListController或者PSViewController,且必须实现- (id)specifiers方法,因为PSListController依赖_specifiers来获得metadata和group</td>
</tr>
<tr>
<td>Makefile</td>
<td><code>preference bundle</code>的<code>Makefile</code>,一般不用过多修改与操作,编译Tweak的Makefile会跟随着一起编译</td>
</tr>
<tr>
<td>Resources文件夹下的文件如下</td>
<td></td>