ios 經(jīng)典面試題

1、Runtime是什么?

Runtime是一套由C、C++和匯編實(shí)現(xiàn)的一套API,為OC語(yǔ)言加入了面向?qū)ο蠛瓦\(yùn)行時(shí)功能。
運(yùn)行時(shí) (Runtime) 是指將數(shù)據(jù)類型的確定由編譯時(shí)推遲到了運(yùn)行時(shí)。 (例如extension - category的區(qū)別)
平時(shí)寫的OC代碼,在運(yùn)行時(shí)會(huì)被轉(zhuǎn)換成RuntimeC語(yǔ)言代碼。RuntimeOC的幕后工作者。

2、方法的本質(zhì),SEL是什么?IMP是什么??jī)烧咧g的關(guān)系又是什么?

  • 方法的本質(zhì)是發(fā)送消息,發(fā)送消息又有以下幾個(gè)流程:

    • 快速查找 (objc_msgSend) ~ cache_t緩存消息
    • 慢速查找: 遞歸自己和父類 ~lookUpImpOrForward
    • 查找不到消息: 進(jìn)行動(dòng)態(tài)方法解析resolveInstanceMethod
    • 消息快速轉(zhuǎn)發(fā): forwardingTargetForSelector
    • 消息慢速轉(zhuǎn)發(fā) : methodSignatureForSelector & forwardInvocation
    • 未找到消息:程序crash,打印日志
  • SEL是方法編號(hào),在read_images期間就編譯進(jìn)入了內(nèi)存

  • IMP就是我們函數(shù)實(shí)現(xiàn)指針,找IMP就是找函數(shù)實(shí)現(xiàn)過(guò)程

  • SEL就相當(dāng)于書本的目錄,IMP就是書本的頁(yè)碼,查找具體的函數(shù)就是想看這本書里面具體篇章的內(nèi)容:

    • 我們首先知道想看什么,也就是title SEL
    • 根據(jù)目錄對(duì)應(yīng)的頁(yè)碼IMP
    • 翻到我們想看的具體內(nèi)容

3、能否向編譯后得到的類中增加實(shí)例變量?能否向運(yùn)行時(shí)創(chuàng)建的類中增加實(shí)例變量?

  • 不能向編譯后得到的類中增加實(shí)例變量,因?yàn)轭愒诰幾g時(shí)已經(jīng)將實(shí)例變量存儲(chǔ)到ro中了,編譯完成,內(nèi)存結(jié)構(gòu)確定,我們無(wú)法進(jìn)行修改。
  • 只要沒(méi)有注冊(cè)到內(nèi)存還是可以添加的
  • 在運(yùn)行時(shí)添加屬性和方法

4、isKindOfClassisMemberOfClass

  • isKindOfClass確定一個(gè)對(duì)象是否是一個(gè)類的成員,或者是繼承自該類的成員
  • isMemberOfClass只能確定一個(gè)對(duì)象是否是當(dāng)前類的成員,即isMemberOfClass不能檢測(cè)任何的類都是基于NSObject類這一事實(shí),而isKindOfClass可以
    我們可以進(jìn)行實(shí)例測(cè)驗(yàn):
    BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
    BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
    BOOL re3 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]];
    BOOL re4 = [(id)[LGPerson class] isMemberOfClass:[LGPerson class]];
    NSLog(@" re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n",re1,re2,re3,re4);

    BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];
    BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];
    BOOL re7 = [(id)[LGPerson alloc] isKindOfClass:[LGPerson class]];//1
    BOOL re8 = [(id)[LGPerson alloc] isMemberOfClass:[LGPerson class]];
    NSLog(@" re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);

我們看一下輸出結(jié)果:

2020-02-05 10:10:29.892802+0800 LGTest[3088:31663]  
 re1 :1
 re2 :0
 re3 :0
 re4 :0
2020-02-05 10:10:29.893424+0800 LGTest[3088:31663] 
 re5 :1
 re6 :1
 re7 :1
 re8 :1

我們看一下isKindOfClass的源碼:

+ (BOOL)isKindOfClass:(Class)cls {
// ? for循環(huán)不斷獲取self的isa指針以及父類的isa指針
    for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {
// ? 將循環(huán)獲取isa指針指向與cls作對(duì)比,如果是這個(gè)類的成員,或者是繼承自該類的成員,返回YES
        if (tcls == cls) return YES;
    }
    return NO;
}

我們?cè)诳匆幌?code>isMemberOfClass的源碼:

