iOS weak原理源码探究
2021-05-13 本文已影响0人
水煮杰尼龟
- 弱引用时干了什么,如下代码来看看
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSObject *obj = [[NSObject alloc]init];
__weak NSObject *weakObj = obj;
}
return 0;
}
- 通过
clang
来看看cpp
代码
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_5c_vc7szrdj0xj63p71bn1fz8n80000gn_T_main_32a486_mi_0);
NSObject *obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
__attribute__((objc_ownership(weak))) NSObject *weakObj = obj;
}
return 0;
}
- 可以看出是通过
objc_ownership
来实现,但是这样也没法追踪下去了
那么转换成.ll中间文件来看看
define i32 @main(i32 %0, i8** %1) #1 {
%3 = alloca i32, align 4
%4 = alloca i32, align 4
%5 = alloca i8**, align 8
%6 = alloca %0*, align 8
%7 = alloca %0*, align 8
store i32 0, i32* %3, align 4
store i32 %0, i32* %4, align 4
store i8** %1, i8*** %5, align 8
%8 = call i8* @llvm.objc.autoreleasePoolPush() #2
notail call void (i8*, ...) @NSLog(i8* bitcast (%struct.__NSConstantString_tag* @_unnamed_cfstring_ to i8*))
%9 = load %struct._class_t*, %struct._class_t** @"OBJC_CLASSLIST_REFERENCES_$_", align 8
%10 = bitcast %struct._class_t* %9 to i8*
%11 = call i8* @objc_alloc_init(i8* %10)
%12 = bitcast i8* %11 to %0*
store %0* %12, %0** %6, align 8
%13 = load %0*, %0** %6, align 8
%14 = bitcast %0** %7 to i8**
%15 = bitcast %0* %13 to i8*
%16 = call i8* @llvm.objc.initWeak(i8** %14, i8* %15) #2
%17 = bitcast %0** %7 to i8**
call void @llvm.objc.destroyWeak(i8** %17) #2
%18 = bitcast %0** %6 to i8**
call void @llvm.objc.storeStrong(i8** %18, i8* null) #2
call void @llvm.objc.autoreleasePoolPop(i8* %8)
ret i32 0
}
- 可以看到
%16 = call i8* @llvm.objc.initWeak(i8** %14, i8* %15) #2
,是调用了objc
的initWeak
方法,那么就去objc
的源码里捋这个方法就行了,下面基本都是在源码里注释探索。
objc_initWeak
id
objc_initWeak(id *location, id newObj)
{
if (!newObj) {
*location = nil;
return nil;
}
return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
(location, (objc_object*)newObj);
}
-
id *location
:注释上能看出,即__weak指针的地址,如文章开头例子中的weakObj
的地址 -
id newObj
:引用的对象,即文章开头例子中的obj
-
而后
return
了storeWeak
方法,这个就是核心实现了
storeWeak
enum HaveOld { DontHaveOld = false, DoHaveOld = true };
enum HaveNew { DontHaveNew = false, DoHaveNew = true };
enum CrashIfDeallocating {
DontCrashIfDeallocating = false, DoCrashIfDeallocating = true
};
template <HaveOld haveOld, HaveNew haveNew,
CrashIfDeallocating crashIfDeallocating>
static id
storeWeak(id *location, objc_object *newObj)
{
ASSERT(haveOld || haveNew);
if (!haveNew) ASSERT(newObj == nil);
Class previouslyInitializedClass = nil;
id oldObj;
SideTable *oldTable;
SideTable *newTable;
retry:
if (haveOld) {///如果weak ptr之前弱引用过一个obj,则将这个obj所对应的SideTable取出,赋值给oldTable
oldObj = *location;
oldTable = &SideTables()[oldObj];
} else {
oldTable = nil;/// 如果weak ptr之前没有弱引用过一个obj,则oldTable = nil
}
if (haveNew) {/// 如果weak ptr要weak引用一个新的obj,则将该obj对应的SideTable取出,赋值给newTable
newTable = &SideTables()[newObj];
} else {
newTable = nil;/// 如果weak ptr不需要引用一个新obj,则newTable = nil
}
/// 加锁
SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);
/// location 应该与 oldObj 保持一致,如果不同,说明当前的 location 已经处理过 oldObj 可是又被其他线程所修改
if (haveOld && *location != oldObj) {
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
goto retry;
}
if (haveNew && newObj) {
Class cls = newObj->getIsa();
if (cls != previouslyInitializedClass &&
!((objc_class *)cls)->isInitialized())
{/// 如果cls还没有初始化,先初始化,再尝试设置weak
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
class_initialize(cls, (id)newObj);
previouslyInitializedClass = cls;
goto retry;/// 重新获取一遍newObj,这时的newObj应该已经初始化过了
}
}
// Clean up old value, if any.
if (haveOld) {
// 如果weak_ptr之前弱引用过别的对象oldObj,则调用weak_unregister_no_lock,在oldObj的weak_entry_t中移除该weak_ptr地址
weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
}
// Assign new value, if any.
if (haveNew) {// 如果weak_ptr需要弱引用新的对象newObj
// 1. 调用weak_register_no_lock方法,将weak ptr的地址记录到newObj对应的weak_entry_t中
newObj = (objc_object *)
weak_register_no_lock(&newTable->weak_table, (id)newObj, location,
crashIfDeallocating);
// 2. 更新newObj的isa的weakly_referenced bit标志位
if (newObj && !newObj->isTaggedPointer()) {
newObj->setWeaklyReferenced_nolock();
}
// 3. *location 赋值,也就是将weak ptr直接指向了newObj。可以看到,这里并没有将newObj的引用计数+1
*location = (id)newObj;// 将weak ptr指向object
}
else {
// No new value. The storage is not changed.
}
// 解锁
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
return (id)newObj;// 返回newObj
}
-
storeWeak
方法接受5个参数,其中HaveOld haveOld, HaveNew haveNew, CrashIfDeallocating crashIfDeallocating
3个枚举分别传入的是false,true,true
,分别表示:weak ptr
之前是否已经指向了一个弱引用,weak ptr
是否需要指向一个新引用, 如果被弱引用的对象正在析构,此时再弱引用该对象,是否应该crash
。 - 结合上述代码注释应该大致能捋清楚
storeWeak
干了啥
这里主要涉及到2个函数(weak_unregister_no_lock和weak_register_no_lock)以及SideTable数据结构
我们具体来看看
SideTable
struct SideTable {
spinlock_t slock;/// 自旋锁
RefcountMap refcnts;/// 存储对象引用计数的map
weak_table_t weak_table;/// 存储对象弱引用的map
SideTable() {
memset(&weak_table, 0, sizeof(weak_table));
}
~SideTable() {
_objc_fatal("Do not delete SideTable.");
}
};
-
SideTable
存储着对象的引用计数以及weak
引用,而一个个的SideTable
又构成了一个集合,叫SideTables
。SideTables
在系统中是全局唯一的。 -
newTable = &SideTables()[newObj]
可以看到通过对象获取到其SideTable
,而其内部是通过StripedMap
对应的算法获取的,参数是对象地址。
class StripedMap {
static unsigned int indexForPointer(const void *p) {
uintptr_t addr = reinterpret_cast<uintptr_t>(p);
return ((addr >> 4) ^ (addr >> 9)) % StripeCount;
}
}
RefcountMap
struct RefcountMapValuePurgeable {
static inline bool isPurgeable(size_t x) {
return x == 0;
}
};
typedef objc::DenseMap<DisguisedPtr<objc_object>,size_t,RefcountMapValuePurgeable> RefcountMap;
- 可以简单理解为一个
map
,key
是DisguisedPtr<objc_object>
类型,value
是size_t
类型,还多了一个
RefcountMapValuePurgeable
:是否可清除数据,内部通过判断引用计数为0
weak_table_t
struct weak_table_t {
weak_entry_t *weak_entries;/// 存储弱引用对象的相关信息
size_t num_entries;/// 元素个数
uintptr_t mask; /// hash数组长度-1,用于和hash值做位与计算,来确定数组下标(hash数组的长度,而不是元素个数。比如,数组长度可能是64,而元素个数仅存了2个)
uintptr_t max_hash_displacement;/// 可能会发生的hash冲突的最大次数
};
struct weak_entry_t {
DisguisedPtr<objc_object> referent;// 被弱引用的对象
// 引用该对象的对象列表,联合
union {
struct {/// 引用个数大于4
weak_referrer_t *referrers;// 弱引用该对象的对象列表的动态数组
uintptr_t out_of_line_ness : 2;// 是否使用动态数组标记位
uintptr_t num_refs : PTR_MINUS_2;// 动态数组中元素的个数
uintptr_t mask; // 用于hash确定动态数组index
uintptr_t max_hash_displacement;// 最大的hash冲突次数
};
struct {/// 引用个数小于4
// out_of_line_ness field is low bits of inline_referrers[1]
weak_referrer_t inline_referrers[WEAK_INLINE_COUNT];
};
};
bool out_of_line() {
return (out_of_line_ness == REFERRERS_OUT_OF_LINE);
}
weak_entry_t& operator=(const weak_entry_t& other) {
memcpy(this, &other, sizeof(other));
return *this;
}
weak_entry_t(objc_object *newReferent, objc_object **newReferrer)
: referent(newReferent)
{
inline_referrers[0] = newReferrer;
for (int i = 1; i < WEAK_INLINE_COUNT; i++) {
inline_referrers[i] = nil;
}
}
};
-
weak_entries
实质上是一个hash
数组,数组中存储weak_entry_t
类型的元素
引用计数
- 看一下OC是如何获取引用计数的
inline uintptr_t
objc_object::rootRetainCount()
{
/// tagged pointer,直接返回this ,不需要引用计数的
if (isTaggedPointer()) return (uintptr_t)this;
sidetable_lock();
isa_t bits = LoadExclusive(&isa.bits);
ClearExclusive(&isa.bits);
/// 采用了优化的isa指针
if (bits.nonpointer) {
/// 先读取isa.extra_rc
uintptr_t rc = 1 + bits.extra_rc;
if (bits.has_sidetable_rc) {///如果extra_rc不够大, 还需要读取sidetable中的数据
rc += sidetable_getExtraRC_nolock();
}
sidetable_unlock();
return rc;
}
sidetable_unlock();
/// 未采用了优化的isa指针,直接返回sidetable中的数据
return sidetable_retainCount();
}
weak_register_no_lock
- 将
weak ptr
地址 注册到obj
对应的weak_entry_t
中
weak_register_no_lock(&newTable->weak_table, (id)newObj, location,
crashIfDeallocating);
id
weak_register_no_lock(weak_table_t *weak_table, id referent_id,
id *referrer_id, bool crashIfDeallocating)
{
objc_object *referent = (objc_object *)referent_id;
objc_object **referrer = (objc_object **)referrer_id;
///referent为nil 或 referent 采用了TaggedPointer计数方式,直接返回
if (!referent || referent->isTaggedPointer()) return referent_id;
/// 确保被引用的对象可用
bool deallocating;
if (!referent->ISA()->hasCustomRR()) {
deallocating = referent->rootIsDeallocating();
}
else {
BOOL (*allowsWeakReference)(objc_object *, SEL) =
(BOOL(*)(objc_object *, SEL))
object_getMethodImplementation((id)referent,
@selector(allowsWeakReference));
if ((IMP)allowsWeakReference == _objc_msgForward) {
return nil;
}
deallocating =
! (*allowsWeakReference)(referent, @selector(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;
///在 weak_table中通过referent找对应的weak_entry
if ((entry = weak_entry_for_referent(weak_table, referent))) {/// 找到了
/// 将referrer插入到weak_entry_t的引用数组中
append_referrer(entry, referrer);
}
else {/// 没找到
/// 新建一个
weak_entry_t new_entry(referent, referrer);
weak_grow_maybe(weak_table);///是否需要动态扩容
weak_entry_insert(weak_table, &new_entry);///将weak_entry_t插入到weak_table中
}
// Do not set *referrer. objc_storeWeak() requires that the
// value not change.
return referent_id;
}
weak_entry_for_referent
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;
/// 通过referent进行hash算法 ,将hash值和mask进行位与运算,得到初始数组下标
size_t begin = hash_pointer(referent) & weak_table->mask;
size_t index = begin;
size_t hash_displacement = 0;
///通过index拿到的referent与上面的referent不相同
while (weak_table->weak_entries[index].referent != referent) {
index = (index+1) & weak_table->mask; /// index+1 ,找下一个位置,基于二进制运算,当寻找到最后一个位置时,它又会自动让你从数组的第一个位置开始寻找
/// 在数组中转了一圈还没找到目标元素 触发bad_weak_table crash
if (index == begin) bad_weak_table(weak_table->weak_entries);
hash_displacement++;
if (hash_displacement > weak_table->max_hash_displacement) {
/// hash冲突大于了最大可能的冲突次数,说明目标对象不存在于数组中,返回nil
return nil;
}
}
return &weak_table->weak_entries[index];
}
append_referrer
static void append_referrer(weak_entry_t *entry, objc_object **new_referrer)
{
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.
///inline_referrers存满了,转为动态数组
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_ness = REFERRERS_OUT_OF_LINE;
entry->mask = WEAK_INLINE_COUNT-1;
entry->max_hash_displacement = 0;
}
/// 断言: 此时使用的动态数组
ASSERT(entry->out_of_line());
/// 如果动态数组中元素个数大于或等于数组位置总空间的3/4
if (entry->num_refs >= TABLE_SIZE(entry) * 3/4) {
///扩展数组空间为当前长度的一倍 size_t new_size = old_size ? old_size * 2 : 8;
return grow_refs_and_insert(entry, new_referrer);
}
// 不需要扩容,直接插入到weak_entry中 ,这里的hash算法与weak_entry_for_referent里的一样
size_t begin = w_hash_pointer(new_referrer) & (entry->mask);
size_t index = begin;
size_t hash_displacement = 0;
while (entry->referrers[index] != nil) {
hash_displacement++;
index = (index+1) & entry->mask;
if (index == begin) bad_weak_table(entry);
}
if (hash_displacement > entry->max_hash_displacement) {
entry->max_hash_displacement = hash_displacement;
}
/// 将new_referrer存入hash数组,并更新元素个数num_refs
weak_referrer_t &ref = entry->referrers[index];
ref = new_referrer;
entry->num_refs++;
}
weak_grow_maybe
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.
/// 当大于现有长度的3/4时,进行扩容。
if (weak_table->num_entries >= old_size * 3 / 4) {
weak_resize(weak_table, old_size ? old_size*2 : 64);
}
}
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数据
weak_table->mask = new_size - 1;
weak_table->weak_entries = new_entries;
weak_table->max_hash_displacement = 0;
weak_table->num_entries = 0; // restored by weak_entry_insert below
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);
}
}
weak_entry_insert
/// 插入entry进entries里
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);
size_t begin = hash_pointer(new_entry->referent) & (weak_table->mask);
size_t index = begin;
size_t hash_displacement = 0;
while (weak_entries[index].referent != nil) {
index = (index+1) & weak_table->mask;
if (index == begin) bad_weak_table(weak_entries);
hash_displacement++;
}
weak_entries[index] = *new_entry;
weak_table->num_entries++;
if (hash_displacement > weak_table->max_hash_displacement) {
weak_table->max_hash_displacement = hash_displacement;
}
}
weak_unregister_no_lock
- 如果
weak_ptr
之前弱引用过别的对象oldObj
,则调用weak_unregister_no_lock
,在oldObj
的weak_entry_t
中移除该weak_ptr
地址
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))) {///找到referent所对应的weak_entry_t
remove_referrer(entry, referrer);///移除referrer
/// 检查weak_entry_t的hash数组是否已经空了
bool empty = true;
if (entry->out_of_line() && entry->num_refs != 0) {
empty = false;
}
else {
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
if (entry->inline_referrers[i]) {
empty = false;
break;
}
}
}
if (empty) {///空了,将weak_entry_t从weak_table中移除
weak_entry_remove(weak_table, entry);
}
}
}
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(entry, sizeof(*entry));
weak_table->num_entries--;
weak_compact_maybe(weak_table);
}
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.
/// 当前数组长度大于1024,且实际使用空间最多只有1/16时
if (old_size >= 1024 && old_size / 16 >= weak_table->num_entries) {
weak_resize(weak_table, old_size / 8);/// 缩小8倍
// leaves new table no more than 1/2 full
}
}
Dealloc
- 当对象释放的时候,weak引用的释放。
inline void
objc_object::rootDealloc()
{
if (isTaggedPointer()) return; // fixme necessary?
if (fastpath(isa.nonpointer && ///是否isa优化
!isa.weakly_referenced && ///是否弱引用
!isa.has_assoc && /// 是否有关联对象
!isa.has_cxx_dtor && ///是否自定义c++析构
!isa.has_sidetable_rc))/// 是否用到sidetable
{
assert(!sidetable_present());
free(this);
}
else {
object_dispose((id)this);
}
}
- 有弱引用的时候,走
object_dispose
id
object_dispose(id obj)
{
if (!obj) return nil;
objc_destructInstance(obj);
free(obj);
return nil;
}
void *objc_destructInstance(id obj)
{
if (obj) {
// Read all of the flags at once for performance.
bool cxx = obj->hasCxxDtor();
bool assoc = obj->hasAssociatedObjects();
// This order is important.
if (cxx) object_cxxDestruct(obj);/// 调用C++析构函数
if (assoc) _object_remove_assocations(obj);// 关联对象remove相关
obj->clearDeallocating();// 清理引用
}
return obj;
}
inline void
objc_object::clearDeallocating()
{
if (slowpath(!isa.nonpointer)) {
// Slow path for raw pointer isa.
sidetable_clearDeallocating();
}
else if (slowpath(isa.weakly_referenced || isa.has_sidetable_rc)) {
// Slow path for non-pointer isa with weak refs and/or side table data.
clearDeallocating_slow();
}
assert(!sidetable_present());
}
NEVER_INLINE void
objc_object::clearDeallocating_slow()
{
ASSERT(isa.nonpointer && (isa.weakly_referenced || isa.has_sidetable_rc));
///以this指针为key,找到对应的SideTable
SideTable& table = SideTables()[this];
table.lock();
if (isa.weakly_referenced) {/// 弱引用
weak_clear_no_lock(&table.weak_table, (id)this);
}
if (isa.has_sidetable_rc) {/// 采用了SideTable
table.refcnts.erase(this);/// SideTable移除this
}
table.unlock();
}
weak_clear_no_lock
void
weak_clear_no_lock(weak_table_t *weak_table, id referent_id)
{
objc_object *referent = (objc_object *)referent_id;
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;
}
for (size_t i = 0; i < count; ++i) {
objc_object **referrer = referrers[i];
if (referrer) {
///将weak ptr设置为nil,这也就是为什么weak 指针会自动设置为nil的原因
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();
}
}
}
///将referent的weak_entry_t移除出weak_table
weak_entry_remove(weak_table, entry);
}
实践看看引用计数和weak引用
- 现在苹果基本都是采用了
isa
指针优化的,即isa
指针不只是表示class
类型,而是一个占用64位的结构体
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
# define ISA_BITFIELD \
///1 表示开启了isa优化,0 表示没有启用isa优化
uintptr_t nonpointer : 1; \
///对象是否有关联对象
uintptr_t has_assoc : 1; \
///对象是否有C++或ARC析构函数
uintptr_t has_cxx_dtor : 1; \
///类指针的值
uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
///固定为0x1a,用于在调试时区分对象是否已经初始化
uintptr_t magic : 6; \
///对象是否被别的对象弱引用
uintptr_t weakly_referenced : 1; \
///对象是否正在被释放
uintptr_t deallocating : 1; \
///是否引用计数过大,借用sidetable来存储
uintptr_t has_sidetable_rc : 1; \
///对象的引用计数减1
uintptr_t extra_rc : 19
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
# define ISA_MAGIC_MASK 0x001f800000000001ULL
# define ISA_MAGIC_VALUE 0x001d800000000001ULL
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t deallocating : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 8
# define RC_ONE (1ULL<<56)
# define RC_HALF (1ULL<<7)
# else
# error unknown architecture for packed isa
# endif
-
我这边是在
Mac
跑的可编译源码,所以对应的是x86_64
-
首先在
NSObject *obj = [[NSObject alloc]init];
代码后打上断点,先看看obj
的引用计数,很明显引用计数是1,extra_rc
是isa
的后8位,那我们就来看看是不是1 -
断点后,
x/4gx obj
打印obj
的4段内存,第一段即是isa
- 用编程计算机看二进制位
- 可以看到
extra_rc
是0000 0000
,它的值是引用计数减1,所以引用计数为0+1=1
再new一个obj持有看看
NSObject *obj = [[NSObject alloc]init];
NSObject *obj1 = obj;
x:4gx1.png
extra_rc1.png
- 可以看到
extra_rc
变成0000 0001
了,引用计数为1+1=2
。
下面再来验证一下weak引用
NSObject *obj = [[NSObject alloc]init];
__weak NSObject *weakObj = obj;
-
weak
不会增加引用计数,那么这里obj引用计数还是1,而且weakly_referenced
位应该为1。
- 可以看出
weakly_referenced
确实变成1了,而引用计数还是0+1=1
,符合预期。