oc runtime之weak
来源:互联网 发布:1hhhh域名升级访问中 编辑:程序博客网 时间:2024/06/05 11:54
二、weak
声明了弱引用,实际上调用了objc_initWeak函数
/** * 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. */idobjc_initWeak(id *location, id newObj){ if (!newObj) { *location = nil; return nil; } return storeWeak<false/*old*/, true/*new*/, true/*crash*/> (location, (objc_object*)newObj);}
我们可以看到它最终调用了storeWeak函数,那我们再看看这个函数
// 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.template <bool HaveOld, bool HaveNew, bool 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; // 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]; } else { oldTable = nil; } if (HaveNew) { newTable = &SideTables()[newObj]; } else { newTable = nil; } SideTable::lockTwo<HaveOld, HaveNew>(oldTable, newTable); 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. if (HaveNew && newObj) { Class cls = newObj->getIsa(); if (cls != previouslyInitializedClass && !((objc_class *)cls)->isInitialized()) { SideTable::unlockTwo<HaveOld, HaveNew>(oldTable, newTable); _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. previouslyInitializedClass = cls; goto retry; } } // 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. } SideTable::unlockTwo<HaveOld, HaveNew>(oldTable, newTable); return (id)newObj;}
这里先获得oldTable和newTable,当然如果有的话,然后对它们上锁。下面有两个重试,第一个情况是,如果old value被改了,则解锁重试去取新的sideTable;第二个情况是,为了避免死锁。在弱引用机制和+initialize机制这两个机制下,要保证所有的弱引用对象必须完成了initialize,如果没有完成,会去调用_class_initialize函数来完成,然后设置previouslyInitializedClass条件来标置已完成了,解锁重试。
1、weak_register_no_lock
PRIVATE_EXTERN id weak_register_no_lock(weak_table_t *weak_table, id referent, id *referrer) { if (referent) { // ensure that the referenced object is viable BOOL (*allowsWeakReference)(id, SEL) = (BOOL(*)(id, SEL)) class_getMethodImplementation(object_getClass(referent), @selector(allowsWeakReference)); if ((IMP)allowsWeakReference != _objc_msgForward) { if (! (*allowsWeakReference)(referent, @selector(allowsWeakReference))) { _objc_fatal("cannot form weak reference to instance (%p) of class %s", referent, object_getClassName(referent)); } } else { return NULL; } // now remember it and where it is being stored weak_entry_t *entry; if ((entry = weak_entry_for_referent(weak_table, referent))) { append_referrer_no_lock(&entry->referrers, referrer); } else { weak_entry_t new_entry; new_entry.referent = referent; new_entry.referrers.refs = NULL; new_entry.referrers.num_refs = 0; new_entry.referrers.num_allocated = 0; append_referrer_no_lock(&new_entry.referrers, referrer); weak_table->num_weak_refs++; weak_grow_maybe_no_lock(weak_table); weak_entry_insert_no_lock(weak_table, &new_entry); } } // Do not set *referrer. objc_storeWeak() requires that the // value not change. return referent;}
函数先调用allowsWeakReference来判断是否允许弱引用,下面调用weak_entry_for_referent函数来查询weak_table里是否已有referent的entry,有的话将referrer加入到entry->referrers,没有的话创建new_entry,将其插入weak_table
2、weak_unregister_no_lock
// 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 collectedPRIVATE_EXTERN void weak_unregister_no_lock(weak_table_t *weak_table, id referent, id *referrer){ weak_entry_t *entry; if ((entry = weak_entry_for_referent(weak_table, referent))) { remove_referrer_no_lock(&entry->referrers, referrer); if (entry->referrers.num_refs == 0) { weak_entry_remove_no_lock(weak_table, entry); weak_table->num_weak_refs--; } } // Do not set *referrer = NULL. objc_storeWeak() requires that the // value not change.}
// Remove old_referrer from list, if it's present.// Does not remove duplicates.// fixme this is slow if old_referrer is not present.static void remove_referrer_no_lock(weak_referrer_array_t *list, id *old_referrer){ size_t index = hash_pointer(old_referrer) % list->num_allocated; size_t start_index = index, hash_displacement = 0; while (list->refs[index].referrer != old_referrer) { index++; hash_displacement++; if (index == list->num_allocated) index = 0; if (index == start_index || hash_displacement > list->max_hash_displacement) { malloc_printf("attempted to remove unregistered weak referrer %p\n", old_referrer); return; } } list->refs[index].referrer = NULL; list->num_refs--;}
// Remove entry from the zone's table of weak references, and rehash// Does not update num_weak_refs.static void weak_entry_remove_no_lock(weak_table_t *weak_table, weak_entry_t *entry){ // remove entry entry->referent = NULL; if (entry->referrers.refs) _free_internal(entry->referrers.refs); entry->referrers.refs = NULL; entry->referrers.num_refs = 0; entry->referrers.num_allocated = 0; // rehash after entry weak_entry_t *weak_entries = weak_table->weak_entries; size_t table_size = weak_table->max_weak_refs; size_t hash_index = entry - weak_entries; size_t index = hash_index; if (!weak_entries) return; do { index++; if (index == table_size) index = 0; if (!weak_entries[index].referent) return; weak_entry_t entry = weak_entries[index]; weak_entries[index].referent = NULL; weak_entry_insert_no_lock(weak_table, &entry); } while (index != hash_index);}
我们可以看到:
首先,通过weak_entry_for_referent从weak_table中查询entry,如果存在,则从entry->referrers中移除此referrer。
然后,如果referrers表为空就调用weak_entry_remove_no_lock函数将此entry从weak_table中移除。在这里可以看到entry->referent = NULL,即把引用者置空。
3、对象释放
通过apple的runtime源码,不难发现NSObject执行dealloc时调用_objc_rootDealloc继而调用object_dispose随后调用objc_destructInstance方法
/************************************************************************ objc_destructInstance* Destroys an instance without freeing memory. * Calls C++ destructors.* Calls ARR ivar cleanup.* Removes associative references.* Returns `obj`. Does nothing if `obj` is nil.* Be warned that GC DOES NOT CALL THIS. If you edit this, also edit finalize.* CoreFoundation and other clients do call this under GC.**********************************************************************/void *objc_destructInstance(id obj) { if (obj) { Class isa_gen = _object_getClass(obj); class_t *isa = newcls(isa_gen); // Read all of the flags at once for performance. bool cxx = hasCxxStructors(isa); bool assoc = !UseGC && _class_instancesHaveAssociatedObjects(isa_gen); // This order is important. if (cxx) object_cxxDestruct(obj); if (assoc) _object_remove_assocations(obj); if (!UseGC) objc_clear_deallocating(obj); } return obj;}
这个函数主要干了后面的三件事:
1、object_cxxDestruct来执行对象以及父类的.cxx_destruct(它是clang在编译时候动态插入的)方法,来释放成员变量。
2、_object_remove_assocations释放关联的变量。
3、objc_clear_deallocating清除weak表等。
我们来看objc_clear_deallocating
void objc_clear_deallocating(id obj) { assert(obj); if (obj->isTaggedPointer()) return; obj->clearDeallocating();}
inline void objc_object::clearDeallocating(){ if (!isa.indexed) { // Slow path for raw pointer isa. sidetable_clearDeallocating(); } else if (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());}
分别看一下这两个clear
1)sidetable_clearDeallocating
void objc_object::sidetable_clearDeallocating(){ SideTable& table = SideTables()[this]; // clear any weak table items // clear extra retain count and deallocating bit // (fixme warn or abort if extra retain count == 0 ?) table.lock(); RefcountMap::iterator it = table.refcnts.find(this); if (it != table.refcnts.end()) { if (it->second & SIDE_TABLE_WEAKLY_REFERENCED) { weak_clear_no_lock(&table.weak_table, (id)this); } table.refcnts.erase(it); } table.unlock();}
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);}
可见它即是清除此对象关联的weak表,并将表中的引用者置空。
2)clearDeallocating_slow
// Slow path of clearDeallocating() // for objects with indexed 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.indexed && (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();
has_sidetable_rc是判断引用计数是否过大,clearDeallocating_slow最终也是调用weak_clear_no_lock
最后贴一张从网上找的图片来结束
- oc runtime之weak
- oc runtime之strong
- OC学习Runtime之方法
- OC学习Runtime之补充
- OC学习之Runtime之关联对象
- OC学习Runtime之Method swizzling
- OC学习Runtime之协议与分类
- OC运行时机制之Runtime
- iOS开发 -OC之 runtime机制
- OC - Runtime
- OC-Runtime
- oc: Runtime
- OC学习之Runtime之一类与对象
- oc strong weak retain assign
- OC property strong VS weak
- OC中weak/assign/strong
- OC学习之Runtime之一成员变量和属性
- OC学习Runtime之消息传递,消息转发机制
- 最优化问题综述
- 大型场景裁剪渲染
- main函数加载jetty,及java请求url
- JMS 使用
- Openresty最佳案例 | 第5篇:http和C_json模块
- oc runtime之weak
- reactjs router 4 (react-router-dom)使用嵌套路由遇到的问题
- gcc参数中的-I, -L和-l
- 图片文件操作工具类---ImageUtil
- Redis 在Centos7下配置开机自启动
- 关于正则匹配字符串之间的字符
- centos7下nginx无法访问
- EL表达式详解
- linux ftp配置