+ (BOOL)isMemberOfClass:(Class)cls {
// ? 只是拿到當(dāng)前self的isa指向與cls作對(duì)比,也就是只能確定這個(gè)對(duì)象是否是當(dāng)前類的成員
    return object_getClass((id)self) == cls;
}

5、[self class][super class]

首先我們創(chuàng)建兩個(gè)類,LGPerson繼承NSObject,LGStudent繼承LGPerson,在LGStudentinit方法中進(jìn)行打印一下代碼:

NSLog(@"%@",NSStringFromClass([self class]));
NSLog(@"%@",NSStringFromClass([super class]));

看結(jié)果:

2020-02-05 16:14:44.064160+0800 LGTest[23673:249566] LGStudent
2020-02-05 16:14:44.064508+0800 LGTest[23673:249566] LGStudent

到這里就有問(wèn)題了,為什么都是LGStudent?第二個(gè)不應(yīng)該是LGPerson嗎?
我們通過(guò)clang生成.cpp文件,拿到以下代碼:

// ? 原代碼很長(zhǎng),精簡(jiǎn)之后為以下內(nèi)容
NSStringFromClass(objc_msgSend((id)self, sel_registerName("class")));
NSStringFromClass(objc_msgSendSuper({(id)self, (id)class_getSuperclass(objc_getClass("LGPerson"))}, sel_registerName("class"))));

我們可以看到super關(guān)鍵字底層發(fā)送消息不是調(diào)用的objc_msgSend,而是objc_msgSendSuper,我們?cè)趨R編中找到這樣一段代碼:

* id objc_msgSendSuper(struct objc_super *super, SEL op, ...)
 *
 * struct objc_super {
 *     id receiver;
 *     Class cls;   // the class to search
 * }

根據(jù).cpp內(nèi)容可知receiverself(LGStudentsuper_classclass_getSuperclass(objc_getClass("LGPerson"))LGStudent的父類LGPerson
調(diào)用時(shí)先調(diào)用LGPersonclass方法,如果沒(méi)有,就沿繼承體系往上找直到NSObjectclass,最后內(nèi)部是使用objc_msgSend(objc_super->receiver, @selector("class"))去調(diào)用,所以輸出LGStudent

  • [self class]就是發(fā)送消息objc_msgSend,消息接受者self,方法編號(hào)class
  • [super class]本質(zhì)就是objc_msgSendSuper,消息的接收者還是self,方法編號(hào)class,只是objc_msgSendSuper會(huì)更快,直接跳過(guò)self的查找

6、weak實(shí)現(xiàn)原理

我們開發(fā)中經(jīng)常是使用weak關(guān)鍵字來(lái)解決循環(huán)引用的問(wèn)題,原因是被weak引用的對(duì)象它的引用計(jì)數(shù)不會(huì)增加,而且在這個(gè)對(duì)象被釋放的時(shí)候被weak修飾的變量會(huì)自動(dòng)置空,不會(huì)造成野指針的問(wèn)題,相對(duì)來(lái)說(shuō)比較安全。那么weak底層究竟是如何實(shí)現(xiàn)的呢?接下來(lái)我們一起來(lái)探究weak的實(shí)現(xiàn)原理。
我們首先定位weak的代碼:
我們?cè)?code>main函數(shù)中進(jìn)行斷點(diǎn):

WX20200206-155353@2x.png

然后通過(guò)匯編的方法找到weak的底層是objc_initWeak
WX20200206-155536@2x.png

我們下一個(gè)objc_initWeak的符號(hào)斷點(diǎn),可直接定位到源碼:

id
objc_initWeak(id *location, id newObj)
{
    if (!newObj) {
        *location = nil;
        return nil;
    }

    return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
        (location, (objc_object*)newObj);
}

通過(guò)上面的源碼我們可以看到是重點(diǎn)函數(shù)storeWeak:

storeWeak(id *location, objc_object *newObj)
{
    assert(haveOld  ||  haveNew);
    if (!haveNew) assert(newObj == nil);

    Class previouslyInitializedClass = nil;
    id oldObj;
    // ? 聲明新舊兩個(gè)SideTable
    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:
    // ? 獲取全局SideTables,并復(fù)制給oldTable和newTable
    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.
    // ? 防止弱引用機(jī)制和初始化出現(xiàn)死鎖,在弱引用之前確保對(duì)象已經(jīng)初始化成功
    if (haveNew  &&  newObj) {
        Class cls = newObj->getIsa();
        if (cls != previouslyInitializedClass  &&  
            !((objc_class *)cls)->isInitialized()) 
        {
            SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
            class_initialize(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.
    // ? 存儲(chǔ)新值,函數(shù)weak_register_no_lock
    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;
}

我們看源碼的前部分都是對(duì)SideTable聲明的表進(jìn)行判斷,然后我們可以了解到弱引用指針是存在SideTable表中

  • 如果weak指針需要弱引用新的對(duì)象newObj就執(zhí)行存儲(chǔ)新值weak_register_no_lock函數(shù)
  • 如果weak指針之前弱引用過(guò)別的對(duì)象oldObj,則調(diào)用weak_unregister_no_lock,在oldObjweak_entry_t中移除該weak指針地址
    我們?cè)诳匆幌?code>weak_register_no_lock函數(shù):
id 
weak_register_no_lock(weak_table_t *weak_table, id referent_id, 
                      id *referrer_id, bool crashIfDeallocating)
{
    // ? 獲取弱引用對(duì)象
    objc_object *referent = (objc_object *)referent_id;
    objc_object **referrer = (objc_object **)referrer_id;

    if (!referent  ||  referent->isTaggedPointer()) return referent_id;

    // ensure that the referenced object is viable
    // ? 確保被引用的對(duì)象沒(méi)有在被釋放
    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);
    }
    // ? 正在被釋放的對(duì)象不能被弱引用
    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_table 中找到被弱引用對(duì)象 referent 對(duì)應(yīng)的 weak_entry,并將 referrer 加入到 weak_entry 中
    weak_entry_t *entry;
    if ((entry = weak_entry_for_referent(weak_table, referent))) {
        append_referrer(entry, referrer);
    } 
    else {
        // 創(chuàng)建了這個(gè)數(shù)組 - 插入weak_table
        weak_entry_t new_entry(referent, referrer);
        // ? 進(jìn)行擴(kuò)容
        weak_grow_maybe(weak_table);
        // ? 插入weak_entry
        weak_entry_insert(weak_table, &new_entry);
    }

    // Do not set *referrer. objc_storeWeak() requires that the 
    // value not change.

    return referent_id;
}

如果對(duì)象可以被弱引用,則將引用弱對(duì)象所在的weak_table中的weak_entry_t取出,如果weak_entry_t不存在,則會(huì)新建一個(gè),然后將指向被弱引用對(duì)象地址的指針referrer通過(guò)函數(shù)append_referrer插入到對(duì)應(yīng)的weak_entry_t引用數(shù)組。至此就完成了弱引用。
如果weak指針在指向obj之前,已經(jīng)弱引用了其他的對(duì)象,則需要先將弱引用從指針對(duì)象其他的weak_entry_t的數(shù)組中移除。在調(diào)用weak_unregister_no_lock函數(shù)來(lái)做移除操作,我們看一下weak_unregister_no_lock函數(shù)源碼:

void
weak_unregister_no_lock(weak_table_t *weak_table, id referent_id, 
                        id *referrer_id)
{
    // ? 拿到以前弱引用的對(duì)象和對(duì)象的地址
    objc_object *referent = (objc_object *)referent_id;
    objc_object **referrer = (objc_object **)referrer_id;

    weak_entry_t *entry;

    if (!referent) return;
    // ? 查找到以前弱引用的對(duì)象 referent 所對(duì)應(yīng)的 weak_entry_t
    if ((entry = weak_entry_for_referent(weak_table, referent))) {
        // ? 在以前弱引用的對(duì)象 referent 所對(duì)應(yīng)的 weak_entry_t 的 hash 數(shù)組中,移除弱引用 referrer
        remove_referrer(entry, referrer);
        // ? 移除元素之后, 要檢查一下 weak_entry_t 的 hash 數(shù)組是否已經(jīng)空了
        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;
                }
            }
        }
        // ? 如果 weak_entry_t 的hash數(shù)組已經(jīng)空了,則需要將 weak_entry_t 從 weak_table 中移除
        if (empty) {
            weak_entry_remove(weak_table, entry);
        }
    }

    // Do not set *referrer = nil. objc_storeWeak() requires that the 
    // value not change.
}

到這里對(duì)象的弱引用過(guò)程已經(jīng)全部執(zhí)行完畢。
我們知道當(dāng)對(duì)象的引用計(jì)數(shù)為0時(shí),系統(tǒng)會(huì)調(diào)用對(duì)象的dealloc方法進(jìn)行釋放,我們看一下具體的實(shí)現(xiàn):

