溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Weak關鍵字介紹

發(fā)布時間:2020-05-29 17:57:51 來源:網(wǎng)絡 閱讀:551 作者:jjjyyy66 欄目:網(wǎng)絡安全

    

由淺入深

先來看看最簡單的一個例子:

#import "ViewController.h"@interface ViewController ()@property (nonatomic,strong)id strongPoint;@property (nonatomic,weak)id weakPoint;@end@implementation ViewController- (void)viewDidLoad {
    [super viewDidLoad];//    self.strongPoint = [NSDate date];
    self.strongPoint = [[UILabel alloc] init];    self.weakPoint = self.strongPoint;    self.strongPoint = nil;    NSLog(@"result is :%@", self.weakPoint);
}@end

我們可以看到此時輸出的結果為:

2017-02-07 13:20:41.119278 Test[7341:2187477] result is :(null)

如果我們使用的strong來修飾weakPoint,此時輸出的結果為:

2017-02-07 13:23:13.211164 Test[7344:2187993] result is :<UILabel: 0x100206070; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x17009a590>>

如果我們使用assign來修飾weakPoint,此時運行程序可能會崩潰(因為如果引用操作發(fā)生時內(nèi)存還沒有改變內(nèi)容,依舊可以輸出正確結果,如果引用的時候內(nèi)存內(nèi)容發(fā)生改變了,就會crash),因為當assign指針所指向的內(nèi)存被釋放之后,不會自動賦值為nil,這樣再次引用該指針的時候就會導致野指針操作。
對上述代碼運行結果進行分析:
當使用weak關鍵字的時候,不會增加對象的計數(shù),而且當所指對象置nil的時候,使用weak修飾的指針將被賦值為nil;
當使用strong關鍵字的時候,會增加對象的計數(shù),也就是說會保持對象值的存在,所以當使用strong的時候weakPoint還會有值。
因此,我們從這里可以得出一個結果:
strong是強引用,它會保持對象值的存在;
weak是弱引用,當weak指針指向的對象摧毀之后,屬性值也會清空(nil out)。
(注意:使用 _ _ weak修飾 和在@ property里面設置weak是一樣的)
但是當我們執(zhí)行如下代碼的時候:

__strong NSString *yourString = @"Your String"; 
__weak NSString *myString = yourString;
yourString = nil;
__unsafe_unretained NSString *theirString = myString;NSLog(@"%p %@", yourString, yourString);NSLog(@"%p %@", myString, myString);NSLog(@"%p %@", theirString, theirString);

你會發(fā)現(xiàn)只有yourString為空,其他兩個都不為空,這個是為什么呢?原因如下


這里是因為字符字面值永遠不會被釋放,所以你的weak指針還是指向它。
當你使用@""創(chuàng)建一個string對象的時候,它就是一個字面值,永遠不會被改變。如果你在程序中很多地方都用到了一樣的字符串,那么你可以測試一下,它們都是同一個對象(地址一樣),String字面值不會銷毀。使用[[NSString alloc] initWithString:@"literal string"]也是一樣的效果。因為它指向了一個字面值的string。


那么請問weak指針指向對象被回收的時候該指針是如何被自動置為nil的呢??


首先,大家可以看一下博客最后面的附錄,里面有兩個文檔,嚴格來說是Apple的opensouce。里面有一個objc-weak的類。這里是一個objc-weak.h類和一個objc-weak.mm類。

擴展常識
.m和.mm的區(qū)別
.m:源代碼文件,這個典型的源代碼文件擴展名,可以包含OC和C代碼。
.mm:源代碼文件,帶有這種擴展名的源代碼文件,除了可以包含OC和C代碼之外,還可以包含C++代碼。僅在你的OC代碼中確實需要使用C++類或者特性的時候才用這種擴展名。

從.h中可以看到以下幾個關鍵的兩個結構體:weak_entry_t和weak_table_t,以及一些方法。接下來簡單介紹一下weak如何自動置為nil。
weak的實現(xiàn)其實是一個weak表,該表是一個由自旋鎖管理的哈希表。
以下是從NSObject.mm里面摘出的一些方法:

idobjc_initWeak(id *location, id newObj)
{    if (!newObj) {
        *location = nil;        return nil;
    }    return storeWeak<false/*old*/, true/*new*/, true/*crash*/>
        (location, (objc_object*)newObj);
}

該function的作用是初始化一個新的weak指針指向對象的地址。其中的參數(shù)介紹如下:

  • location段:_ _ weak指針的地址

  • newObj:對象的指針地址

這里調用的storeWeak方法,storeWeak方法里面通過template模板的參數(shù)進行更新weak操作,看源碼可以知道里面會調用weak_register_no_lock/weak_unregister_no_lock等objc-weak.mm里面的方法進行相應的操作。objc-weak.h里面有句話:

For ARR, we also keep track of whether an arbitrary object is being 
deallocated by briefly placing it in the table just prior to invoking 
dealloc, and removing it via objc_clear_deallocating just prior to memory 
reclamation.

對象被廢棄時最后調用objc_clear_deallocating。該函數(shù)實現(xiàn)如下:

void objc_clear_deallocating(id obj) {    assert(obj);    assert(!UseGC);    if (obj->isTaggedPointer()) return;
    obj->clearDeallocating();
}

也就是調用了clearDeallocating,繼續(xù)追蹤可以發(fā)現(xiàn),它最終是使用了迭代器來取weak表的value,然后調用weak_clear_no_lock,然后查找對應的value,將該weak指針置空:

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

objc_clear_deallocating該函數(shù)的動作如下:

  1. 從weak表中獲取廢棄對象的地址為鍵值的記錄

  2. 將包含在記錄中的所有附有 _ _ weak修飾符變量的地址,賦值為nil

  3. 將weak表中刪除該記錄

  4. 從引用計數(shù)表中刪除廢棄對象的地址為鍵值的記錄
    看了objc-weak.mm的源碼大概了解了:其實Weak表示一個hash表,然后里面的key是指向對象的地址,Value是Weak指針的地址的數(shù)組


向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI