Objective-C weak 关键字 实现 源码分析

  1. __weak
    1. objc_storeWeak
      1. weakTable
      2. weak对象的释放
    2. 立即释放对象
    3. 使用附有__weak修饰符的变量,即是使用注册到autoreleasepool中的对象
    4. allowWeakReference/retainWeakReference
    5. 具体源码分析

__weak

1
id __week obj1 = obj;

编译器的模拟代码

1
2
3
4
id obj1;
obj1 = 0;
objc_storeWeak(&obj1, obj);
objc_storeWeak(&obj1, 0);

objc_storeWeak
函数把第二参数的赋值对象的地址作为键值,将第一参数的附有__weak修饰的变量的地址注册到weak表中。
如果第二参数为0,则把变量的地址从weak表中删除。
initWeak的实现

1
2
3
4
5
id objc_initWeak(id *object, id value)
{
*object = 0;
return objc_storeWeak(object, value);
}

storeWeak是Objective-C的开源部分
让我们来看看storeWeak到底是怎么实现的

objc_storeWeak

storeWeak的源码
官方英文注释挺全的,可以直接理解~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Clean up old value, if any.
if (HaveOld) {
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
// Set is-weakly-referenced bit in refcount table.
if (newObj && !newObj->isTaggedPointer()) {
newObj->setWeaklyReferenced_nolock();
}
// Do not set *location anywhere else. That would introduce a race.
*location = (id)newObj;
}
else {
// No new value. The storage is not changed.
}

来看看storeWeak的实现
获取oldObj/newObj

1
2
3
4
5
6
7
8
9
10
11
if (HaveOld) {
oldObj = *location;
oldTable = &SideTables()[oldObj];
} else {
oldTable = nil;
}
if (HaveNew) {
newTable = &SideTables()[newObj];
} else {
newTable = nil;
}

首先是根据weak指针找到其指向的老的对象:

1
oldObj = *location;

然后获取到与新旧对象相关的SideTable对象:

1
2
oldTable = &SideTables()[oldObj];
newTable = &SideTables()[newObj];

&SideTables()[oldObj]这是什么鬼??
其时是 实现了一个类 StripedMap 重载了[]操作符
(c++: 哪里都能看到我 233)

1
2
3
4
public:
T& operator[] (const void *p) {
return array[indexForPointer(p)].value;
}

下面要做的就是在老对象的weak表中移除指向信息,而在新对象的weak表中建立关联信息:

1
2
3
4
5
6
7
if (HaveOld) {
weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
}
if (HaveNew) {
newObj = weak_register_no_lock(&newTable->weak_table, newObj,location);
// weak_register_no_lock returns NULL if weak store should be rejected
}

接下来让弱引用指针指向新的对象:

1
*location = newObj;

最后会返回这个新对象:

1
return newObj;

以上我们能发现weak的管理实际上跟weak_table有这千丝万缕的联系,接下来就对weak_table进行分析!

weakTable

(关先上源码还是先总结…我思考了很久…。。。。)

来张图直观感受一下:
weakTable

下面开始对这些结构体进行分析:

weak表的结构定义:

1
2
3
4
5
6
7
8
9
10
/**
* The global weak references table. Stores object ids as keys,
* and weak_entry_t structs as their values.
*/
struct weak_table_t {
weak_entry_t *weak_entries;
size_t num_entries;
uintptr_t mask;
uintptr_t max_hash_displacement;
};

这是一张存储全局弱引用对象的表
object 的 ids 作为Hash表的keys
weak_entry_t 作为他们的值.

来看weak_entry_t的结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct weak_entry_t {
DisguisedPtr<objc_object> referent;
union {
struct {
weak_referrer_t *referrers;
uintptr_t out_of_line : 1;
uintptr_t num_refs : PTR_MINUS_1;
uintptr_t mask;
uintptr_t max_hash_displacement;
};
struct {
// out_of_line=0 is LSB of one of these (don't care which)
weak_referrer_t inline_referrers[WEAK_INLINE_COUNT];
};
};
};

referrers: 所有指向 referent 的指针
referent: 指向内存上的weak对象的指针
weak_referrer_t: referent对象的地址

referent关系.png

现在我们可以得出什么结论了呢

  1. OC中 弱引用变量的管理是利用 weak表(Hash表)来管理的
  2. weak表中的weak_entries负责管理指向weak对象的变量

weak对象的释放

释放对象时,废弃谁都不持有的对象的同时,程序的动作是怎么样的呢?

