-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathNSObject.mm
2801 lines (2232 loc) · 90.2 KB
/
NSObject.mm
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
/*
* Copyright (c) 2010-2012 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
#include "objc-private.h"
#include "NSObject.h"
#include "objc-weak.h"
#include "llvm-DenseMap.h"
#include "NSObject.h"
#include <malloc/malloc.h>
#include <stdint.h>
#include <stdbool.h>
#include <mach/mach.h>
#include <mach-o/dyld.h>
#include <mach-o/nlist.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <libkern/OSAtomic.h>
#include <Block.h>
#include <map>
#include <execinfo.h>
@interface NSInvocation
- (SEL)selector;
@end
/***********************************************************************
* Weak ivar support
**********************************************************************/
static id defaultBadAllocHandler(Class cls)
{
_objc_fatal("attempt to allocate object of class '%s' failed",
cls->nameForLogging());
}
static id(*badAllocHandler)(Class) = &defaultBadAllocHandler;
static id callBadAllocHandler(Class cls)
{
// fixme add re-entrancy protection in case allocation fails inside handler
return (*badAllocHandler)(cls);
}
void _objc_setBadAllocHandler(id(*newHandler)(Class))
{
badAllocHandler = newHandler;
}
namespace {
// The order of these bits is important.
#define SIDE_TABLE_WEAKLY_REFERENCED (1UL<<0)
#define SIDE_TABLE_DEALLOCATING (1UL<<1) // MSB-ward of weak bit
#define SIDE_TABLE_RC_ONE (1UL<<2) // MSB-ward of deallocating bit
#define SIDE_TABLE_RC_PINNED (1UL<<(WORD_BITS-1))
// SIDE_TABLE_RC_PINNED是将 SideTable 中的对象的引用计数的64位数据的最高位作为对象引用计数上溢出的标记,是MSB,引用计数上溢出时,对象的retain、release操作不会改变引用计数位域的值。
#define SIDE_TABLE_RC_SHIFT 2
#define SIDE_TABLE_FLAG_MASK (SIDE_TABLE_RC_ONE-1)
// RefcountMap disguises its pointers because we
// don't want the table to act as a root for `leaks`.
///DenseMap存储了对象指针与引用计数的键值对,运行时一共维护8或64个SideTable,对应一共有8或64个DenseMap。
///SideTable通过一个自旋��控制对DenseMap集合的访问。所以对象如何在这8或64个SideTable之间(DenseMap之间)分布存储是提高系统并发效率的关键
typedef objc::DenseMap<DisguisedPtr<objc_object>,size_t,true> RefcountMap;
// Template parameters.
enum HaveOld { DontHaveOld = false, DoHaveOld = true };
enum HaveNew { DontHaveNew = false, DoHaveNew = true };
/// 根据SideTables的结构我们知道全局共有8或64张SideTable。一张SideTable会管理多个对象,而并非一个。
struct SideTable {
spinlock_t slock; /// 自旋锁:实际类型为os_unfair_lock. 防止多线程访问SideTable冲突
/**
将RefcountMap简单的的理解为是一个map,key是DisguisedPtr<objc_object>,value是对象的引用计数。
这也是objc::DenseMap<DisguisedPtr<objc_object>,size_t,true> RefcountMap的含义
即模板类型分别对应:key,DisguisedPtr类型。value,size_t类型。是否清除为vlaue==0的数据,true
value是size_t(64位系统中占用64位)来保存引用计数,
其中1位用来存储固定标志位,在溢出的时候使用,一位表示正在释放中,一位表示是否有弱引用,其余位表示实际的引用计数
value的每位代表的含义
63 62 2 1 0
SIDE_TABLE_RC_PINNED .................... SIDE_TABLE_DEALLOCATING SIDE_TABLE_WEAKLY_REFERENCED
RefcountMap refcnts 是一个C++的对象,内部包含了一个迭代器
其中以DisguisedPtr<objc_object> 对象指针为key,size_t 为value保存对象引用计数
将key、value通过std::pair打包以后,放入迭代器中,所以取出值之后,.first代表key,.second代表value
*/
RefcountMap refcnts; /// 存放引用计数的hash表
weak_table_t weak_table; /// 存放弱引用的hash表
SideTable() { /// 构造函数
memset(&weak_table, 0, sizeof(weak_table)); /// 初始化weak_table
/// C 库函数 void *memset(void *str, int c, size_t n) 复制字符 c(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符。
}
~SideTable() { /// 析构函数
_objc_fatal("Do not delete SideTable.");
}
void lock() { slock.lock(); }
void unlock() { slock.unlock(); }
void forceReset() { slock.forceReset(); }
// Address-ordered lock discipline for a pair of side tables.
/// 两个关于锁操作的静态模板函数
template<HaveOld, HaveNew>
static void lockTwo(SideTable *lock1, SideTable *lock2);
template<HaveOld, HaveNew>
static void unlockTwo(SideTable *lock1, SideTable *lock2);
/**
1. spinlock_t 自旋锁在runtime源码中经常看到,当自旋锁被一个线程获得时,他不能被其他线程获得。和互斥锁不同的是,它不会从函数直接返回,让线程sleep,而是让其处于active状态。
由于自旋不释放CPU,因而持有自选锁的线程应尽快释放锁,好的一面的看,这样减少了上下文的切换速度。也因此它的使用场景应该是那种占用时间较短抢占情况。
我们注意到这里 slock 属性是放置每个SideTable下的,这种细粒度的锁到底有什么意义?
我们知道,一般代码加锁是为了保障读写安全,结合实际,如果整个SideTables只用一个锁去做处理,也就意味着,一旦表中的一个SideTable在被其他线程操作,那么其他线程将无法对其他SideTable进行处理,可想而知这种实现方式的效率是比较低的。而如果我们转换思想,降低锁的粒度,让他分配到每个SideTable中时,即使表中有SideTable正在被读取或者写入,但是不会影响到表中其他的元素。同时也保证了线程读写安全。在一定程度上给数据读写提供提供了相当高的伸缩性,每个锁的在理想情况下共同分担竞争请求。
这种处理方式也就是所谓拆分锁。
*/
};
template<>
void SideTable::lockTwo<DoHaveOld, DoHaveNew>
(SideTable *lock1, SideTable *lock2)
{
spinlock_t::lockTwo(&lock1->slock, &lock2->slock);
}
template<>
void SideTable::lockTwo<DoHaveOld, DontHaveNew>
(SideTable *lock1, SideTable *)
{
lock1->lock();
}
template<>
void SideTable::lockTwo<DontHaveOld, DoHaveNew>
(SideTable *, SideTable *lock2)
{
lock2->lock();
}
template<>
void SideTable::unlockTwo<DoHaveOld, DoHaveNew>
(SideTable *lock1, SideTable *lock2)
{
spinlock_t::unlockTwo(&lock1->slock, &lock2->slock);
}
template<>
void SideTable::unlockTwo<DoHaveOld, DontHaveNew>
(SideTable *lock1, SideTable *)
{
lock1->unlock();
}
template<>
void SideTable::unlockTwo<DontHaveOld, DoHaveNew>
(SideTable *, SideTable *lock2)
{
lock2->unlock();
}
// We cannot use a C++ static initializer to initialize SideTables because
// libc calls us before our C++ initializers run. We also don't want a global
// pointer to this struct because of the extra indirection.
// Do it the hard way.
/**
我们不能用C++静态初始化方法去初始化SideTables,
因为C++初始化方法运行之前libc就会调用我们;我们同样不想用一个全局的指针去指向SideTables,
因为需要额外的代价。但是没办法我们只能这样。
libc 比 C++ static initializer 先调用到 SideTables,所以不能用 C++ static initializer 去调用 SideTableInit ,所以才用 SideTableBuf 来初始化。
*/
/// SideTableBuf是一个外部不可见的静态内存区块,其类型为StripedMap<SideTable>。它是内存管理的基础,其它的功能与特性都是基于这个插槽而展开的
/// SideTableBuf是一个静态内存区域,new StripedMap<SideTable>在其上创建了对象,巧妙的避开了初始化顺序问题
alignas(StripedMap<SideTable>) static uint8_t
SideTableBuf[sizeof(StripedMap<SideTable>)]; /// 静态内存区域
///
static void SideTableInit() {
new (SideTableBuf) StripedMap<SideTable>(); /// new StripedMap<SideTable>在SideTableBuf上创建了对象
}
/**
我们看到SideTables本质上StripedMap类型。其中包含的元素为SideTable类型
这里的reinterpret_cast关键字,我们可以理解为强制类型转换,也就是将SideTableBuf转换为
StripedMap类型,其中包含的元素为SideTale类型。
!important:
Side Tables() 结构是什么?
在他的结构下面挂了很多 Side Table 数据结构,这些数据结构在不同的架构上面是有不同个数的,比如在非嵌入式系统当中,Side Table 这个表一共有 64 个,在这里解释一下,Side Tables() 实际上是一个哈希表,可以通过一个对象指针来具体找到它对应的引用计数表或者说弱引用表在哪一张具体的 Side Table 当中
如说只有一张 SideTable ,那么在内存中分配的所有对象的引用计数或者说弱引用存储都放到一张大表当中,这个时候如果说要操作某一个对象的引用计数值进行修改,比如说加一减一的操作,由于所有的对象可能是在不同的线程当中去分配创建的,包括调用他们的 retain,release等方法也可能是在不同线程当中操作的,那么这个时候再对这张表进行操作的时候,需要进行加锁处理才能保证对数据的访问安全,在这个过程当中就存在了效率问题,如果现在已经有一个对象在操作这张表,那么下一个对象就要等前一个对象操作完后把锁释放之后它才能操作这张表
系统为了解决效率问题引用了分离锁的技术方案
可以把内存对象所对应的引用技术表可以分拆成多个部分,比如说把它分拆成8个,分拆成8个需要对这8个表分别加锁
比如说某一个对象A在第一张表里面,另一个对象B在另一张表中,那么当A和B同时进行引用计数操作的时候可以并发操作,但是如果按照一张表的情况下他们就需要顺序操作
怎样实现快速分流?
快速分流指的是通过一个对象的指针��何快速的定位到它属于哪张 side Table 表?
side Tables 的本质是一张哈希表,这张哈希表当中可能有64张具体的 side Table ,然后存储不同对象的引用计数表和弱引用表
*/
static StripedMap<SideTable>& SideTables() { /// 返回指向StripedMap<SideTable>的隐式指针
return *reinterpret_cast<StripedMap<SideTable>*>(SideTableBuf); /// 将SideTableBuf转为StripedMap<SideTable>*
/** C++知识点
1.使用了 C++ 标准转换运算符 reinterpret_cast ,其表达方式为:reinterpret_cast <new_type> (expression)
用来处理无关类型之间的转换。该关键字会产生一个新值,并保证与原参数(expression)拥有完全相同的比特位。
reinterpret_cast 运算符并不会改变括号中运算对象的值,而是对该对象从位模式上进行重新解释 https://zhuanlan.zhihu.com/p/33040213
2.引用就是个弱化的指针
引用变量是一个别名,也就是说,它是某个已存在变量的另一个名字。一旦把引用初始化为某个变量,就可以使用该引用名称或变量名称来指向变量。
https://www.runoob.com/cplusplus/cpp-references.html
通过使用引用来替代指针,会使 C++ 程序更容易阅读和维护。C++ 函数可以返回一个引用,方式与返回一个指针类似。
当函数返回一个引用时,则返回一个指向返回值的隐式指针
https://www.runoob.com/cplusplus/returning-values-by-reference.html
*/
}
// anonymous namespace
};
void SideTableLockAll() {
SideTables().lockAll();
}
void SideTableUnlockAll() {
SideTables().unlockAll();
}
void SideTableForceResetAll() {
SideTables().forceResetAll();
}
void SideTableDefineLockOrder() {
SideTables().defineLockOrder();
}
void SideTableLocksPrecedeLock(const void *newlock) {
SideTables().precedeLock(newlock);
}
void SideTableLocksSucceedLock(const void *oldlock) {
SideTables().succeedLock(oldlock);
}
void SideTableLocksPrecedeLocks(StripedMap<spinlock_t>& newlocks) {
int i = 0;
const void *newlock;
while ((newlock = newlocks.getLock(i++))) {
SideTables().precedeLock(newlock);
}
}
void SideTableLocksSucceedLocks(StripedMap<spinlock_t>& oldlocks) {
int i = 0;
const void *oldlock;
while ((oldlock = oldlocks.getLock(i++))) {
SideTables().succeedLock(oldlock);
}
}
//
// The -fobjc-arc flag causes the compiler to issue calls to objc_{retain/release/autorelease/retain_block}
//
id objc_retainBlock(id x) {
return (id)_Block_copy(x);
}
//
// The following SHOULD be called by the compiler directly, but the request hasn't been made yet :-)
//
BOOL objc_should_deallocate(id object) {
return YES;
}
id
objc_retain_autorelease(id obj)
{
return objc_autorelease(objc_retain(obj));
}
/// 与引用计数相关
/**
NSObject.mm文件中,void objc_storeStrong(id *location, id obj)函数用于将obj对象的引用存储于location地址,实质上则是将obj的地址存储到location地址,注意调用了objc_retain(obj)增加了obj对象的引用计数,原来存储于location地址的对象prev则调用objc_release将其释放。
*/
void
objc_storeStrong(id *location, id obj)
{
id prev = *location;
if (obj == prev) {
return;
}
objc_retain(obj); /// 引用计数+1
*location = obj;
objc_release(prev); /// 引用计数-1
}
// Update a weak variable.
// If HaveOld is true, the variable has an existing value
// that needs to be cleaned up. This value might be nil.
// If HaveNew is true, there is a new value that needs to be
// assigned into the variable. This value might be nil.
// If CrashIfDeallocating is true, the process is halted(停止) if newObj is
// deallocating or newObj's class does not support weak references.
// If CrashIfDeallocating is false, nil is stored instead.
// HaveOld: true - 变量有值
// false - 需要被及时清理,当前值可能为 nil
// HaveNew: true - 需要被分配的新值,当前值可能为 nil
// false - 不需要分配新值
// CrashIfDeallocating: true - 说明 newObj 已经释放或者 newObj 不支持弱引用,该过程需要暂停
// false - 用 nil 替代存储
enum CrashIfDeallocating {
DontCrashIfDeallocating = false, DoCrashIfDeallocating = true
};
///C++的模板函数 语法解释:https://blog.csdn.net/qq_35637562/article/details/55194097
/**
HaveOld:是否有旧值;
HaveNew:是否指定新值;
CrashIfDeallocating:若为true,传入的newObj不支持weak类型或野指针时程序将中断运行;若为false,传入的newObj不支持weak类型或野指针时置新值为nil;
location:weak指针旧值地址(类型 objc_object **);
newObj:weak指针新值(类型 objc_object *);
*/
template <HaveOld haveOld, HaveNew haveNew,
CrashIfDeallocating crashIfDeallocating>
static id
storeWeak(id *location, objc_object *newObj)
{
// 该过程用来更新弱引用指针的指向
assert(haveOld || haveNew); // assert断言,assert(表达式),表达式为真,什么也不做,表达式为假则crash。
if (!haveNew) assert(newObj == nil);
// 初始化 previouslyInitializedClass 指针
Class previouslyInitializedClass = nil;
// ��明两个 SideTable
// ① 新旧散列创建
id oldObj;
SideTable *oldTable;
SideTable *newTable;
// Acquire locks for old and new values.
// Order by lock address to prevent lock ordering problems.
// Retry if the old value changes underneath us.
// 获得新值和旧值的锁存位置(用地址作为唯一标示)
// 通过地址来建立索引标志,防止桶重复
// 下面指向的操作会改变旧值
retry:
if (haveOld) {
oldObj = *location;
oldTable = &SideTables()[oldObj]; /// 获取旧值对象所在的SideTable
} else {
oldTable = nil;
}
if (haveNew) {
newTable = &SideTables()[newObj]; /// 获取新值对象所在的SideTable
} else {
newTable = nil;
}
// 加锁操作,防止多线程中竞争冲突
SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);
// 避免线程冲突重处理
// location 应该与 oldObj 保持一致,如果不同,说明当前的 location 已经处理过 oldObj 可是又被其他线程所修改
if (haveOld && *location != oldObj) {
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
goto retry;
}
// Prevent a deadlock between the weak reference machinery
// and the +initialize machinery by ensuring that no
// weakly-referenced object has an un-+initialized isa.
// 防止弱引用间死锁
// 并且通过 +initialize 初始化构造器保证所有弱引用的 isa 非空指向
if (haveNew && newObj) {
// 获得新对象的 isa 指针
Class cls = newObj->getIsa();
// 判断 isa 非空且已经初始化
if (cls != previouslyInitializedClass &&
!((objc_class *)cls)->isInitialized())
{
// 解锁
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
// 对其 isa 指针进行初始化
_class_initialize(_class_getNonMetaClass(cls, (id)newObj));
// If this class is finished with +initialize then we're good.
// If this class is still running +initialize on this thread
// (i.e. +initialize called storeWeak on an instance of itself)
// then we may proceed but it will appear initializing and
// not yet initialized to the check above.
// Instead set previouslyInitializedClass to recognize it on retry.
// 如果该类已经完成执行 +initialize 方法是最理想情况
// 如果该类 +initialize 在线程中
// 例如 +initialize 正在调用 storeWeak 方法
// 需要手动对其增加保护策略,并设置 previouslyInitializedClass 指针进行标记
previouslyInitializedClass = cls;
// 重新尝试
goto retry;
}
}
// Clean up old value, if any.
// ② 清除旧值
if (haveOld) {
/// 阅读代码时先看 weak_register_no_lock部分在看weak_unregister_no_lock。
/// weak_register_no_lock是从无到有的过程涉及的信息较多
weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
}
// Assign new value, if any.
// ③ 分配新值
if (haveNew) {
newObj = (objc_object *)
weak_register_no_lock(&newTable->weak_table, (id)newObj, location,
crashIfDeallocating);
// weak_register_no_lock returns nil if weak store should be rejected
// 如果弱引用被释放 weak_register_no_lock 方法返回 nil
// Set is-weakly-referenced bit in refcount table.
if (newObj && !newObj->isTaggedPointer()) {
/// 完成弱引用注册后,新对象newObj需要调用setWeaklyReferenced_nolock(...)方法标记对象被弱引用
/// 因为对象在析构时,若对象被弱引用,则需要将这些弱引用全部置nil
newObj->setWeaklyReferenced_nolock();
}
// Do not set *location anywhere else. That would introduce a race.
// 之前不要设置 location 对象,这里需要更改指针指向
*location = (id)newObj;
}
else {
// No new value. The storage is not changed.
// 没有新值,则无需更改
}
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
return (id)newObj;
}
/**
* This function stores a new value into a __weak variable. It would
* be used anywhere a __weak variable is the target of an assignment.
*
* @param location The address of the weak pointer itself
* @param newObj The new object this weak ptr should now point to
*
* @return \e newObj
*/
id
objc_storeWeak(id *location, id newObj)
{
return storeWeak<DoHaveOld, DoHaveNew, DoCrashIfDeallocating>
(location, (objc_object *)newObj);
}
/**
* This function stores a new value into a __weak variable.
* If the new object is deallocating or the new object's class
* does not support weak references, stores nil instead.
*
* @param location The address of the weak pointer itself
* @param newObj The new object this weak ptr should now point to
*
* @return The value stored (either the new object or nil)
*/
id
objc_storeWeakOrNil(id *location, id newObj)
{
return storeWeak<DoHaveOld, DoHaveNew, DontCrashIfDeallocating>
(location, (objc_object *)newObj);
}
/**
* Initialize a fresh weak pointer to some object location.
* It would be used for code like:
*
* (The nil case)
* __weak id weakPtr;
* (The non-nil case)
* NSObject *o = ...;
* __weak id weakPtr = o;
*
* This function IS NOT thread-safe with respect to concurrent
* modifications to the weak variable. (Concurrent weak clear is safe.)
*
* @param location Address of __weak ptr.
* @param newObj Object ptr.
*/
id
objc_initWeak(id *location, id newObj)
{
// 查看对象实例是否有效
// 无效对象直接导致指针释放
if (!newObj) {
*location = nil;
return nil;
}
// 这里传递了三个 bool 数值
// 使用 template 进行常量参数传递是为了优化性能 C++的模板语法: 模板中可以携带参数,此处应该就是模板中携带参数
return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
(location, (objc_object*)newObj);
}
id
objc_initWeakOrNil(id *location, id newObj)
{
if (!newObj) {
*location = nil;
return nil;
}
return storeWeak<DontHaveOld, DoHaveNew, DontCrashIfDeallocating>
(location, (objc_object*)newObj);
}
/**
* Destroys the relationship between a weak pointer
* and the object it is referencing in the internal weak
* table. If the weak pointer is not referencing anything,
* there is no need to edit the weak table.
*
* This function IS NOT thread-safe with respect to concurrent
* modifications to the weak variable. (Concurrent weak clear is safe.)
*
* @param location The weak pointer address.
*/
void
objc_destroyWeak(id *location)
{
(void)storeWeak<DoHaveOld, DontHaveNew, DontCrashIfDeallocating>
(location, nil);
}
/*
Once upon a time we eagerly cleared *location if we saw the object
was deallocating. This confuses code like NSPointerFunctions which
tries to pre-flight the raw storage and assumes if the storage is
zero then the weak system is done interfering. That is false: the
weak system is still going to check and clear the storage later.
This can cause objc_weak_error complaints and crashes.
So we now don't touch the storage until deallocation completes.
*/
id
objc_loadWeakRetained(id *location)
{
id obj;
id result;
Class cls;
SideTable *table;
retry:
// fixme std::atomic this load
obj = *location;
if (!obj) return nil;
if (obj->isTaggedPointer()) return obj;
table = &SideTables()[obj];
table->lock();
if (*location != obj) {
table->unlock();
goto retry;
}
result = obj;
cls = obj->ISA();
if (! cls->hasCustomRR()) {
// Fast case. We know +initialize is complete because
// default-RR can never be set before then.
assert(cls->isInitialized());
if (! obj->rootTryRetain()) {
result = nil;
}
}
else {
// Slow case. We must check for +initialize and call it outside
// the lock if necessary in order to avoid deadlocks.
if (cls->isInitialized() || _thisThreadIsInitializingClass(cls)) {
BOOL (*tryRetain)(id, SEL) = (BOOL(*)(id, SEL))
class_getMethodImplementation(cls, SEL_retainWeakReference);
if ((IMP)tryRetain == _objc_msgForward) {
result = nil;
}
else if (! (*tryRetain)(obj, SEL_retainWeakReference)) {
result = nil;
}
}
else {
table->unlock();
_class_initialize(cls);
goto retry;
}
}
table->unlock();
return result;
}
/**
* This loads the object referenced by a weak pointer and returns it, after
* retaining and autoreleasing the object to ensure that it stays alive
* long enough for the caller to use it. This function would be used
* anywhere a __weak variable is used in an expression.
*
* @param location The weak pointer address
*
* @return The object pointed to by \e location, or \c nil if \e location is \c nil.
*/
id
objc_loadWeak(id *location)
{
if (!*location) return nil;
return objc_autorelease(objc_loadWeakRetained(location));
}
/**
* This function copies a weak pointer from one location to another,
* when the destination doesn't already contain a weak pointer. It
* would be used for code like:
*
* __weak id src = ...;
* __weak id dst = src;
*
* This function IS NOT thread-safe with respect to concurrent
* modifications to the destination variable. (Concurrent weak clear is safe.)
*
* @param dst The destination variable.
* @param src The source variable.
*/
void
objc_copyWeak(id *dst, id *src)
{
id obj = objc_loadWeakRetained(src);
objc_initWeak(dst, obj);
objc_release(obj);
}
/**
* Move a weak pointer from one location to another.
* Before the move, the destination must be uninitialized.
* After the move, the source is nil.
*
* This function IS NOT thread-safe with respect to concurrent
* modifications to either weak variable. (Concurrent weak clear is safe.)
*
*/
void
objc_moveWeak(id *dst, id *src)
{
objc_copyWeak(dst, src);
objc_destroyWeak(src);
*src = nil;
}
/***********************************************************************
Autorelease pool implementation
A thread's autorelease pool is a stack of pointers.
线程的自动释放池是个指针栈
Each pointer is either an object to release, or POOL_BOUNDARY which is
an autorelease pool boundary.
每个指针要么指向要释放的对象,要么指向POOL_BOUNDARY(自动释放池的边界)
A pool token is a pointer to the POOL_BOUNDARY for that pool. When
the pool is popped, every object hotter than the sentinel(哨兵) is released.
一个池的token是指向该池子的POOL_BOUNDARY的指针。当池子出栈,每个比这个哨兵(token)hotter的对象会被释放
The stack is divided into a doubly-linked list of pages. Pages are added
and deleted as necessary.
该栈是由是pages组成双向链表结构.pages会被根据需要添加或删除
Thread-local storage points to the hot page, where newly autoreleased
objects are stored.
线程存储指向hotPage的指针,先创建的自动释放的对象存储在此。
**********************************************************************/
// Set this to 1 to mprotect() autorelease pool contents
#define PROTECT_AUTORELEASEPOOL 0
// Set this to 1 to validate the entire autorelease pool header all the time
// (i.e. use check() instead of fastcheck() everywhere)
#define CHECK_AUTORELEASEPOOL (DEBUG)
BREAKPOINT_FUNCTION(void objc_autoreleaseNoPool(id obj));
BREAKPOINT_FUNCTION(void objc_autoreleasePoolInvalid(const void *token));
namespace {
struct magic_t {
static const uint32_t M0 = 0xA1A1A1A1;
# define M1 "AUTORELEASE!"
static const size_t M1_len = 12;
uint32_t m[4]; /// 4 * 4 = 16个字节
magic_t() {
assert(M1_len == strlen(M1));
assert(M1_len == 3 * sizeof(m[1]));
m[0] = M0;
strncpy((char *)&m[1], M1, M1_len);
}
~magic_t() {
m[0] = m[1] = m[2] = m[3] = 0;
}
bool check() const {
return (m[0] == M0 && 0 == strncmp((char *)&m[1], M1, M1_len));
}
bool fastcheck() const {
#if CHECK_AUTORELEASEPOOL
return check();
#else
return (m[0] == M0);
#endif
}
# undef M1
};
/***
此处只是看了关于AutoreleasePoolPage的源码,AutoreleasePoolPage与RunLoop也有密切的关系,这部分内容需要看RunLoop的源码
Autorelease pool 与 RunLoop 也有非常紧密的关系。App 启动后再主线程 RunLoop 会注册两个 Observer
第一个 Observer 监听 Entry 事件,其回调会调用objc_autoreleasePoolPush()函数创建自动释放池;
第二个Observer监听两个事件,监听到BeforeWaiting(即将进入休眠)时调用objc_autoreleasePoolPop()函数释放旧的 autorelease pool
并调用objc_autoreleasePoolPush()函数建立新的 autorelease pool ;
监听到 Exit 事件时,调用objc_autoreleasePoolPop(void *ctxt)函数释放 autorelease pool (顺便一提:调用pop传入的ctxt参数实际上是调用push新建 autorelease pool 时返回的POOL_BOUNDARY的地址)。
注意:
1.runloop自动创建自动释放池
2.手动创建 @autoreleasepool{ \\添加需要自动释放的对象 }
***/
/**
A thread's autorelease pool is a stack of pointers.
Each pointer is either an object to release, or POOL_SENTINEL which is
an autorelease pool boundary.
A pool token is a pointer to the POOL_SENTINEL for that pool. When
the pool is popped, every object hotter than the sentinel is released.
The stack is divided into a doubly-linked list of pages. Pages are added
and deleted as necessary.
Thread-local storage points to the hot page, where newly autoreleased
objects are stored.
其表达的要点如下:
1.线程的 autorelease pool 的实质是指针的堆栈,autorelease pool 是与线程关联的;
2.Autorelease pool 中的指针要么指向需要release的对象,要么是POOL_SENTINEL,POOL_SENTINEL是 autorelease pool 的边界;
3.Autorelease pool 的 token是指向 autorelease pool 自身的POOL_SENTINEL的指针。当autorelease pool释放时,会释放所有比 tokenhotter更“热”的对象;
4.Autorelease pool 中,指针的堆栈被划分到 分页中,分页使用双向链表的数据结构关联,分页可按需添加或删除;
线程本地存储指向hot page“热”分页,“热”分页保存最新的 autorelease 的对象。
*/
class AutoreleasePoolPage
{
// EMPTY_POOL_PLACEHOLDER is stored in TLS when exactly one pool is
// pushed and it has never contained any objects. This saves memory
// when the top level (i.e. libdispatch) pushes and pops pools but
// never uses them.
/// 当仅推入一个池且从未包含任何对象时,EMPTY_POOL_PLACEHOLDER就会存储在TLS中。当顶层(即libdispatch)推送并弹出池但从不使用它们时,这样可以节省内存。
# define EMPTY_POOL_PLACEHOLDER ((id*)1)
# define POOL_BOUNDARY nil /// 边界
static pthread_key_t const key = AUTORELEASE_POOL_KEY;
static uint8_t const SCRIBBLE = 0xA3; // 0xA3A3A3A3 after releasing
static size_t const SIZE =
#if PROTECT_AUTORELEASEPOOL
PAGE_MAX_SIZE; // must be multiple of vm page size
#else
PAGE_MAX_SIZE; // size and alignment, power of 2
#endif
static size_t const COUNT = SIZE / sizeof(id);
magic_t const magic; ///16字节 校验
id *next; ///8 指向AutoreleasePoolPage中下一个可分配的地址
pthread_t const thread; /// 8
AutoreleasePoolPage * const parent; /// 8 指向当前节点的上一个AutoreleasePoolPage节点,若parent为null则表示该节点为双向链表的开始节点
AutoreleasePoolPage *child; /// 8 下一个AutoreleasePoolPage节点
uint32_t const depth; /// 4 当前AutoreleasePoolPage节点在链表中的位置
uint32_t hiwat; /// 4 校验
// SIZE-sizeof(*this) bytes of contents follow
/**
AutoreleasePoolPage重载了new运算符,指定构建AutoreleasePoolPage实例分配定长的4096字节内存空间
#define I386_PGBYTES 4096
#define PAGE_SIZE I386_PGBYTES
#define PAGE_MAX_SIZE PAGE_SIZE
*/
/// 重载new运算符
static void * operator new(size_t size) {
return malloc_zone_memalign(malloc_default_zone(), SIZE, SIZE);
}
/// 重载delete运算符
static void operator delete(void * p) {
return free(p);
}
inline void protect() {
#if PROTECT_AUTORELEASEPOOL
mprotect(this, SIZE, PROT_READ);
check();
#endif
}
inline void unprotect() {
#if PROTECT_AUTORELEASEPOOL
check();
mprotect(this, SIZE, PROT_READ | PROT_WRITE);
#endif
}
/// C++ 构造函数初始化列表:构造函数初始化列表以一个冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟一个放在括号中的初始化式
AutoreleasePoolPage(AutoreleasePoolPage *newParent)
: magic(), next(begin()), thread(pthread_self()),
parent(newParent), child(nil),
depth(parent ? 1+parent->depth : 0),
hiwat(parent ? parent->hiwat : 0)
{
if (parent) {
parent->check();
assert(!parent->child);
parent->unprotect();
parent->child = this;
parent->protect();
}
protect();
}
~AutoreleasePoolPage()
{
check();
unprotect();
assert(empty());
// Not recursive: we don't want to blow out the stack
// if a thread accumulates a stupendous amount of garbage
assert(!child);
}
void busted(bool die = true)
{
magic_t right;
(die ? _objc_fatal : _objc_inform)
("autorelease pool page %p corrupted\n"
" magic 0x%08x 0x%08x 0x%08x 0x%08x\n"
" should be 0x%08x 0x%08x 0x%08x 0x%08x\n"
" pthread %p\n"
" should be %p\n",
this,
magic.m[0], magic.m[1], magic.m[2], magic.m[3],
right.m[0], right.m[1], right.m[2], right.m[3],
this->thread, pthread_self());
}
void check(bool die = true)
{
if (!magic.check() || !pthread_equal(thread, pthread_self())) {
busted(die); /// busted破灭
}
}
void fastcheck(bool die = true)
{
#if CHECK_AUTORELEASEPOOL
check(die);
#else
if (! magic.fastcheck()) {
busted(die);
}
#endif
}
id * begin() {
/// 获取开始存放autorelrese对象的地址 sizeof(*this)获取的大小是56Byte
return (id *) ((uint8_t *)this+sizeof(*this));
}
id * end() {
/// 一个的Page的结尾
return (id *) ((uint8_t *)this+SIZE);
}
bool empty() {
/// 没有存放autorelrese对象
return next == begin();
}
bool full() {
/// 该Page满了
return next == end();
}
bool lessThanHalfFull() {
/// 是否少于一半
return (next - begin() < (end() - begin()) / 2);
}
id *add(id obj)
{
assert(!full());
unprotect();
id *ret = next; // faster than `return next-1` because of aliasing
*next++ = obj; /// next指向下一个存储位置
protect();
return ret;
}
void releaseAll()
{
releaseUntil(begin());/// 释放到当前page存储堆栈的开始处
}