??? 僅通過 __strong 是無法解決程序中的重大問題的。所說的重大問題就是引用計數(shù)式內(nèi)存管理中所必然發(fā)生的“循環(huán)引用”問題。
??? 例如:
{
??? id test0 = [[Test alloc] init];
??? id text1 = [[Test alloc] init];
??? [test0 setObject:test1];
??? [test1 setObject:test0];
}
??? 以下為持有對象狀態(tài):
{
? ? id test0 = [[Test alloc] init]; // 對象 A
// etst0 持有 Test 對象 A 的強引用
? ? id text1 = [[Test alloc] init]; // 對象 B
// test1 持有對象 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_。
// 發(fā)生內(nèi)存泄漏
??? 循環(huán)引用容易發(fā)生內(nèi)存泄漏。所謂的內(nèi)存泄漏就是應(yīng)當(dāng)廢棄的對象在其作用域之外繼續(xù)存在。
??? 此代碼的本意是賦予變量 test0 的對象 A 和賦予變量 test1 的對象 B 在超出其變量作用域時被釋放,即在對象不被任何變量持有的狀態(tài)下予以廢棄。但是,循環(huán)引用使得對象不能被再次廢棄。
??? 以下情況,雖然只有一個對象,也會出現(xiàn)循環(huán)引用:
id test = [[Test alloc] init];
[test setObject:test];
?? 使用 __weak 修飾符可以避免循環(huán)引用。
??? __weak 修飾符與 __strong 修飾符相反,提供弱引用,弱引用不能持有對象實例。
id __weak obj = [[NSObject alloc] init];
??? 變量 obj 上附加了 __waek 修飾符。實際上如果編譯以上代碼,編譯器會發(fā)出警告。
??? 此源代碼將自己生成并持有的對象賦值給附有 __weak 修飾符的變量 obj。即變量 obj 持有對持有對象的弱引用。因此,為了不以自己持有的狀態(tài)來保存自己生成并持有的對象,生成的對象會立即被釋放。編譯器對此會給出警告。如果像下面這樣,將對象賦值給附有 __strong 修飾符的變量之后再賦值給附有 __weak 修飾符的變量,就不會有警告了。
{
??? id __strong obj0 = [[NSObject alloc] init];
??? id __weak obj1 = obj0;
}
??? 下面為對象持有情況:
{
?// 自己生成并持有對象
??? id __strong obj0 = [[NSObject alloc] init];
// 因為 obj0 變量為強引用,所以自己持有對象
??? id __weak obj1 = obj0;
// obj1變量持有生成對象的弱引用
} /* 因為 obj0 變量超出其作用域,強引用失效,所以自動釋放自己持有的對象。因此對象的所有者不存在,所以廢棄該對象 */
??? 因為帶 __weak 修飾符的變量(即弱引用)不持有對象,所以在超出其變量作用域時,對象即被釋放。如果像以下內(nèi)容將可能發(fā)生循環(huán)引用的類成員變量改成附有 __weak 修飾符的成員變量的話,該現(xiàn)象是可以避免的。
@inerface Test : NSObject
{
??? id __weak obj_;
}
- (void)setObject:(id __strong)obj;
@end
??? _weak 修飾符還有另一優(yōu)點。在持有某對象的弱引用時,若該對象被廢棄,則此弱引用自動失效且處于 nil 被賦值的狀態(tài)。如下:
id __weak obj1 = nil;
{
??? id __strong obj0 = [[NSObject alloc] init];
??? obj1 = obj0;
??? NSLog(@"A: %@", obj1);
}
NSLog(@"B: %@", obj1);
??? 此源碼執(zhí)行效果如下:
A:<NSObject: 0x753e180>
B: (null)
??? 以下為對象持有情況
id __weak obj1 = nil;
{
// 自己生成并持有對象
??? id __strong obj0 = [[NSObject alloc] init];
// 因為 obj0 變量為強引用,所以自己持有對象
??? obj1 = obj0;
// obj1 變量持有對象的弱引用
??? NSLog(@"A: %@", obj1);
// 輸出 obj1 變量持有的弱引用對象
}
/* 因為 obj0 變量超出其作用域,強引用失效,所以自動釋放自己持有的對象。因為對象無持有者,所以廢棄該對象。廢棄對象的同時,持有該對象弱引用的 obj1變量的弱引用失效,nil 賦值飛 obj1 */
NSLog(@"B: %@", obj1);
// 輸出賦值給 obj1 變量中的 nil
??? 像這樣,使用 __weak 修飾符可避免虛幻引用,通過檢查附有 _weak 的變量是否為 nil,可以判斷被賦值的對象是否已廢棄。
// 結(jié)束,是另一種開始。
// 作者會將所讀所學(xué)摘錄及分享
// 本文參考《Objcetive-C高級編程iOS與OS X多線程和內(nèi)存管理》