舉個(gè)例子:
常見的寫法:
@property(atomic,retain,readwrite) Dog *dog;
1偎箫、第一個(gè)位置的值:
atomic:線程保護(hù)的木柬,默認(rèn)
nonatomic:線程不保護(hù)的
2、第二個(gè)位置的值:
assign:直接賦值淹办,默認(rèn)
retain:保留對(duì)象,內(nèi)部會(huì)自動(dòng)調(diào)用retain方法眉枕,引用計(jì)數(shù)+1
copy:拷貝對(duì)象
3、第三個(gè)位置的值:
readwrite:生成get/set方法,默認(rèn)
readonly:只生成get方法
主要是第二位置的值不易區(qū)分速挑,接下來我們看看
1.關(guān)于assign谤牡、copy 、retain等關(guān)鍵字之間含義和區(qū)別姥宝?
assign: 簡(jiǎn)單賦值翅萤,不更改索引計(jì)數(shù)
copy: 建立一個(gè)索引計(jì)數(shù)為1的對(duì)象,然后釋放舊對(duì)象
retain:釋放舊的對(duì)象腊满,將舊對(duì)象的值賦予輸入對(duì)象套么,再提高輸入對(duì)象的索引計(jì)數(shù)為1
區(qū)別:
Copy其實(shí)是建立了一個(gè)相同的對(duì)象,而retain不是:
比如: 一個(gè)NSString對(duì)象碳蛋,地址為0×1111胚泌,內(nèi)容為@”STR”
Copy到另外一個(gè)NSString之 后,開辟一片新內(nèi)存空間來存儲(chǔ)肃弟,地址為0×2222玷室,內(nèi)容相同,新的對(duì)象retain為1笤受, 舊有對(duì)象沒有變化
retain到另外一個(gè)NSString之 后穷缤,地址相同(建立一個(gè)指針,指針拷貝)箩兽,內(nèi)容當(dāng)然相同津肛,這個(gè)對(duì)象的retain值+1
也就是說,retain是指針拷貝汗贫,copy是內(nèi)容拷貝快耿。在拷貝之前,都會(huì)釋放舊的對(duì)象芳绩。
- 使用assign: 對(duì)基礎(chǔ)數(shù)據(jù)類型 (NSInteger)和C數(shù)據(jù)類型(int, float, double, char,等)
- 使用copy: 對(duì)NSString
- 使用retain: 對(duì)其他NSObject和其子類
舉個(gè)例子:
NSString *houseOfMM = [[NSString alloc] initWithString:'北京別墅房'];
上面一段代碼會(huì)執(zhí)行以下兩個(gè)動(dòng)作:
1 在堆上分配一段內(nèi)存用來存儲(chǔ)@"北京別墅房 " ,比如:內(nèi)存地址為 0X1111 內(nèi)容為 '"北京別墅房" ,
2 在棧上分配一段內(nèi)存用來存儲(chǔ) houseOfMM ,比如:地址為 0XAAAA 內(nèi)容自然為 0X1111 下面分別看下(assign,retain,copy):
1.assign的情況:
NSString * myHouse = [ houseOfMM assign ];
此時(shí) myHouse 和 houseOfMM 完全相同,地址都是 0XAAAA ,
內(nèi)容為 0X1111 ,即 myHouse 只是 houseOfMM 的別名,對(duì)任何一個(gè)操作就等于對(duì)另一個(gè)操作撞反。
因此 retainCount 不需要增加.(同進(jìn)同出妥色,關(guān)系好,一把鑰匙遏片,給我拿著)
2.retain的情況:
NSString * myHouse = [ houseOfMM retain ];
此時(shí) myHouse 的地址不再為 0XAAAA ,可能為 0XAABB ,但是內(nèi)容依然為 0X1111 .
因此 myHouse 和 houseOfMM 都可以管理' 北京別墅房 '所在的內(nèi)存嘹害。
因此 retainCount 需要增加1.(有些獨(dú)立,各自進(jìn)出吮便,兩把鑰匙)
3.copy的情況(這樣說并不精準(zhǔn)笔呀,文中沒有過多對(duì)深淺copy詳述,這里補(bǔ)充下:
針對(duì)不可變對(duì)象調(diào)用copy返回該對(duì)象本身髓需,調(diào)用mutableCopy返回一個(gè)可變對(duì)象(新的)许师;
針對(duì)可變對(duì)象調(diào)用copy返回一個(gè)不可變對(duì)象(新的),調(diào)用mutableCopy返回另外一個(gè)可變對(duì)象(新的)。也就是說只有不可變對(duì)象copy時(shí)才會(huì)發(fā)生指針拷貝):
NSString * myHouse = [ houseOfMM mutableCopy];
此時(shí)會(huì)在堆上重新開辟一段內(nèi)存存放@'北京別墅房',比如0X1122,
內(nèi)容為@'北京別墅房',同時(shí)會(huì)在棧上為myHouse分配空間,比如地址:0XAACC,內(nèi)容為0X1122,
因此retainCount增加1供myHouse來管理0X1122這段內(nèi)存.(兩套@'北京別墅房')
注: ARC中的strong相當(dāng)于非ARC中的retain微渠,ARC來了以后多搞一把鑰匙就strong了啦搭幻。
看到這,或許有人會(huì)上文中對(duì)棧和堆的有些疑惑逞盆, 接下來是轉(zhuǎn)一位大神很經(jīng)典的談棧和堆檀蹋,來一起看看,更能理解上面的含義:
一云芦、預(yù)備知識(shí)—程序的內(nèi)存分配
一個(gè)由C/C++編譯的程序占用的內(nèi)存分為以下幾個(gè)部分
1俯逾、棧區(qū)(stack)— 由編譯器自動(dòng)分配釋放 ,存放函數(shù)的參數(shù)值舅逸,局部變量的值等桌肴。其 操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧。
2堡赔、堆區(qū)(heap) — 一般由程序員分配釋放识脆, 若程序員不釋放,程序結(jié)束時(shí)可能由OS回收 善已。注意它與數(shù)據(jù)結(jié)構(gòu)中的堆是兩回事灼捂,分配方式倒是類似于鏈表
3、全局區(qū)(靜態(tài)區(qū))(static)—换团,全局變量和靜態(tài)變量的存儲(chǔ)是放在一塊的悉稠,初始化的全局變量和靜態(tài)變量在一塊區(qū)域, 未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另 一塊區(qū)域艘包。 - 程結(jié)束后由系統(tǒng)釋放的猛。
4、**文字常量區(qū) ** —常量字符串就是放在這里的想虎。 程序結(jié)束后由系統(tǒng)釋放
5卦尊、程序代碼區(qū)—存放函數(shù)體的二進(jìn)制代碼。
二舌厨、例子程序
這是一個(gè)前輩寫的岂却,非常詳細(xì)
//main.cpp
int a = 0; 全局初始化區(qū)
char *p1; 全局未初始化區(qū)
main()
{
int b; 棧
char s[] = "abc"; 棧
char *p2; 棧
char *p3 = "123456"; 123456/0在常量區(qū),p3在棧上裙椭。
static int c =0躏哩; 全局(靜態(tài))初始化區(qū)
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);
分配得來得10和20字節(jié)的區(qū)域就在堆區(qū)。
strcpy(p1, "123456"); 123456/0放在常量區(qū)揉燃,編譯器可能會(huì)將它與p3所指向的"123456"
優(yōu)化成一個(gè)地方扫尺。
}
三、堆和棧的理論知識(shí)
2.1申請(qǐng)方式
stack:
由系統(tǒng)自動(dòng)分配炊汤。 例如正驻,聲明在函數(shù)中一個(gè)局部變量 int b; 系統(tǒng)自動(dòng)在棧中為b開辟空間
heap:
需要程序員自己申請(qǐng)弊攘,并指明大小,在c中malloc函數(shù)
如p1 = (char *)malloc(10);
在C++中用new運(yùn)算符
如p2 = new char[10];
但是注意p1拨拓、p2本身是在棧中的肴颊。
2.2 申請(qǐng)后系統(tǒng)的響應(yīng)
棧:只要棧的剩余空間大于所申請(qǐng)空間,系統(tǒng)將為程序提供內(nèi)存渣磷,否則將報(bào)異常提示棧溢 出婿着。
堆:首先應(yīng)該知道操作系統(tǒng)有一個(gè)記錄空閑內(nèi)存地址的鏈表,當(dāng)系統(tǒng)收到程序的申請(qǐng)時(shí)醋界, 會(huì)遍歷該鏈表竟宋,尋找第一個(gè)空間大于所申請(qǐng)空間的堆結(jié)點(diǎn),然后將該結(jié)點(diǎn)從空閑結(jié)點(diǎn)鏈表 中刪除形纺,并將該結(jié)點(diǎn)的空間分配給程序丘侠,另外,對(duì)于大多數(shù)系統(tǒng)逐样,會(huì)在這塊內(nèi)存空間中的 首地址處記錄本次分配的大小蜗字,這樣,代碼中的delete語句才能正確的釋放本內(nèi)存空間脂新。
另外挪捕,由于找到的堆結(jié)點(diǎn)的大小不一定正好等于申請(qǐng)的大小,系統(tǒng)會(huì)自動(dòng)的將多余的那部分重新放入空閑鏈表中争便。
2.3 申請(qǐng)大小的限制
棧:在Windows下,棧是向低地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu)级零,是一塊連續(xù)的內(nèi)存的區(qū)域。這句話的意思是棧頂?shù)牡刂泛蜅5淖畲笕萘渴窍到y(tǒng)預(yù)先規(guī)定好的滞乙,在WINDOWS下奏纪,棧的大小是2M(也有 的說是1M,總之是一個(gè)編譯時(shí)就確定的常數(shù))斩启,如果申請(qǐng)的空間超過棧的剩余空間時(shí)序调,將 提示overflow。因此兔簇,能從棧獲得的空間較小炕置。
堆:堆是向高地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu),是不連續(xù)的內(nèi)存區(qū)域男韧。這是由于系統(tǒng)是用鏈表來存儲(chǔ) 的空閑內(nèi)存地址的,自然是不連續(xù)的默垄,而鏈表的遍歷方向是由低地址向高地址此虑。堆的大小受限于計(jì)算機(jī)系統(tǒng)中有效的虛擬內(nèi)存。由此可見口锭,堆獲得的空間比較靈活朦前,也比較大介杆。
2.4 申請(qǐng)效率的比較:
棧由系統(tǒng)自動(dòng)分配,速度較快韭寸。但程序員是無法控制的春哨。
堆是由new分配的內(nèi)存,一般速度比較慢恩伺,而且容易產(chǎn)生內(nèi)存碎片,不過用起來最方便.
另外赴背,在WINDOWS下,最好的方式是用VirtualAlloc分配內(nèi)存晶渠,他不是在堆凰荚,也不是在棧是
直接在進(jìn)程的地址空間中保留一塊內(nèi)存,雖然用起來最不方便褒脯。但是速度快便瑟,也最靈活。
2.5 堆和棧中的存儲(chǔ)內(nèi)容
棧: 在函數(shù)調(diào)用時(shí)番川,第一個(gè)進(jìn)棧的是主函數(shù)中后的下一條指令(函數(shù)調(diào)用語句的下一條可
執(zhí)行語句)的地址到涂,然后是函數(shù)的各個(gè)參數(shù),在大多數(shù)的C編譯器中颁督,參數(shù)是由右往左入棧
的践啄,然后是函數(shù)中的局部變量。注意靜態(tài)變量是不入棧的适篙。
當(dāng)本次函數(shù)調(diào)用結(jié)束后往核,局部變量先出棧,然后是參數(shù)嚷节,最后棧頂指針指向最開始存的地
址聂儒,也就是主函數(shù)中的下一條指令,程序由該點(diǎn)繼續(xù)運(yùn)行硫痰。
堆:一般是在堆的頭部用一個(gè)字節(jié)存放堆的大小衩婚。堆中的具體內(nèi)容由程序員安排。
2.6存取效率的比較
char s1[] = "aaaaaaaaaaaaaaa";
char *s2 = "bbbbbbbbbbbbbbbbb";
aaaaaaaaaaa是在運(yùn)行時(shí)刻賦值的效斑;
而bbbbbbbbbbb是在編譯時(shí)就確定的非春;
但是,在以后的存取中缓屠,在棧上的數(shù)組比指針?biāo)赶虻淖址?例如堆)快奇昙。
比如:
#include
void main()
{
char a = 1;
char c[] = "1234567890";
char *p ="1234567890";
a = c[1];
a = p[1];
return;
}
對(duì)應(yīng)的匯編代碼
10: a = c[1];
00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]
0040106A 88 4D FC mov byte ptr [ebp-4],cl
11: a = p[1];
0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]
00401070 8A 42 01 mov al,byte ptr [edx+1]
00401073 88 45 FC mov byte ptr [ebp-4],al
第一種在讀取時(shí)直接就把字符串中的元素讀到寄存器cl中,而第二種則要先把指針值讀到
edx中敌完,再根據(jù)edx讀取字符储耐,顯然慢了。
2.7小結(jié):
堆和棧的區(qū)別可以用如下的比喻來看出:
使用棧就象我們?nèi)ワ堭^里吃飯滨溉,只管點(diǎn)菜(發(fā)出申請(qǐng))什湘、付錢长赞、和吃(使用),吃飽了就走闽撤,不必理會(huì)切菜得哆、洗菜等準(zhǔn)備工作和洗碗、刷鍋等掃尾工作哟旗,他的好處是快捷贩据,但是自由度小。
使用堆就象是自己動(dòng)手做喜歡吃的菜肴热幔,比較麻煩乐设,但是比較符合自己的口味,而且自由度大绎巨。
對(duì)了近尚,還有一個(gè)問題,凡事會(huì)問為什么? 為什么NSString 用copy, 數(shù)值用assion...等 ?
這個(gè)愛問為什么的小明同學(xué) 知乎上也有給你解答: https://www.zhihu.com/question/20102376 场勤、http://blog.csdn.net/itianyi/article/details/9018567
其他奧秘需要你自己動(dòng)手動(dòng)腦解決吧戈锻!``