天天看點

__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中注冊次
 * /

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

/*
 * obj對象在autoreleasepool中僅注冊次
 * /