前言:
在iOS 開發(fā)中 我們經(jīng)常用到 weak strong retain assign copy 屬性的修飾,他們的區(qū)別本質(zhì)上是?1.是否開辟新的內(nèi)存
2.是否對地址C有引用計數(shù)增加 的區(qū)別。
參考簡書:?http://www.reibang.com/p/a29a0bdd5da8??启绰。 筆者主要分三個 部分來介紹區(qū)別俯艰。
第一 是? 非容器 可變變量 的修飾 如NSMutableString? ?第二是? 容器對象 也就是NSArray等 類型的? ?第三 是非容器不可變變量 的修飾 。 第四 是 Block 類型的修飾 來區(qū)分每個地方的區(qū)別骡澈。DEMO下載地址珍策。 建議下載demo 試試。?
代碼分析:
第一 是非容器 可變變量 的修飾 如NSMutableString
首先
- (void)memoryTest
{
NSMutableString*tempMStr = [[NSMutableStringalloc]initWithString:@"strValue"];
NSLog(@"tempMStr值地址:%p吭狡,tempMStr值%@,tempMStr值引用計數(shù)%@\\n", tempMStr,tempMStr,[tempMStr valueForKey:retainCountKey]);
//輸出tempMStr值地址:0x7a05f650尖殃,tempMStr值strValue,tempMStr值引用計數(shù)1
}
這個方法 提供一中 獲取 內(nèi)存地址 和 引用計數(shù)的方式 在 ARC 的情況下。 下面的每個方法都是 基于上述方法划煮。
這是一個 閃退的方法assign
可以發(fā)現(xiàn)在輸出assignMStr時會出現(xiàn)奔潰的情況送丰。原因是發(fā)送了野指針的情況。assign同weak般此,指向C并且計數(shù)不+1蚪战,但當(dāng)C地址引用計數(shù)為0時,assign不會對C地址進行B數(shù)據(jù)的抹除操作 也就是設(shè)置為 nil铐懊,只是進行值釋放邀桑。這就導(dǎo)致野指針存在,即當(dāng)這塊地址還沒寫上其他值前科乎,能輸出正常值壁畸,但一旦重新寫上數(shù)據(jù),該指針隨時可能沒有值,造成奔潰捏萍。
- (void)compareAssignAndWeak
{
NSMutableString*mstrOrigin = [[NSMutableStringalloc]initWithString:@"mstrOriginValue"];
self.assignMStr= mstrOrigin;
self.weakMStr= mstrOrigin;
mstrOrigin = [[NSMutableStringalloc]initWithString:@"mstrOriginChange3"];
NSLog(@"weakMStr輸出:%p,%@\\n",_weakMStr,_weakMStr);
NSLog(@"assignMStr輸出:%p,%@\\n",self.assignMStr,self.assignMStr);
}
/**
注意 引用計數(shù)的 和 修改不同 的值的情況
*/
- (void)testOne
{
//
NSMutableString*mstrOrigin = [[NSMutableStringalloc]initWithString:@"mstrOriginValue"];
// 有意思的 是retainCount是一個對象類型
NSLog(@"mstrOrigin 開始的引用計數(shù)%@",[mstrOrigin valueForKey:@"retainCount"]);
self.aCopyMStr= mstrOrigin;
self.strongMStr= mstrOrigin;
self.retainMStr= mstrOrigin;
self.weakMStr= mstrOrigin;
self.assignMStr= mstrOrigin;
//
NSLog(@"mstrOrigin輸出:%p,%@\\n", mstrOrigin,mstrOrigin);
NSLog(@"aCopyMStr輸出:%p,%@\\n",_aCopyMStr,_aCopyMStr);
NSLog(@"strongMStr輸出:%p,%@\\n",_strongMStr,_strongMStr);
NSLog(@"retainMStr輸出:%p,%@\\n",_retainMStr,_retainMStr);
NSLog(@"weakMStr輸出:%p,%@\\n",_weakMStr,_weakMStr);
NSLog(@"assignMStr輸出:%p,%@\\n",_assignMStr,_assignMStr);
NSLog(@"mstrOrigin 新的引用計數(shù)%@",[mstrOrigin valueForKey:@"retainCount"]);
NSLog(@"aCopyMStr 的引用計數(shù)是:%@",[_aCopyMStrvalueForKey:@"retainCount"]);
/*
mstrOrigin 開始的引用計數(shù)1
mstrOrigin輸出:0x60400004cab0,mstrOriginValue\n
aCopyMStr輸出:0x600000447c20,mstrOriginValue\n
strongMStr輸出:0x60400004cab0,mstrOriginValue\n
retainMStr輸出:0x60400004cab0,mstrOriginValue\n
weakMStr輸出:0x60400004cab0,mstrOriginValue\n
assignMStr輸出:0x60400004cab0,mstrOriginValue\n
mstrOrigin 新的引用計數(shù)3
aCopyMStr 的引用計數(shù)是:1
*/
/*
從上面可以看出copy 并沒有改變引用計數(shù)太抓,ratain ,Strong 使得引用計數(shù)分別 +1
*/
NSLog(@"-------------修改原值后 ----------------");
[mstrOriginappendString:@"+appendValue"];
NSLog(@"mstrOrigin輸出:%p,%@\\n", mstrOrigin,mstrOrigin);
NSLog(@"aCopyMStr輸出:%p,%@\\n",_aCopyMStr,_aCopyMStr);// 沒有改變
NSLog(@"strongMStr輸出:%p,%@\\n",_strongMStr,_strongMStr);
NSLog(@"retainMStr輸出:%p,%@\\n",_retainMStr,_retainMStr);
NSLog(@"weakMStr輸出:%p,%@\\n",_weakMStr,_weakMStr);
NSLog(@"assignMStr輸出:%p,%@\\n",_assignMStr,_assignMStr);
NSLog(@"----------------- 設(shè)置成nil--------------------");
mstrOrigin =nil;
NSLog(@"mstrOrigin輸出:%p,%@\\n", mstrOrigin,mstrOrigin);
NSLog(@"aCopyMStr輸出:%p,%@\\n",_aCopyMStr,_aCopyMStr);
NSLog(@"strongMStr輸出:%p,%@\\n",_strongMStr,_strongMStr);
NSLog(@"retainMStr輸出:%p,%@\\n",_retainMStr,_retainMStr);
NSLog(@"weakMStr輸出:%p,%@\\n",_weakMStr,_weakMStr);
NSLog(@"assignMStr輸出:%p,%@\\n",_assignMStr,_assignMStr);
NSLog(@"mstrOrigin 新的引用計數(shù)%@",[mstrOrigin valueForKey:@"retainCount"]);
NSLog(@"strongMStr 新的引用計數(shù)%@",[_strongMStrvalueForKey:@"retainCount"]);
NSLog(@"aCopyMStr 的引用計數(shù)是:%@",[_aCopyMStrvalueForKey:@"retainCount"]);
NSLog(@"assignMStr 的引用計數(shù)是:%@",[_assignMStrvalueForKey:@"retainCount"]);
// strong retain 等影響了weak assign 的對比
}
讀者? 注意一點 在這個?testOne 中打印信息?_assignMStr 這個值沒有奔潰 !A铊尽走敌! 為什么? 因為 strong 屬性 對mstrOrigin 的修飾 影響了 _assginMStr 造成 并沒有被釋放 逗噩。 也就不會有野指針的問題掉丽!? 這是筆者要說明的。和參考的簡書作者沒有 注意到的异雁。?
- (void)arrTest
{
NSMutableArray*mArrOrigin = [[NSMutableArrayalloc]init];
NSMutableString*mstr1 = [[NSMutableStringalloc]initWithString:@"value1"];
NSMutableString*mstr2 = [[NSMutableStringalloc]initWithString:@"value2"];
NSMutableString*mstr3 = [[NSMutableStringalloc]initWithString:@"value3"];
[mArrOriginaddObject:mstr1];
[mArrOriginaddObject:mstr2];
//將mArrOrigin拷貝給aCopyMArr捶障,strongMArr,weakMArr
self.aCopyMArr= mArrOrigin;
self.strongMArr= mArrOrigin;
self.weakMArr= mArrOrigin;
NSLog(@"mArrOrigin輸出:%p,%@\\n", mArrOrigin,mArrOrigin);
NSLog(@"aCopyMArr輸出:%p,%@\\n",_aCopyMArr,_aCopyMArr);
NSLog(@"strongMArr輸出:%p,%@\\n",_strongMArr,_strongMArr);
NSLog(@"weakMArr輸出:%p,%@\\n",_weakMArr,_weakMArr);
NSLog(@"weakMArr輸出:%p,%@\\n",_weakMArr[0],_weakMArr[0]);
NSLog(@"mArrOrigin中的數(shù)據(jù)引用計數(shù)%@", [mArrOrigin valueForKey:@"retainCount"]);
NSLog(@"%p %p %p %p",&mArrOrigin,mArrOrigin,mArrOrigin[0],mArrOrigin[1]);
// 說明 weak 并沒有增加引用計數(shù)
//給原數(shù)組添加一個元素
[mArrOriginaddObject:mstr3];
NSLog(@"mArrOrigin輸出:%p,%@\\n", mArrOrigin,mArrOrigin);
NSLog(@"aCopyMArr輸出:%p,%@\\n",_aCopyMArr,_aCopyMArr);
NSLog(@"strongMArr輸出:%p,%@\\n",_strongMArr,_strongMArr);
NSLog(@"weakMArr輸出:%p,%@\\n",_weakMArr,_weakMArr);
NSLog(@"mArrOrigin中的數(shù)據(jù)引用計數(shù)%@", [mArrOrigin valueForKey:@"retainCount"]);
//修改原數(shù)組中的元素纲刀,看是否有隨之變化 copy也發(fā)生了改變
[mstr1appendFormat:@"aaa"];
NSLog(@"mArrOrigin輸出:%p,%@\\n", mArrOrigin,mArrOrigin);
NSLog(@"aCopyMArr輸出:%p,%@\\n",_aCopyMArr,_aCopyMArr);
NSLog(@"strongMArr輸出:%p,%@\\n",_strongMArr,_strongMArr);
NSLog(@"weakMArr輸出:%p,%@\\n",_weakMArr,_weakMArr);
#pragma mark ---- !!! 注意發(fā)現(xiàn)改變 mstr1
// _aCopyMArr 的第一個元素也發(fā)生了 也就是 copy 了array 但是沒有對每個元素
// 每個元素copy 一遍放新的數(shù)組里面去 注意是并沒有 這么做项炼。
}
如代碼中注意所說。
上面代碼有點多示绊,所做的操作是mArrOrigin(value1,value2)賦值給copy,strong,weak修飾的aCopyMArr,strongMArr,weakMArr锭部。通過給原數(shù)組增加元素,修改原數(shù)組元素值耻台,然后輸出mArrOrigin的引用計數(shù)空免,和數(shù)組地址,查看變化盆耽。
發(fā)現(xiàn)其中數(shù)組本身指向的內(nèi)存地址除了aCopyMArr重新開辟了一塊地址,strongMArr,weakMArr和mArrOrigin指針指向的地址是一樣的扼菠。也就是說
容器可變變量中容器本身和非容器可變變量是一樣的摄杂,copy深拷貝,strongMArr,weakMArr和assign都是淺拷貝
另外我們發(fā)現(xiàn)被拷貝對象mArrOrigin中的數(shù)據(jù)引用計數(shù)居然不是1而是3循榆。也就是說容器內(nèi)的數(shù)據(jù)拷貝都是進行了淺拷貝析恢。同時當(dāng)我們修改數(shù)組中的一個數(shù)據(jù)時strongMArr,weakMArr,aCopyMArr中的數(shù)據(jù)都改變了秧饮,說明
容器可變變量中的數(shù)據(jù)在拷貝的時候都是淺拷貝
- (void)nsstringTest
{
NSLog(@"\\n\\n\\n\\n------------------不可變量實驗------------------------");
NSString*strOrigin = [[NSStringalloc]initWithUTF8String:"strOrigin0123456"];
self.aCopyStr= strOrigin;
self.strongStr= strOrigin;
self.weakStr= strOrigin;
NSLog(@"strOrigin輸出:%p,%@\\n", strOrigin,strOrigin);
NSLog(@"aCopyStr輸出:%p,%@\\n",_aCopyStr,_aCopyStr);
NSLog(@"strongStr輸出:%p,%@\\n",_strongStr,_strongStr);
NSLog(@"weakStr輸出:%p,%@\\n",_weakStr,_weakStr);
NSLog(@"------------------修改原值后------------------------");
strOrigin =@"aaa";
NSLog(@"strOrigin輸出:%p,%@\\n", strOrigin,strOrigin);
NSLog(@"aCopyStr輸出:%p,%@\\n",_aCopyStr,_aCopyStr);
NSLog(@"strongStr輸出:%p,%@\\n",_strongStr,_strongStr);
NSLog(@"weakStr輸出:%p,%@\\n",_weakStr,_weakStr);
NSLog(@"------------------結(jié)論------------------------");
NSLog(@"strOrigin值值為改變映挂,但strOrigin和aCopyStr指針地址和指向都已經(jīng)改變,說明不可變類型值不可被修改盗尸,重新初始化");
self.aCopyStr=nil;
self.strongStr=nil;
NSLog(@"strOrigin輸出:%p,%@\\n", strOrigin,strOrigin);
NSLog(@"aCopyStr輸出:%p,%@\\n",_aCopyStr,_aCopyStr);
NSLog(@"strongStr輸出:%p,%@\\n",_strongStr,_strongStr);
NSLog(@"weakStr輸出:%p,%@\\n",_weakStr,_weakStr);
NSLog(@"------------------結(jié)論------------------------");
NSLog(@"當(dāng)只有weakStr擁有C時柑船,值依舊會被釋放,同非容器可變變量");
}
這個 weak 測試告訴我們 上面方法中 strong 和 copy strOrigin 影響了weak 這個屬性的值
對比 和 上面那個方法的測試 weakStr 的輸出泼各。
還有一個 strOrigin 被賦予新的值后前面一塊區(qū)域?qū)嶋H是被系統(tǒng)回收了 weak 的對象設(shè)置成了 null 值
這樣 沒有野指針鞍时。
- (void)weakStringTestTwo
{
NSString*strOrigin = [[NSStringalloc]initWithUTF8String:"HelloDeLong"];
self.weakStr= strOrigin;
NSLog(@"weakStr輸出:%p,%@\\n",_weakStr,_weakStr);
strOrigin =@"aaa";
NSLog(@"weakStr輸出:%p,%@\\n",_weakStr,_weakStr);
/*
weakStr輸出:0x6000002252a0,HelloDeLong\n
weakStr輸出:0x0,(null)\n
*/
}
- (void)retainBlockAlone
{
// 前面已經(jīng)比較過strong 和 assign 的區(qū)別
TestBlockmBlock = ^(inta){
returna+3;
};
self.retainTestBlock= mBlock;
inta=self.retainTestBlock(4);
mBlock = ^(inta){
returna+5;
};
//self.retainTestBlock = mBlock;
intb=self.retainTestBlock(4);
mBlock =nil;
intc=self.retainTestBlock(4);// 無論 mBlock 如何改變都沒有改變 retainBlock
}
在處理用strong聲明的Block屬性引發(fā)的問題時偶然發(fā)現(xiàn)的。在諸多教程中都會講到:聲明屬性時用strong或者retain效果是一樣的(貌似更多開發(fā)者更傾向于用strong)。不過在聲明Block時逆巍,使用strong和retain會有截然不同的效果及塘。strong會等于copy,而retain竟然等于assign锐极!
當(dāng)然定義Block還是應(yīng)該用copy(還有其他需要注意的地方笙僚,可以參考這篇文章:iOS: ARC和非ARC下使用Block屬性的問題),因為非ARC下不copy的Block會在棧中灵再,ARC中的Block都會在堆上的肋层。?
代碼這東西 還是得敲 邊敲就會有新的靈感。