- (void)dealloc {
    _objc_rootDealloc(self);
}
***************?***************
void
_objc_rootDealloc(id obj)
{
    assert(obj);

    obj->rootDealloc();
}
***************?***************
inline void
objc_object::rootDealloc()
{
    // ? 判斷對(duì)象是否采用了Tagged Pointer技術(shù)
    if (isTaggedPointer()) return;  // fixme necessary?
    // ? 判斷是否能夠進(jìn)行快速釋放,判斷一下條件,滿足進(jìn)行 free()
    if (fastpath(isa.nonpointer  &&         // ? 對(duì)象是否采用了優(yōu)化的isa計(jì)數(shù)方式
                 !isa.weakly_referenced  && // ? 對(duì)象沒(méi)有被弱引用
                 !isa.has_assoc  &&         // ? 對(duì)象沒(méi)有關(guān)聯(lián)對(duì)象
                 !isa.has_cxx_dtor  &&      // ? 對(duì)象沒(méi)有自定義的C++析構(gòu)函數(shù)
                 !isa.has_sidetable_rc))    // ? 對(duì)象沒(méi)有用到sideTable來(lái)做引用計(jì)數(shù)
    {
        assert(!sidetable_present());
        free(this);
    }
    // ? 如果以上判斷沒(méi)有通過(guò),做下一步處理
    else {
        object_dispose((id)this);
    }
}

我們這里對(duì)象進(jìn)行過(guò)弱引用,所以上面一些列判斷不通過(guò),進(jìn)入到object_dispose函數(shù):

id 
object_dispose(id obj)
{
    if (!obj) return nil;

    objc_destructInstance(obj);    
    free(obj);

    return nil;
}
***************?***************
/***********************************************************************
* 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.
**********************************************************************/
/**
 銷毀實(shí)例而不會(huì)釋放內(nèi)存。
 調(diào)用C ++析構(gòu)函數(shù)。
 調(diào)用ARC ivar清理。
 刪除關(guān)聯(lián)引用。
 返回obj。 如果obj為零則不執(zhí)行任何操作。
 */
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.
        // ? 如果有C++析構(gòu)函數(shù),則從類中銷毀C++析構(gòu)函數(shù)
        if (cxx) object_cxxDestruct(obj);
        // ? 如果有關(guān)聯(lián)對(duì)象,則移除所有的關(guān)聯(lián)對(duì)象,并將其自身從Association Manager的map中移除
        if (assoc) _object_remove_assocations(obj);
        // ? 繼續(xù)清理其它相關(guān)的引用
        obj->clearDeallocating();
    }

    return obj;
}

從以上代碼我們可以看到最后會(huì)繼續(xù)清理其他相關(guān)引用,調(diào)用clearDeallocating函數(shù):

inline void 
objc_object::clearDeallocating()
{
    if (slowpath(!isa.nonpointer)) {
        // Slow path for raw pointer isa.
        // ? 如果要釋放的對(duì)象沒(méi)有采用了優(yōu)化過(guò)的isa引用計(jì)數(shù)
        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.
        // ? 如果要釋放的對(duì)象采用了優(yōu)化過(guò)的isa引用計(jì)數(shù),并且有弱引用或者使用了sideTable的輔助引用計(jì)數(shù)
        clearDeallocating_slow();
    }

    assert(!sidetable_present());
}

clearDeallocating函數(shù)內(nèi)部會(huì)根據(jù)要釋放的對(duì)象是否采用了優(yōu)化過(guò)的ISA做引用計(jì)數(shù)分成兩種情況:

1、如果要釋放的對(duì)象沒(méi)有采用了優(yōu)化過(guò)的isa引用計(jì)數(shù)
void 
objc_object::sidetable_clearDeallocating()
{
    // ? 在全局的SideTables中,以this指針(要釋放的對(duì)象)為key,找到對(duì)應(yīng)的SideTable
    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();
    // ? 在散列表SideTable中找到對(duì)應(yīng)的引用計(jì)數(shù)表RefcountMap,拿到要釋放的對(duì)象的引用計(jì)數(shù)
    RefcountMap::iterator it = table.refcnts.find(this);
    if (it != table.refcnts.end()) {
        // ? 如果要釋放的對(duì)象被弱引用了,通過(guò)weak_clear_no_lock函數(shù)將指向該對(duì)象的弱引用指針置為nil
        if (it->second & SIDE_TABLE_WEAKLY_REFERENCED) {
            weak_clear_no_lock(&table.weak_table, (id)this);
        }
        // ? 從引用計(jì)數(shù)表中擦除該對(duì)象的引用計(jì)數(shù)
        table.refcnts.erase(it);
    }
    table.unlock();
}
2、如果要釋放的對(duì)象采用了優(yōu)化過(guò)的isa引用計(jì)數(shù)
NEVER_INLINE void
objc_object::clearDeallocating_slow()
{
    assert(isa.nonpointer  &&  (isa.weakly_referenced || isa.has_sidetable_rc));
    // ? 在全局的SideTables中,以this指針(要釋放的對(duì)象)為key,找到對(duì)應(yīng)的SideTable
    SideTable& table = SideTables()[this];
    table.lock();
    
    if (isa.weakly_referenced) {
        // ? 要釋放的對(duì)象被弱引用了,通過(guò)weak_clear_no_lock函數(shù)將指向該對(duì)象的弱引用指針置為nil
        weak_clear_no_lock(&table.weak_table, (id)this);
    }
    // ? 使用了sideTable的輔助引用計(jì)數(shù),直接在SideTable中擦除該對(duì)象的引用計(jì)數(shù)
    if (isa.has_sidetable_rc) {
        table.refcnts.erase(this);
    }
    table.unlock();
}

