__weak & autoreleasepool

__weak修飾符相對於__strong修飾,是爲了解決循環引用的。

@interface Test: NSObject
{
    id __strong obj_;
}
- (void)setObject:(id __strong)obj;
@end

@implement Test
- (id)init {
    self = [super init];
    return self;
}

- (void)setObject:(id __storng)obj {
    obj_ = obj;
}
@end

{
id test0 = [Test new]; /* 對象A */

/*
 * test0持有Test對象A的強引用
 * /

id test1 = [Test new]; /* 對象B */

/*
 * test1持有Test對象B的強引用
 * /

[test0 setObject:test1];

/*
 * Test對象A的obj_成員變量持有Test對象B的強引用
 * 此時,持有Test對象B的強引用的變量爲Test對象A的obj_和test1
 */

[test1 setObject:test0];

/*
 * Test對象B的obj_成員變量持有Test對象A的強引用
 * 此時,持有Test對象A的強引用的變量爲Test對象B的obj_和test0
 */
} /*
   * 因爲test0變量超出其作用域,強引用失效,自動釋放Test對象A
   * 因爲test1變量超出其作用域,強引用失效,自動釋放Test對象B
   * 持有Test對象A的強引用的變量爲Test對象B的obj_
   * 持有Test對象B的強引用的變量爲Test對象A的obj_
   * 發生內存泄漏
   */

上面的例子就是一個明顯的引用循環。可以用weak來解決

@interface Test: NSObject
{
    id __strong obj_;
} /*
   * 相互不擁有
   * /

weak還有以下2點特性

  • 1.若附有__weak修飾符的變量所引用的對象被廢棄,則將nil賦值給該變量
  • 2.使用__weak修飾符的變量,即是註冊到autoreleasepool中的對象

1

{
    id __weak obj1 = obj;
}

/*上述語句在編譯器中的模擬源碼*/

id obj1;
objc_initWeak(&obj1, obj)
objc_destoryWeak(&obj1)

objc_initWeak先初始化變量,然後再作用域結束時調用objc_destoryWeak釋放該變量。
而objc_initWeak(&objc1,obj)的源碼可以如下表示

obj1 = 0;
objc_storeWeak(&obj1,obj);

objc_storeWeak函數把第二參數的賦值對象的地址作爲鍵值,將第一參數的附有 __weak修飾符的變量的地址註冊到weak表中,如果第二參數爲0,則把變量的地址從weak表中刪除。刪除操作執行爲以下內容:

  1. 從weak表中獲取廢棄對象的地址爲鍵值的記錄
  2. 將包含在記錄中所有附有__weak修飾符的變量的地址,賦值爲nil
  3. 從weak表中刪除該紀錄
  4. 從引用計數表中刪除廢棄對象的地址爲鍵值的記錄

由此可知,當大量使用附有__weak修飾符的變量,則會消耗很多CPU資源,因爲會進行如上操作。所以比較好的策略是隻在避免循環引用時使用__weak修飾符。

2.

id __weak obj1 = obj;
NSLog(@"%@", obj1);

/*編譯器的模擬代碼*/
id obj1;
objc_initWeak(&obj1, obj);
id tmp = objc_loadWeakRetained(&obj1);
objc_autorelease(tmp);
NSLog(@"%@", tmp);
objc_destoryWeak(&obj1);

從上面的代碼可知,objc_loadWeakRetained函數取出__weak修飾符變量所引用的對象並retain,objc_autorelease函數將對象註冊到autoreleasepool中。
因此每次使用附有__weak修飾符的對象都會在autoreleasepool中創建對象,所以大量使用時,建議先暫時賦值給 __strong修飾符的變量後再使用。

id __weak o = obj;
NSLog(@"%@", o);
NSLog(@"%@", o);
NSLog(@"%@", o);
NSLog(@"%@", o);
NSLog(@"%@", o);

/*
 * obj對象會在autoreleasepool中註冊5次
 * /

id __weak o = obj;
id tmp = o;
NSLog(@"%@", tmp);
NSLog(@"%@", tmp);
NSLog(@"%@", tmp);
NSLog(@"%@", tmp);
NSLog(@"%@", tmp);

/*
 * obj對象在autoreleasepool中僅註冊1次
 * /
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章