对象被废弃时最后调用的objc_clear_deallocating函数的动作如下:
(1) 从weak表中获取废弃对象的地址为键值的记录。
(2) 将包含在记录中的所有附有__weak修饰符变量的地址,赋值为nil
(3) 从weak表中删除该记录。
(4) 从引用计数表中删除废弃对象的地址为键值的记录。

由此可知,如果大量使用附有weak修饰符的变量,则会消耗相应的CPU资源。良策是只在需要避免循环引用时使用weak修饰符。

立即释放对象

1
2
3
{
id __weak obj = [[NSObject alloc] init];
}

因为该源码将自己生成并持有的对象赋值给附有__weak修饰符的变量中,所以自己不能持有该对象,这是会被释放并且废弃。

使用附有__weak修饰符的变量,即是使用注册到autoreleasepool中的对象

1
2
3
4
{
id __weak obj1 = obj;
NSLog(@"%@",obj1);
}

编译器模拟的代码

1
2
3
4
5
6
id obj1;
objc_initWeak(&obj,obj);
id tmp = objc_loadWeakRetained(&obj);
objc_autorelease(tmp);
NSLog(@"%@", tmp);
objc_destoryWeak(&obj1);

注意这两行代码

1
2
id tmp = objc_loadWeakRetained(&obj);
objc_autorelease(tmp);

与赋值时相比,在使用附有__weak修饰符变量的情况下,增加了对objc_loadWeakRetained函数和objc_autorelease函数的调用。
(1) objc_loadWeakRetained 函数取出附有__weak修饰符变量所引用的对象并retain
(2) objc_autorelease 函数将对象注册到autoreleasepool中。

注意:
每次使用weak修饰的变量,会使变量所引用的对象注册到autoreleasepool中。
如果要避免这种情况可以将附有
weak修饰符的变量赋值给附有__strong修饰符的变量后再次使用。

1
2
id __weak o = obj;
id tmp = o;

allowWeakReference/retainWeakReference

当allowsWeakReference/retainWeakReference实例方法(没有写入NSObject接口说明文档中)返回NO的情况。

1
2
- (BOOL)allowsWeakReference;
- (BOOL)retainWeakReference;

在赋值给__weak修饰符的变量时,如果allowsWeakReference方法返回NO,程序将异常终止。
对象retain时,如果retainWeakReference方法返回NO, 该变量将使用nil

具体源码分析

以上关于weak的

objc开源部分

weak_unregister_no_lock
怎么理解呢objc_object **referrer?

1
2
objc_object *referent = (objc_object *)referent_id;
objc_object **referrer = (objc_object **)referrer_id;

referent 是一个指针,指向内存上的对象的存储位置.
referrer 只指向referent的指针

1
*referrer = referent

我们要结合remove_referrer这个函数来理解

remove_referrer

1
2
3
4
5
6
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
if (entry->inline_referrers[i] == old_referrer) {
entry->inline_referrers[i] = nil;
return;
}
}

referrer理解

所以我们要拿到referrer 根据这个值来和entry链表中的指针进行比较,如果发现,就nil

(指针搞得我都晕了…佩服c/c++系统工程师)
(阅读源码真的是一件有意思的是哈哈)

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
#include "objc-private.h"
#include "objc-weak.h"
#include <stdint.h>
#include <stdbool.h>
#include <sys/types.h>
#include <libkern/OSAtomic.h>
#define TABLE_SIZE(entry) (entry->mask ? entry->mask + 1 : 0)
static void append_referrer(weak_entry_t *entry, objc_object **new_referrer);
BREAKPOINT_FUNCTION(
void objc_weak_error(void)
);
/**
* Unique hash function for object pointers only.
*
* @param key The object pointer
*
* @return Size unrestricted hash of pointer.
*/
static inline uintptr_t hash_pointer(objc_object *key) {
return ptr_hash((uintptr_t)key);
}
/**
* Unique hash function for weak object pointers only.
*
* @param key The weak object pointer.
*
* @return Size unrestricted hash of pointer.
*/
static inline uintptr_t w_hash_pointer(objc_object **key) {
return ptr_hash((uintptr_t)key);
}
/**
* Grow the entry's hash table of referrers. Rehashes each
* of the referrers.
*
* @param entry Weak pointer hash set for a particular object.
*/
__attribute__((noinline, used))
static void grow_refs_and_insert(weak_entry_t *entry,
objc_object **new_referrer)
{
assert(entry->out_of_line);
size_t old_size = TABLE_SIZE(entry);
size_t new_size = old_size ? old_size * 2 : 8;
size_t num_refs = entry->num_refs;
weak_referrer_t *old_refs = entry->referrers;
entry->mask = new_size - 1;
entry->referrers = (weak_referrer_t *)
calloc(TABLE_SIZE(entry), sizeof(weak_referrer_t));
entry->num_refs = 0;
entry->max_hash_displacement = 0;
for (size_t i = 0; i < old_size && num_refs > 0; i++) {
if (old_refs[i] != nil) {
append_referrer(entry, old_refs[i]);
num_refs--;
}
}
// Insert
append_referrer(entry, new_referrer);
if (old_refs) free(old_refs);
}
/**
* Add the given referrer to set of weak pointers in this entry.
* Does not perform duplicate checking (b/c weak pointers are never
* added to a set twice).
*
* @param entry The entry holding the set of weak pointers.
* @param new_referrer The new weak pointer to be added.
*/
static void append_referrer(weak_entry_t *entry, objc_object **new_referrer)
{
// if is Array implementation
if (! entry->out_of_line) {
// Try to insert inline.
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
if (entry->inline_referrers[i] == nil) {
entry->inline_referrers[i] = new_referrer;
return;
}
}
// Couldn't insert inline. Allocate out of line.
weak_referrer_t *new_referrers = (weak_referrer_t *)
calloc(WEAK_INLINE_COUNT, sizeof(weak_referrer_t));
// This constructed table is invalid, but grow_refs_and_insert
// will fix it and rehash it.
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
new_referrers[i] = entry->inline_referrers[i];
}
entry->referrers = new_referrers;
entry->num_refs = WEAK_INLINE_COUNT;
entry->out_of_line = 1;
entry->mask = WEAK_INLINE_COUNT-1;
entry->max_hash_displacement = 0;
}
assert(entry->out_of_line);
if (entry->num_refs >= TABLE_SIZE(entry) * 3/4) {
return grow_refs_and_insert(entry, new_referrer);
}
//find a place to insert ref
//weak_entry_remove() may bzero() some place
size_t index = w_hash_pointer(new_referrer) & (entry->mask);
size_t hash_displacement = 0;
while (entry->referrers[index] != NULL) {
index = (index+1) & entry->mask;
hash_displacement++;
}
if (hash_displacement > entry->max_hash_displacement) {
entry->max_hash_displacement = hash_displacement;
}
weak_referrer_t &ref = entry->referrers[index];
ref = new_referrer;
entry->num_refs++;
}
/**
* Remove old_referrer from set of referrers, if it's present.
* Does not remove duplicates, because duplicates should not exist.
*
* @todo this is slow if old_referrer is not present. Is this ever the case?
*
* @param entry The entry holding the referrers.
* @param old_referrer The referrer to remove.
*/
static void remove_referrer(weak_entry_t *entry, objc_object **old_referrer)
{
if (! entry->out_of_line) {
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
if (entry->inline_referrers[i] == old_referrer) {
entry->inline_referrers[i] = nil;
return;
}
}
_objc_inform("Attempted to unregister unknown __weak variable "
"at %p. This is probably incorrect use of "
"objc_storeWeak() and objc_loadWeak(). "
"Break on objc_weak_error to debug.\n",
old_referrer);
objc_weak_error();
return;
}
size_t index = w_hash_pointer(old_referrer) & (entry->mask);
size_t hash_displacement = 0;
while (entry->referrers[index] != old_referrer) {
index = (index+1) & entry->mask;
hash_displacement++;
if (hash_displacement > entry->max_hash_displacement) {
_objc_inform("Attempted to unregister unknown __weak variable "
"at %p. This is probably incorrect use of "
"objc_storeWeak() and objc_loadWeak(). "
"Break on objc_weak_error to debug.\n",
old_referrer);
objc_weak_error();
return;
}
}
entry->referrers[index] = nil;
entry->num_refs--;
}
/**
* Add new_entry to the object's table of weak references.
* Does not check whether the referent is already in the table.
*/
static void weak_entry_insert(weak_table_t *weak_table, weak_entry_t *new_entry)
{
weak_entry_t *weak_entries = weak_table->weak_entries;
assert(weak_entries != nil);
//mask may keep entry in array
size_t index = hash_pointer(new_entry->referent) & (weak_table->mask);
size_t hash_displacement = 0;
//hash index 处理
while (weak_entries[index].referent != nil) {
index = (index+1) & weak_table->mask;
hash_displacement++;
}
weak_entries[index] = *new_entry;
weak_table->num_entries++;
//update max_hash_displacement
if (hash_displacement > weak_table->max_hash_displacement) {
weak_table->max_hash_displacement = hash_displacement;
}
}
static void weak_resize(weak_table_t *weak_table, size_t new_size)
{
size_t old_size = TABLE_SIZE(weak_table);
weak_entry_t *old_entries = weak_table->weak_entries;
weak_entry_t *new_entries = (weak_entry_t *)
calloc(new_size, sizeof(weak_entry_t));
weak_table->mask = new_size - 1;
//new
weak_table->weak_entries = new_entries;
weak_table->max_hash_displacement = 0;
weak_table->num_entries = 0; // restored by weak_entry_insert below
//use pointer
if (old_entries) {
weak_entry_t *entry;
weak_entry_t *end = old_entries + old_size;
for (entry = old_entries; entry < end; entry++) {
if (entry->referent) {
weak_entry_insert(weak_table, entry);
}
}
free(old_entries);
}
}
// Grow the given zone's table of weak references if it is full.
static void weak_grow_maybe(weak_table_t *weak_table)
{
size_t old_size = TABLE_SIZE(weak_table);
// Grow if at least 3/4 full.
if (weak_table->num_entries >= old_size * 3 / 4) {
weak_resize(weak_table, old_size ? old_size*2 : 64);
}
}
// Shrink the table if it is mostly empty.
static void weak_compact_maybe(weak_table_t *weak_table)
{
size_t old_size = TABLE_SIZE(weak_table);
// Shrink if larger than 1024 buckets and at most 1/16 full.
if (old_size >= 1024 && old_size / 16 >= weak_table->num_entries) {
weak_resize(weak_table, old_size / 8);
// leaves new table no more than 1/2 full
}
}
/**
* Remove entry from the zone's table of weak references.
*/
static void weak_entry_remove(weak_table_t *weak_table, weak_entry_t *entry)
{
// remove entry
if (entry->out_of_line) free(entry->referrers);
//bzero()函数在由s指向的区域中放置n个0。
bzero(entry, sizeof(*entry));
weak_table->num_entries--;
//maybe resize weak_table
weak_compact_maybe(weak_table);
}
/**
* Return the weak reference table entry for the given referent.
* If there is no entry for referent, return NULL.
* Performs a lookup.
*
* @param weak_table
* @param referent The object. Must not be nil.
*
* @return The table of weak referrers to this object.
*/
static weak_entry_t *
weak_entry_for_referent(weak_table_t *weak_table, objc_object *referent)
{
assert(referent);
weak_entry_t *weak_entries = weak_table->weak_entries;
if (!weak_entries) return nil;
size_t index = hash_pointer(referent) & weak_table->mask;
size_t hash_displacement = 0;
while (weak_table->weak_entries[index].referent != referent) {
index = (index+1) & weak_table->mask;
hash_displacement++;
if (hash_displacement > weak_table->max_hash_displacement) {
return nil;
}
}
return &weak_table->weak_entries[index];
}
/**
* Unregister an already-registered weak reference.
* This is used when referrer's storage is about to go away, but referent
* isn't dead yet. (Otherwise, zeroing referrer later would be a
* bad memory access.)
* Does nothing if referent/referrer is not a currently active weak reference.
* Does not zero referrer.
*
* FIXME currently requires old referent value to be passed in (lame)
* FIXME unregistration should be automatic if referrer is collected
*
* @param weak_table The global weak table.
* @param referent The object.
* @param referrer The weak reference.
*/
void
weak_unregister_no_lock(weak_table_t *weak_table, id referent_id,
id *referrer_id)
{
objc_object *referent = (objc_object *)referent_id;
objc_object **referrer = (objc_object **)referrer_id;
weak_entry_t *entry;
if (!referent) return;
if ((entry = weak_entry_for_referent(weak_table, referent))) {
remove_referrer(entry, referrer);
bool empty = true;
//after unregister the entry's referrers is empty?
// Hash implementation
if (entry->out_of_line && entry->num_refs != 0) {
empty = false;
}
// Array implementation
else {
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
if (entry->inline_referrers[i]) {
empty = false;
break;
}
}
}
// if entry.references empty
if (empty) {
weak_entry_remove(weak_table, entry);
}
}
// Do not set *referrer = nil. objc_storeWeak() requires that the
// value not change.
}
/**
* Registers a new (object, weak pointer) pair. Creates a new weak
* object entry if it does not exist.
*
* @param weak_table The global weak table.
* @param referent The object pointed to by the weak reference.
* @param referrer The weak pointer address.
*/
id
weak_register_no_lock(weak_table_t *weak_table, id referent_id,
id *referrer_id, bool crashIfDeallocating)
{
//object
objc_object *referent = (objc_object *)referent_id;
//The Point which point the object
objc_object **referrer = (objc_object **)referrer_id;
if (!referent || referent->isTaggedPointer()) return referent_id;
// ensure that the referenced object is viable
// judge is Allows Weak Reference
bool deallocating;
if (!referent->ISA()->hasCustomRR()) {
deallocating = referent->rootIsDeallocating();
}
else {
BOOL (*allowsWeakReference)(objc_object *, SEL) =
(BOOL(*)(objc_object *, SEL))
object_getMethodImplementation((id)referent,
SEL_allowsWeakReference);
if ((IMP)allowsWeakReference == _objc_msgForward) {
return nil;
}
deallocating =
! (*allowsWeakReference)(referent, SEL_allowsWeakReference);
}
if (deallocating) {
if (crashIfDeallocating) {
_objc_fatal("Cannot form weak reference to instance (%p) of "
"class %s. It is possible that this object was "
"over-released, or is in the process of deallocation.",
(void*)referent, object_getClassName((id)referent));
} else {
return nil;
}
}
// now remember it and where it is being stored
weak_entry_t *entry;
if ((entry = weak_entry_for_referent(weak_table, referent))) {
append_referrer(entry, referrer);
}
else {
weak_entry_t new_entry;
new_entry.referent = referent;
new_entry.out_of_line = 0;
new_entry.inline_referrers[0] = referrer;
for (size_t i = 1; i < WEAK_INLINE_COUNT; i++) {
new_entry.inline_referrers[i] = nil;
}
weak_grow_maybe(weak_table);
weak_entry_insert(weak_table, &new_entry);
}
// Do not set *referrer. objc_storeWeak() requires that the
// value not change.
return referent_id;
}
#if DEBUG
bool
weak_is_registered_no_lock(weak_table_t *weak_table, id referent_id)
{
return weak_entry_for_referent(weak_table, (objc_object *)referent_id);
}
#endif
/**
* Called by dealloc; nils out all weak pointers that point to the
* provided object so that they can no longer be used.
*
* @param weak_table
* @param referent The object being deallocated.
*/
void
weak_clear_no_lock(weak_table_t *weak_table, id referent_id)
{
//referent objc
objc_object *referent = (objc_object *)referent_id;
//referent objc entry(which save many referents)
weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
if (entry == nil) {
/// XXX shouldn't happen, but does with mismatched CF/objc
//printf("XXX no entry for clear deallocating %p\n", referent);
return;
}
// zero out references
weak_referrer_t *referrers;
size_t count;
if (entry->out_of_line) {
referrers = entry->referrers;
count = TABLE_SIZE(entry);
}
else {
referrers = entry->inline_referrers;
count = WEAK_INLINE_COUNT;
}
//entry->referrers all nil
for (size_t i = 0; i < count; ++i) {
objc_object **referrer = referrers[i];
if (referrer) {
if (*referrer == referent) {
*referrer = nil;
}
else if (*referrer) {
_objc_inform("__weak variable at %p holds %p instead of %p. "
"This is probably incorrect use of "
"objc_storeWeak() and objc_loadWeak(). "
"Break on objc_weak_error to debug.\n",
referrer, (void*)*referrer, (void*)referent);
objc_weak_error();
}
}
}
weak_entry_remove(weak_table, entry);
}
/**
* This function gets called when the value of a weak pointer is being
* used in an expression. Called by objc_loadWeakRetained() which is
* ultimately called by objc_loadWeak(). The objective is to assert that
* there is in fact a weak pointer(s) entry for this particular object being
* stored in the weak-table, and to retain that object so it is not deallocated
* during the weak pointer's usage.
*
* @param weak_table
* @param referrer The weak pointer address.
*/
/*
Once upon a time we eagerly cleared *referrer if we saw the referent
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
weak_read_no_lock(weak_table_t *weak_table, id *referrer_id)
{
objc_object **referrer = (objc_object **)referrer_id;
objc_object *referent = *referrer;
//Detection Tagged Pointer
if (referent->isTaggedPointer()) return (id)referent;
weak_entry_t *entry;
// referent == nil or entry == nil
if (referent == nil ||
!(entry = weak_entry_for_referent(weak_table, referent)))
{
return nil;
}
//Custom RR denotes a custom retain-release implementation
//
if (! referent->ISA()->hasCustomRR()) {
//???question
if (! referent->rootTryRetain()) {
return nil;
}
}
//has isa
else {
BOOL (*tryRetain)(objc_object *, SEL) = (BOOL(*)(objc_object *, SEL))
object_getMethodImplementation((id)referent,
SEL_retainWeakReference);
//IMP != _objc_magForward
if ((IMP)tryRetain == _objc_msgForward) {
return nil;
}
//IMP != nil
if (! (*tryRetain)(referent, SEL_retainWeakReference)) {
return nil;
}
}
return (id)referent;
}
script>