美文网首页
iOS内存管理-week和关联对象怎么释放(1)

iOS内存管理-week和关联对象怎么释放(1)

作者: 写代码的小农民 | 来源:发表于2021-01-10 13:54 被阅读0次

iOS是通过引用计数来管理对象的生命周期的,当引用计数起为0的时候会调用delloc函数释放该对象,在64bit中,引用计数可以直接存储在优化过的isa指针中,也可能存储在SideTable类中:

image.png
  • refcnts 是一个存放着对象引用计数的散列表,当isa中存储不下的时候会存储在refcnts中。执行SideTable_release操作的时候引用计数会减一,引用计数器为0的时候会通过message_send执行delloc函数,会在refcnts表中找到对象并释放。

  • weak_table 一个全局的weak 引用哈希表:存放弱引用指针,会在弱引用的对象释放的时候,通过该弱引用对象的地址找到弱引用指针,释放并至为nil;

面试

  • runtime 是怎么实现weak置nil的
  • weak修饰的释放则自动被置为nil的实现原理
  • ARC帮我们做了什么?

delloc方法释放对象

当一个对象的引用计数为0的时候会执行delloc函数:

// Replaced by NSZombies
- (void)dealloc {
    _objc_rootDealloc(self);
}

void
_objc_rootDealloc(id obj)
{
    assert(obj);

    obj->rootDealloc();
}

然后会通过对象的isa指针判断对象是否有弱引用,C++析构函数,关联对象,如果没有直接执行free()释放该对象,如果有执行object_dispose();

inline void
objc_object::rootDealloc()
{
    if (isTaggedPointer()) return;  // fixme necessary?

    if (fastpath(isa.nonpointer  &&  
                 !isa.weakly_referenced  &&  
                 !isa.has_assoc  &&  
                 !isa.has_cxx_dtor  &&  
                 !isa.has_sidetable_rc))
    {
        assert(!sidetable_present());
        free(this);
    } 
    else {
        object_dispose((id)this);
    }
}

object_dispose在执行free(obj)释放对象前,会执行objc_destructInstance(obj)释放关联对象,弱引用,C++析构函数

/***********************************************************************
* object_dispose
* fixme
* Locking: none
**********************************************************************/
id 
object_dispose(id obj)
{
    if (!obj) return nil;

    objc_destructInstance(obj);    
    free(obj);

    return nil;
}

释放弱引用,C++析构函数,然后执行clearDeallocating释放关联对象;

/***********************************************************************
* objc_destructInstance
* Destroys an instance without freeing memory. 
* Calls C++ destructors.
* Calls ARC ivar cleanup.
* Removes associative references.
* Returns `obj`. Does nothing if `obj` is 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);
        if (assoc) _object_remove_assocations(obj);
        obj->clearDeallocating();
    }

    return obj;
}

根据isa.nonpointer判断isa类型是否是64位优化后的指针;

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());
}

weak_clear_no_lock(&table.weak_table, (id)this);获取全局weak_table表, 并把对象地址传入,通过对象地址&table 算出散列表中的下标,然后清除引用的对象;

// Slow path of clearDeallocating() 
// for objects with nonpointer isa
// that were ever weakly referenced 
// or whose retain count ever overflowed to the side table.
NEVER_INLINE void
objc_object::clearDeallocating_slow()
{
    assert(isa.nonpointer  &&  (isa.weakly_referenced || isa.has_sidetable_rc));

    SideTable& table = SideTables()[this];
    table.lock();
    if (isa.weakly_referenced) {
        weak_clear_no_lock(&table.weak_table, (id)this);
    }
    if (isa.has_sidetable_rc) {
        table.refcnts.erase(this);
    }
    table.unlock();
}

通过这个方法获取entry
weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
然后执行 weak_entry_remove(weak_table, entry) 从weak_table表中移除,然后执行free()释放该对象;

/** 
 * 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) 
{
    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) {
            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);
}

/** 
 * 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 begin = hash_pointer(referent) & weak_table->mask;
    size_t index = begin;
    size_t hash_displacement = 0;
    while (weak_table->weak_entries[index].referent != referent) {
        index = (index+1) & weak_table->mask;
        if (index == begin) bad_weak_table(weak_table->weak_entries);
        hash_displacement++;
        if (hash_displacement > weak_table->max_hash_displacement) {
            return nil;
        }
    }
    
    return &weak_table->weak_entries[index];
}

面试

  • weak修饰的释放则自动被置为nil的实现原理?

1,创建person对象, 并被person2弱引用了,所以person2这个弱引用指针就会放在全局的weak_table表中;

2,当释放要person对象的时候,通过runtime去weak_table表中找到person的弱引用person2,然后释放person2这个弱引用指针,并且设置person2=nil,然后再释放person对象;

  __strong Person *person1;
  __weak Person *person2;
  __unsafe_unretained Person *person3;
    
    NSLog(@"111");
    
    {
        Person *person = [[Person alloc] init];
        
        person2 = person;
    }
    
    NSLog(@"222 - %@", person2);


 111
 [Person dealloc]
 222 - (null)

特别补充一点,如有需要会通过erase函数擦掉SideTable表中person的引用计数:

 if (isa.has_sidetable_rc) {
        table.refcnts.erase(this);
    }
  • ARC帮我们做了什么?

ARC 其实就是LLVM + runtime,LLVM会在编译阶段自动帮我们添加[object release],然后会在runtime阶段根据引用计数判断,当引用计数为0的时候会通过isa指针的信息抹除对象的一些引用,这些都是runtime的功劳。

相关文章

  • iOS内存管理-week和关联对象怎么释放(1)

    iOS是通过引用计数来管理对象的生命周期的,当引用计数起为0的时候会调用delloc函数释放该对象,在64bit中...

  • iOS内存管理-week和关联对象怎么释放(2)

    关联对象可以为category添加成员变量,因为我们虽然可以通过category为类添加属性,但是只是生成了方法声...

  • iOS(OC)常见面试题

    1.iOS怎么管理内存?在ObjC中对象时存储在堆中的,系统并不会自动释放堆中的内存(注意基本类型是由系统自己管理...

  • iOS内存管理

    iOS内存管理需要了解这几个方面: 内存布局 引用计数 自动释放池 循环引用和core foundation对象的...

  • 内存管理:不看白不看,看了就是赚

    一、iOS的内存管理方式 1、小对象的内存管理 -- Tagged Pointer 2、普通对象的内存管理 -- ...

  • iOS内存管理

    摘自《iOS程序员面试笔试宝典》 一、内存管理 1.什么是内存泄漏?什么是安全释放 内存泄漏指动态分配内存的对象在...

  • iOS 关联对象 objc_setAssociatedObjec

    iOS 关联对象 objc_setAssociatedObject ,从源码探讨原理,以及释放时机 1.objc_...

  • Java--垃圾回收原理

    ·内存管理  Java的内存管理很大程度指的就是对象的管理,其中包括对象空间的分配和释放。  对象空间的分配:使用...

  • kkbox-ios-dev笔记(三) - 内存管理/代理

    内存管理(一) 内存泄漏:该释放的对象, 没有被释放(已经不再使用的对象, 没有被释放)无效内存引用:内存已经被释...

  • iOS中copy和mutableCopy详解

    在 iOS 中,最为重要的一点就是内存管理。而在内存管理时,有时会遇到 copy 出来的对象,是否需要释放内存的情...

网友评论

      本文标题:iOS内存管理-week和关联对象怎么释放(1)

      本文链接:https://www.haomeiwen.com/subject/rlfboktx.html