上面兩種方法中都用到了weak_clear_no_lock函數(shù),將指向該對(duì)象的弱引用指針置為nil,我們?cè)诳匆幌?code>weak_clear_no_lock函數(shù):

void 
weak_clear_no_lock(weak_table_t *weak_table, id referent_id) 
{
    // ? 獲取被弱引用對(duì)象的地址
    objc_object *referent = (objc_object *)referent_id;
    // ? 根據(jù)對(duì)象地址找到被弱引用對(duì)象referent在weak_table中對(duì)應(yīng)的weak_entry_t
    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;
    // ? 找出弱引用該對(duì)象的所有weak指針地址數(shù)組
    if (entry->out_of_line()) {
        referrers = entry->referrers;
        count = TABLE_SIZE(entry);
    } 
    else {
        referrers = entry->inline_referrers;
        count = WEAK_INLINE_COUNT;
    }
    // ? 遍歷取出每個(gè)weak指針的地址
    for (size_t i = 0; i < count; ++i) {
        objc_object **referrer = referrers[i];
        if (referrer) {
            // ? 如果weak指針確實(shí)弱引用了對(duì)象 referent,則將weak指針設(shè)置為nil
            if (*referrer == referent) {
                *referrer = nil;
            }
             // ? 如果所存儲(chǔ)的weak指針沒(méi)有弱引用對(duì)象 referent,這可能是由于runtime代碼的邏輯錯(cuò)誤引起的,報(bào)錯(cuò)
            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);
}
  • 當(dāng)一個(gè)對(duì)象objc被弱指針指向時(shí),這個(gè)弱指針會(huì)以objc作為鍵,被存儲(chǔ)到sideTable類的weak_table這個(gè)散列表上對(duì)應(yīng)的一個(gè)weak指針數(shù)組里面。
  • 當(dāng)一個(gè)對(duì)象objcdealloc方法被調(diào)用時(shí),運(yùn)行時(shí)會(huì)以objc為鍵,從sideTableweak_table散列表中,發(fā)現(xiàn)對(duì)應(yīng)的weak指針列表,然后將里面的弱指針逐個(gè)放置為nil
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 上篇文章:Runtime在工作中的運(yùn)用 1.objc在向一個(gè)對(duì)象發(fā)送消息時(shí),發(fā)生了什么? objc在向一個(gè)對(duì)象發(fā)送...
    minjing_lin閱讀 1,176評(píng)論 4 7
  • 1.weak和assign區(qū)別 修飾變量類型的區(qū)別: weak 只可以修飾對(duì)象。如果修飾基本數(shù)據(jù)類型,編譯器會(huì)報(bào)錯(cuò)...
    coderjon閱讀 1,128評(píng)論 0 1
  • 相關(guān)文章: 來(lái)自網(wǎng)絡(luò),原文鏈接 http://www.cocoachina.com/ios/20150825/13...
    小楓123閱讀 853評(píng)論 0 2
  • 熊奇,90年的青年,我認(rèn)識(shí)他是在大一的時(shí)候,2010年上半年。 那時(shí)創(chuàng)業(yè)協(xié)會(huì)的朋友歐陽(yáng)把我介紹給熊奇,那時(shí)候他跑到...
    大荷08閱讀 344評(píng)論 0 0
  • 敬愛(ài)的老師,智慧的班主任,親愛(ài)的學(xué)兄們:大家好!我是來(lái)自廣饒亨通農(nóng)機(jī)馬云芹,今天是我的日精進(jìn)行動(dòng)第203天,給大家...
    云_300a閱讀 94評(píng)論 0 0

友情鏈接更多精彩內(nèi)容