技 術(shù) 文 章 / 超 人
2019-03-20 補(bǔ)充@ dynamic與@ synthesize內(nèi)容
個(gè)人覺(jué)得要更加深入直觀了解MRC與ARC的區(qū)別建議先從內(nèi)存分析開(kāi)始所以文章開(kāi)始會(huì)從內(nèi)存說(shuō)起
文章目錄
- 1.五大內(nèi)存區(qū)域
- 1.1 棧區(qū)
- 1.2 堆區(qū)
- 1.3 全局區(qū)
- 1.4 常量區(qū)
- 1.5 代碼區(qū)
- 1.6 static靜態(tài)變量
- 1.7 extern全局變量
- 1.8 const常量
- 2.屬性標(biāo)識(shí)符
- 2.1 @property接箫、@synthesize辅柴、@dynamic
- 2.2 nonatomic與atomic
- 2.3 strong哟玷、weak、retain叁丧、assgin、copy豪嚎、unsafe_unretained
- 2.4 readOnly锯茄、readWrite厢塘、getter=茶没、setter=
- 2.5 __unsafe_unretained、__weak晚碾、__strong
- 3.MRC與ARC區(qū)別
- 3.1 MRC手動(dòng)內(nèi)存管理
- 3.2 ARC自動(dòng)內(nèi)存管理
- 3.3 autoreleasepool自動(dòng)釋放池
- 4.NSString單獨(dú)說(shuō)
1.五大內(nèi)存區(qū)域
棧區(qū)抓半,堆區(qū),全局區(qū)格嘁,常量區(qū)笛求,代碼區(qū)
-
1.1棧區(qū):
創(chuàng)建臨時(shí)變量時(shí)由編譯器自動(dòng)分配,在不需要的時(shí)候自動(dòng)清除的變量的存儲(chǔ)區(qū)
糕簿。
里面的變量通常是局部變量
探入、函數(shù)參數(shù)
等。在一個(gè)進(jìn)程中冶伞,位于用戶虛擬地址空間頂部的是用戶棧新症,編譯器用它來(lái)實(shí)現(xiàn)函數(shù)的調(diào)用步氏。和堆一樣响禽,用戶棧在程序執(zhí)行期間可以動(dòng)態(tài)地?cái)U(kuò)展和收縮。
@interface TestObject()
@end
@implementation TestObject
- (void)testMethodWithName:(NSString *)name
{
//方法參數(shù)name是一個(gè)指針荚醒,指向傳入的參數(shù)指針?biāo)赶虻膶?duì)象內(nèi)存地址芋类。name是在棧中
//通過(guò)打印地址可以看出來(lái),傳入?yún)?shù)的對(duì)象內(nèi)存地址與方法參數(shù)的對(duì)象內(nèi)存地址是一樣的界阁。但是指針地址不一樣侯繁。
NSLog(@"name指針地址:%p,name指針指向的對(duì)象內(nèi)存地址:%p",&name,name);
//*person 是指針變量,在棧中, [Person new]是創(chuàng)建的對(duì)象,放在堆中。
//person指針指向了[Person new]所創(chuàng)建的對(duì)象泡躯。
//那么[Person new]所創(chuàng)建的對(duì)象的引用計(jì)數(shù)器就被+1了,此時(shí)[Person new]對(duì)象的retainCount為1
Person *person = [Person new];
}
-
1.2堆區(qū):
就是那些由 new alloc 創(chuàng)建的對(duì)象所分配的內(nèi)存塊贮竟,它們的釋放系統(tǒng)不會(huì)主動(dòng)去管,由我們的開(kāi)發(fā)者去告訴系統(tǒng)什么時(shí)候釋放這塊內(nèi)存(一個(gè)對(duì)象引用計(jì)數(shù)為0時(shí)系統(tǒng)就會(huì)回銷(xiāo)毀該內(nèi)存區(qū)域?qū)ο?较剃。一般一個(gè) new 就要對(duì)應(yīng)一個(gè) release咕别。在ARC下編譯器會(huì)自動(dòng)在合適位置為OC對(duì)象添加release操作。會(huì)在當(dāng)前線程Runloop退出或休眠時(shí)銷(xiāo)毀這些對(duì)象写穴,MRC則需程序員手動(dòng)釋放惰拱。
堆可以動(dòng)態(tài)地?cái)U(kuò)展和收縮。
//alloc是為Person對(duì)象分配內(nèi)存,init是初始化Person對(duì)象啊送。本質(zhì)上跟[Person new]一樣偿短。
Person *person = [[Person alloc] init];
1.3全局/靜態(tài)存儲(chǔ)區(qū),
全局變量
和靜態(tài)變量
被分配到同一塊內(nèi)存中馋没,在以前的 C 語(yǔ)言中昔逗,全局變量又分為初始化
的和未初始化
的(初始化的全局變量和靜態(tài)變量在一塊區(qū)域,
未初始化的全局變量與靜態(tài)變量在相鄰的另一塊區(qū)域篷朵,
同時(shí)未被初始化的對(duì)象存儲(chǔ)區(qū)可以通過(guò) void* 來(lái)訪問(wèn)和操縱勾怒,
程序結(jié)束后由系統(tǒng)自行釋放),在 C++ 里面沒(méi)有這個(gè)區(qū)分了,
他們共同占用同一塊內(nèi)存區(qū)控硼。1.4常量存儲(chǔ)區(qū)
這是一塊比較特殊的存儲(chǔ)區(qū)泽论,他們里面存放的是常量,不允許修改卡乾。一般值都是放在這個(gè)地方的1.5代碼區(qū)
存放函數(shù)的二進(jìn)制代碼
NSString *string1;//string1 這個(gè)NSString 類(lèi)型的指針,未初始化存在于<全局區(qū)>的<BBS區(qū)>
NSString *string2 = @"1234";//string2 這個(gè)NSString類(lèi)型的指針翼悴,已初始化存在與<全局區(qū)>的<data數(shù)據(jù)區(qū)>,@“1234”存在與堆區(qū)幔妨,因?yàn)锧代表了對(duì)象鹦赎。
static NSString *string3;//string3 這個(gè)NSString 類(lèi)型的指針存在于<全局區(qū)>的<BBS區(qū)>
static NSString *string4 = @"1234";//string4 這個(gè)NSString類(lèi)型的指針存在與<全局區(qū)>的<data數(shù)據(jù)區(qū)>,@“1234”存在與堆區(qū)误堡,因?yàn)锧代表了對(duì)象古话。stiring2和string4的值地址是一樣的
static const NSString *string5 = @"654321";//const 修飾后 string5不能修改值。 其他的與string4一樣
- (void)test
{
int a;//a這個(gè)int類(lèi)型的變量 是存在與<棧區(qū)>的
a = 10;//10這個(gè)值是存在與 <常量區(qū)>的
NSStirng *str锁施;//str這個(gè)NSString類(lèi)型的指針 存在于<棧區(qū)>
str = @“1234”陪踩;//@“1234”這個(gè)@對(duì)象存在于 <堆區(qū)>
static NSString *str1;//str1這個(gè)NSString類(lèi)型的指針 存在于<全局區(qū)>的<BBS區(qū)>
static NSString *str2 = @"4321';//str2這個(gè)NSString類(lèi)型的指針 存在于<全局區(qū)>的<data區(qū)>
NSString *str3;//str3這個(gè)NSString類(lèi)型的指針 存在于<棧區(qū)>
str3 = [[NSString alloc]initWithString:@"1234"];//[[NSString alloc]initWithString:@"1234"]這個(gè)對(duì)象 存在于<堆區(qū)>
}
1.6 static靜態(tài)變量
靜態(tài)變量有兩種
- 全局靜態(tài)變量
優(yōu)點(diǎn)
:不管對(duì)象方法還是類(lèi)方法都可以訪問(wèn)和修改全局靜態(tài)變量,并且外部類(lèi)無(wú)法調(diào)用靜態(tài)變量悉抵,定義后只會(huì)指向固定的指針地址肩狂,供所有對(duì)象使用,節(jié)省空間姥饰。
缺點(diǎn)
:存在的生命周期長(zhǎng)傻谁,從定義直到程序結(jié)束。
建議
:從內(nèi)存優(yōu)化和程序編譯的角度來(lái)說(shuō)列粪,盡量少用全局靜態(tài)變量审磁,因?yàn)榇嬖诘穆暶髦芷陂L(zhǎng),一直占用空間岂座。程序運(yùn)行時(shí)會(huì)單獨(dú)加載一次全局靜態(tài)變量态蒂,過(guò)多的全局靜態(tài)變量會(huì)造成程序啟動(dòng)慢,當(dāng)然就目前的手機(jī)處理器性能掺逼,幾十幾百個(gè)估計(jì)也影響不大吧吃媒。 - 局部靜態(tài)變量
優(yōu)點(diǎn)
:定義后只會(huì)存在一份值,每次調(diào)用都是使用的同一個(gè)對(duì)象內(nèi)存地址的值吕喘,并沒(méi)有重新創(chuàng)建赘那,節(jié)省空間,只能在該局部代碼塊中使用氯质。
缺點(diǎn)
:存在的生命周期長(zhǎng)募舟,從定義直到程序結(jié)束,只能在該局部代碼塊中使用闻察。
建議
:局部和全局靜態(tài)變量從本根意義上沒(méi)有什么區(qū)別拱礁,只是作用域不同而已琢锋。如果值僅一個(gè)類(lèi)中的對(duì)象和類(lèi)方法使用并且值可變,可以定義全局靜態(tài)變量呢灶,如果是多個(gè)類(lèi)使用并可變吴超,建議值定義在model作為成員變量使用。如果是不可變值鸯乃,建議使用宏定義
static NSString *name;
1.7 extern全局變量
全局變量有兩種
- 對(duì)內(nèi)的全局變量:沒(méi)有用extern在.h中修飾的變量鲸阻,僅定義在.m中讓該變量只能在該類(lèi)使用
優(yōu)點(diǎn)
:不管對(duì)象方法還是類(lèi)方法都可以訪問(wèn)和修改全局靜態(tài)變量,并且外部類(lèi)無(wú)法調(diào)用靜態(tài)變量缨睡,定義后只會(huì)存一份值鸟悴,供所有對(duì)象使用,節(jié)省空間奖年。跟全局靜態(tài)變量一樣细诸,只是少了static修飾少了static特性
缺點(diǎn)
:存在的生命周期長(zhǎng),從定義直到程序結(jié)束
建議
:跟全局靜態(tài)變量都一樣了陋守,還需要用對(duì)內(nèi)的全局變量嗎震贵?不用extern修飾就少了extern的特性,還不如用全局靜態(tài)變量嗅义,至少能明確的知道static是對(duì)內(nèi)使用的 - 外部全局變量:除了該類(lèi)屏歹,其他文件也可以訪問(wèn)該變量
優(yōu)點(diǎn)
:除了該類(lèi)隐砸,其他文件也可以訪問(wèn)該變量
缺點(diǎn)
:存在的生命周期長(zhǎng)之碗,從定義直到程序結(jié)束。并且外部可以修改其值季希,出現(xiàn)錯(cuò)誤不容易定位
建議
:使用全局變量的原因就在于其對(duì)外的特性褪那,但是其使用的方便性沒(méi)有使用model的屬性或宏來(lái)得方便。程序啟動(dòng)的時(shí)候會(huì)單獨(dú)加載全局變量式塌,同理與全局靜態(tài)變量博敬,少使用。
.m中要定義
NSString *name;
.h中同時(shí)要定義
extern NSString *name;
全局靜態(tài)變量與全局變量 其實(shí)本質(zhì)上是沒(méi)有區(qū)別的峰尝,只是存在修飾區(qū)別偏窝,一個(gè)static讓其只能內(nèi)部使用,一個(gè)extern讓其可以外部使用
1.8 const常量
不同于變量武学,常量的值是固定不可變的祭往,一般用于只讀值。
優(yōu)點(diǎn)
:只可以讀取值火窒,不能修改硼补。一般用于接口或者文字顯示這種固定值。添加extern可以對(duì)外全局常量,任意位置都可以訪問(wèn)
缺點(diǎn)
:存在的生命周期長(zhǎng)熏矿,從定義直到程序結(jié)束已骇。需要在.h .m中分別定義代碼較多
建議
:良好的編碼習(xí)慣而言离钝,少使用宏,多使用常量褪储。因?yàn)槌A柯暶魇怯忻鞔_類(lèi)型的卵渴,而宏只是替換并不能進(jìn)行類(lèi)型判斷。不夠嚴(yán)謹(jǐn)鲤竹。
.h中定義extern
extern NSString *const name;
.m中定義值
NSString *const name = @"123";
//const聲明部位不同奖恰,意義也不同。const定義的是其右邊整體不可變宛裕。
//*const name1定義的是 name1 不可變瑟啃。 name1是指針。
//因此 不能通過(guò)修改name1而指向其他值揩尸。常規(guī)的const使用這個(gè)方法定義不可修改的值
NSString *const name1 = @"456";
//const * name定義的是 * name不可變蛹屿, 而*name指向的是@"123",
//也就是說(shuō)@"123"這個(gè)內(nèi)存地址的值不可變?yōu)槠渌怠?NSString const * name2 = @"789";
//但name指針可以指向其他值岩榆,所以該定義方式無(wú)法保證值的唯一性错负。
NSString *newName = @"222";
name2 = newName;
2.屬性標(biāo)識(shí)符
- 2.1 @property、@synthesize勇边、@dynamic
@synthesize:作用于在@implementation內(nèi)部犹撒,用@synthesiz聲明的屬性在編譯的時(shí)候會(huì)自動(dòng)為該屬性按照固有規(guī)則生成相應(yīng)的getter setter方法。如果有手動(dòng)生成getter setter方法也不會(huì)報(bào)錯(cuò)粒褒。
//array是聲明的屬性名稱(chēng)识颊,_array是編譯器自動(dòng)生成的成員變量。
@synthesize array = _array;
@dynamic:作用于在@implementation內(nèi)部奕坟,與@synthesize不同祥款,使用@dynamic聲明時(shí)相當(dāng)于告訴編譯器getter setter方法由用戶自己生成。如果聲明為@dynamic而沒(méi)有手動(dòng)生成getter setter方法編譯的時(shí)候不報(bào)錯(cuò)月杉,但是在運(yùn)行時(shí)如果使用.語(yǔ)法
去調(diào)用該屬性時(shí)會(huì)崩潰刃跛。之所以在運(yùn)行時(shí)才會(huì)發(fā)生崩潰是因?yàn)镺C具有動(dòng)態(tài)綁定特性。只有在運(yùn)行時(shí)才會(huì)去確認(rèn)具體的調(diào)用方法苛萎。
@interface TestObject()
{
NSMutableDictionary *_dic;
}
@property (nonatomic, strong) NSMutableDictionary *dic;
@end
//不同于@ synthesize桨昙, @dynamic只需要聲明屬性名稱(chēng)即可,不需要dic = _dic把值賦值過(guò)去腌歉。但是需要自己在@interface中聲明成員變量蛙酪。
@dynamic dic;
@property:相對(duì)于@dynamic 和 @synthesize ,@property聲明的作用區(qū)域在@interface內(nèi)部究履。 它會(huì)告訴編譯器自動(dòng)生成getter setter方法滤否。也允許用戶手動(dòng)生成getter setter中的一個(gè)方法,用@property聲明的屬性不能手動(dòng)同時(shí)寫(xiě)getter setter方法最仑,否則編譯器會(huì)報(bào)錯(cuò)藐俺。@property更好的聲明屬性變量炊甲。因?yàn)樵L問(wèn)方法的命名約定,可以很清晰的看出getter和setter的用處欲芹,會(huì)傳遞一些額外信息卿啡,后面會(huì)跟相應(yīng)的各種信息例如:@property (nonatomic, strong,onlyread) NSString *name;大多數(shù)時(shí)候都用的@property聲明
@interface TestObject()
{
NSMutableDictionary *_dic;
}
//因?yàn)槭褂昧薂property聲明菱父,編譯器會(huì)自動(dòng)生成相應(yīng)getter setter方法颈娜。
//使用@property不能手動(dòng)同時(shí)生成getter setter方法,編譯器會(huì)報(bào)錯(cuò)
//nonatomic表示屬性是非原子操作浙宜,strong表示強(qiáng)引用官辽,
//readonly表示該屬性權(quán)限為僅讀,那么編譯器只會(huì)生成getter方法,不會(huì)生成setter方法
@property (nonatomic, strong,readonly) NSString *name;
@property (nonatomic, strong) NSMutableArray *array;
@property (nonatomic, strong) NSMutableDictionary *dic;
@end
@implementation TestObject
//如果上面的array使用的@property聲明粟瞬,而用戶又要手動(dòng)同時(shí)生成getter setter方法
//可以使用@synthesize 告訴編譯器 該屬性getter setter方法如果沒(méi)有手動(dòng)聲明就自動(dòng)創(chuàng)建同仆,有就不自動(dòng)生成。
@synthesize array = _array;
//如果dic用@property聲明過(guò)了裙品,會(huì)自動(dòng)生成getter setter方法俗批。但是又不希望它自動(dòng)生成getter setter方法。
//可以用@dynamic 聲明市怎。告訴編譯器 該屬性的getter setter方法不自動(dòng)生成
//但如果要自己生成getter setter必須在@ interface內(nèi)部聲明對(duì)應(yīng)的成員變量
@dynamic dic;
- (void)setArray:(NSMutableArray *)array
{
_array = array;
}
- (NSMutableArray *)array
{
if (!_array) {
_array = [NSMutableArray new];
}
return _array;
}
- (void)setArray:(NSMutableDictionary *)dic
{
_dic = dic;
}
- (NSMutableDictionary *)dic
{
if (!_dic) {
_dic = [NSMutableDictionary new];
}
return _dic;
}
@end
- 2.2 nonatomic與atomic
nonatomic(非原子性):在調(diào)用用nonatomic聲明的對(duì)象屬性時(shí)是非線程安全性的岁忘。最為直觀的就是NSMutableArray的使用。當(dāng)同時(shí)在子線程去增刪數(shù)組元素区匠,在主線程中去遍歷數(shù)組元素就會(huì)出現(xiàn)數(shù)組越界或者數(shù)組沒(méi)有遍歷完干像。因?yàn)椴捎玫膎onatomic,不同操作可以同時(shí)執(zhí)行辱志,而不需要等前面的操作完成后在進(jìn)行下一步操作蝠筑。所以稱(chēng)之為非線程安全。非原子性的執(zhí)行效率更高不會(huì)阻塞線程
atomic(原子性):相反與非原子性揩懒,atomic是具有線程安全性的。當(dāng)屬性聲明為atomic時(shí)挽封,調(diào)用該屬性的getter/setter方法已球,會(huì)加上同步鎖,保證同一時(shí)刻只能有一個(gè)線程調(diào)用屬性的getter/setter方法辅愿。但是atomic只能保證線程的讀和寫(xiě)過(guò)程是可靠的智亮。但并不能保證數(shù)據(jù)一定是可靠的。例如:有線程A和線程B的情況下点待,從調(diào)度順序來(lái)說(shuō)阔蛉,線程A先調(diào)用了setter方法修改了屬性值,然后線程B也調(diào)用了setter方法再次修改了屬性值癞埠,最后線程A調(diào)用getter方法獲取屬性值時(shí)状原,獲取到的結(jié)果值可能是線程A修改的值聋呢,也可能是線程B修改的值,因?yàn)锳颠区、B是兩個(gè)線程異步的削锰,最后A調(diào)用getter獲取的值,取決于B線程是已經(jīng)設(shè)置成功還是B線程還沒(méi)有執(zhí)行setter方法毕莱。
- 2.3 strong朋截、weak、retain部服、assgin稳摄、copy厦酬、unsafe_unretained
retain:釋放舊對(duì)象,提高輸入對(duì)象的引用計(jì)數(shù)+1瘫想,將輸入對(duì)象的值賦值于舊對(duì)象,只能用戶聲明OC對(duì)象
@property (nonatomic, retain) Room *room;
- (void)setRoom:(Room *)room // room = r
{
// 只有房間不同才需用release和retain
if (_room != room) {
// 將以前的房間釋放掉 -1,將舊對(duì)象釋放
[_room release];
// MRC中需要手動(dòng)對(duì)房間的引用計(jì)數(shù)器+1
[room retain];
_room = room;
}
}
strong:強(qiáng)引用,它是ARC特有
减噪。在MRC時(shí)代沒(méi)有筹裕,相當(dāng)于retain窄驹。由于MRC時(shí)代是靠引用計(jì)數(shù)器來(lái)管理對(duì)象什么時(shí)候被銷(xiāo)毀所以用retain乐埠,而ARC時(shí)代管理對(duì)象的銷(xiāo)毀是有系統(tǒng)自動(dòng)判斷丈咐,判斷的依據(jù)就是該對(duì)象是否有強(qiáng)引用對(duì)象棵逊。如果對(duì)象沒(méi)有被任何地方強(qiáng)引用就會(huì)被銷(xiāo)毀辆影。所以在ARC時(shí)代基本都用的strong來(lái)聲明代替了retain。只能用于聲明OC對(duì)象(ARC特有)
蘋(píng)果官網(wǎng)對(duì)strong的解釋代碼:
Precondition:object is a valid pointer to a __strong object which is adequately aligned for a pointer. value is null or a pointer to a valid object.
Performs the complete sequence for assigning to a __strong object of non-block type [[*]]. Equivalent to the following code:
void objc_storeStrong(id *object, id value) {
id oldValue = *object;
value = [value retain];
*object = value;
[oldValue release];
}
assgin:簡(jiǎn)單的賦值操作衅澈,不會(huì)更改引用計(jì)數(shù)今布,用于基本的數(shù)據(jù)類(lèi)型聲明部默。
weak:弱引用造虎,表示該屬性是一種“非擁有關(guān)系”算凿。為這種屬性設(shè)置新值時(shí)既不會(huì)保留新值也不會(huì)釋放舊值氓轰,類(lèi)似于assgin署鸡。 然而在對(duì)象被摧毀時(shí)靴庆,屬性也會(huì)被清空(nil out)炉抒。這樣可以有效的防止崩潰(因?yàn)镺C中給沒(méi)有對(duì)象地址的指針發(fā)送消息不會(huì)崩潰端礼,而給有內(nèi)存地址但地址中是空對(duì)象的指針發(fā)消息會(huì)崩潰蛤奥,野指針)凡桥,該聲明必須作用于OC對(duì)象缅刽。
對(duì)于 weak 對(duì)象會(huì)放入一個(gè) hash 表中衰猛, 用 weak 指向的對(duì)象內(nèi)存地址作為 key啡省,當(dāng)此對(duì)象的引用計(jì)數(shù)為0的時(shí)候會(huì)調(diào)用 dealloc卦睹, 使用key在weak 表中搜索结序,將找到的所有對(duì)象設(shè)置為 nil徐鹤。(ARC特有),strong 和 weak的指針返敬,根本區(qū)別在于救赐,strong執(zhí)行了retain操作经磅,而weak沒(méi)有预厌。
runtime 如何實(shí)現(xiàn) weak 變量的自動(dòng)置 nil轧叽?
runtime會(huì)對(duì)注冊(cè)的類(lèi)進(jìn)行內(nèi)存布局炭晒,對(duì)于 weak 修飾的對(duì)象會(huì)放入一個(gè) hash 表中网严。
1.初始化時(shí):runtime 會(huì)調(diào)用 objc_initWeak 函數(shù),初始化一個(gè)新的 weak 指針指向?qū)ο蟮牡刂贰?br>
2.添加引用時(shí):objc_initWeak 函數(shù)會(huì)調(diào)用 objc_storeWeak() 函數(shù)当犯, objc_storeWeak()的作用是更新指 針指向嚎卫,創(chuàng)建對(duì)應(yīng)的弱引用表拓诸。
3.釋放時(shí),調(diào)用 clearDeallocating 函數(shù)恰响。clearDeallocating 函數(shù)首先根據(jù)對(duì)象地址獲取所有 weak 指針 地址的數(shù)組,然后遍歷這個(gè)數(shù)組把其中的數(shù)據(jù)設(shè)為 nil枢劝,最后把這個(gè) entry 從 weak 表中刪除您旁,最后清理對(duì)象的記錄鹤盒。
蘋(píng)果官網(wǎng)對(duì)weak的說(shuō)明
id objc_storeWeak(id *object, id value);
Precondition: object is a valid pointer which either contains a null pointer or has been registered as a __weak >object. value is null or a pointer to a valid object.If value is a null pointer or the object to which it points has begun deallocation, object is assigned null and >unregistered as a __weak object. Otherwise, object is registered as a __weak object or has its registration >updated to point to value.
Returns the value of object after the call.
copy:不同于其他聲明侦锯,copy會(huì)根據(jù)聲明的屬性是否是可變類(lèi)型而進(jìn)行不同操作尺碰。如果對(duì)象是一個(gè)不可變對(duì)象亲桥,例如NSArray NSString 等,那么copy等同于retain悼凑、strong户辫。如果對(duì)象是一個(gè)可變對(duì)象渔欢,例如:NSMutableArray,NSMutableString等奥额,它會(huì)在內(nèi)存中重新開(kāi)辟了一個(gè)新的內(nèi)存空間,用來(lái) 存儲(chǔ)新的對(duì)象,和原來(lái)的對(duì)象是兩個(gè)不同的地址,引用計(jì)數(shù)分別為1. 這就是所謂的深拷貝淺拷貝垫挨,淺拷貝只是copy了對(duì)象的內(nèi)存地址九榔,而深拷貝是重新在內(nèi)存中開(kāi)辟新空間哲泊,新空間對(duì)象值與拷貝對(duì)象的值一樣。但是是完全不同的2個(gè)內(nèi)存地址颂跨。 例如copy修飾的類(lèi)型為 NSString不可變對(duì)象時(shí)喳魏,copy可以保護(hù)其封裝性截酷,當(dāng)賦值對(duì)象是一個(gè) NSMutableString 類(lèi)時(shí)(NSMutableString是 NSString 的子類(lèi)迂苛,表示一種可修改其值的字符串)三幻,此時(shí)若是不用copy修飾拷貝字符串念搬,那么賦值該對(duì)象之后首妖,賦值對(duì)象字符串的值就可能會(huì)在其他地方被修改有缆,修改后賦值后對(duì)象也會(huì)改變棚壁,造成值不對(duì)袖外。所以曼验,這時(shí)就要拷貝一份“不可變” (immutable)的字符串,確保對(duì)象中的字符串值不會(huì)無(wú)意間變動(dòng)颖杏。只要實(shí)現(xiàn)屬性所用的對(duì)象是“可變的” (mutable)留储,就應(yīng)該在設(shè)置新屬性值時(shí)拷貝一份获讳。
1.當(dāng)不可變對(duì)象進(jìn)行copy的時(shí)候,本質(zhì)上只是創(chuàng)建了一個(gè)新的指針帅矗,指向了copy對(duì)象的內(nèi)存空間浑此。所以新指針與原指針?biāo)赶虻牡刂废嗤?br>
2.當(dāng)可變對(duì)象進(jìn)行copy操作的時(shí)候凛俱,由于copy方法返回的對(duì)象都是不可變對(duì)象蒲犬,所以對(duì)可變對(duì)象進(jìn)行copy操作相當(dāng)于單層深拷貝赌朋,創(chuàng)建了一個(gè)新指針,并重新再內(nèi)存中分配了一個(gè)新的對(duì)象达布,但是這個(gè)對(duì)象是不可變對(duì)象黍聂,而非原對(duì)象的可變對(duì)象。所以這是一種單層的深拷貝脐区。
3.不可變對(duì)象進(jìn)行mutableCopy的時(shí)候牛隅,本質(zhì)上是進(jìn)行了深拷貝媒佣,創(chuàng)建了新指針,并深拷貝重新開(kāi)辟了一個(gè)新的內(nèi)存空間放置拷貝的對(duì)象也糊。所以指針和指針?biāo)赶虻膬?nèi)存地址與原對(duì)象都不一樣显设。并且新的內(nèi)存中實(shí)際對(duì)象是一個(gè)可變對(duì)象瑟枫。
4.可變對(duì)象進(jìn)行mutableCopy與不可變對(duì)象進(jìn)行mutableCopy本質(zhì)是一樣的慷妙。
5.copy一個(gè)可變對(duì)象并賦值給一個(gè)不可變對(duì)象膝擂,由于copy方法返回的是不可變對(duì)象,因此會(huì)對(duì)原可變對(duì)象進(jìn)行深拷貝叉寂,新的內(nèi)存地址屏鳍,新的指針钓瞭。并且copy的結(jié)果是一個(gè)不可變對(duì)象
6.copy一個(gè)不可變對(duì)象并復(fù)制給一個(gè)可變對(duì)象,同理與上面5的解答佳鳖,雖然最終賦值給一個(gè)可變的指針系吩,但是內(nèi)存地址中實(shí)際是一個(gè)不可變對(duì)象。
7.mutableCopy一個(gè)不可變對(duì)象科盛,并賦值給一個(gè)可變對(duì)象。對(duì)原對(duì)象進(jìn)行深拷貝榨崩,由于mutableCopy方法返回的是一個(gè)可變對(duì)母蛛。所以實(shí)際內(nèi)存中是一個(gè)可變對(duì)象
unsafe_unretained:和weak 差不多前弯,唯一的區(qū)別便是恕出,對(duì)象即使被銷(xiāo)毀剃根,指針也不會(huì)自動(dòng)置空廉油, 對(duì)象被銷(xiāo)毀后指針指向的是一個(gè)無(wú)用的內(nèi)存地址(野地址)抒线。如果對(duì)象銷(xiāo)毀后后還使用此指針抱慌,程序會(huì)拋出 BAD_ACCESS 的異常抑进。 所以一般不使用unsafe_unretained。目前我還未在實(shí)際項(xiàng)目中使用過(guò)該聲明信殊。(ARC特有)
- 2.4 readOnly、readWrite鳄乏、getter=姓赤、setter=
readOnly表示屬性僅能讀不能設(shè)置其值不铆。告訴編譯器只生成getter方法不生成setter方法。
readWrite默認(rèn)值,表示屬性可讀可寫(xiě)劳坑。編譯器會(huì)自動(dòng)生成getter setter方法
getter=指定屬性gettter方法的方法名
@property (nonatomic, strong, getter=getMyDic) NSMutableDictionary *dic;
setter=指定屬性setter方法的方法名
@property (nonatomic, strong,setter=myArray:) NSMutableArray *arr;
2.5__unsafe_unretained、__weak框仔、__strong
__unsafe_unretained
NSMutableArray __unsafe_unretained *array = [[NSMutableArray alloc]init];
[array addObject:@"123"];
使用__unsafe_unretained修飾符的變量與使用__weak修飾符的變量一樣,因?yàn)樽约荷刹⒊钟械膶?duì)象不能繼續(xù)為自己持有瘪匿,所以生成的對(duì)象會(huì)立即被釋放跛梗。也就是說(shuō)在執(zhí)行完init方法以后,對(duì)象指針?biāo)赶虻膬?nèi)存就已經(jīng)釋放掉了棋弥,但因?yàn)橛玫腳_unsafe_unretained修飾指針并沒(méi)不像__weak的指針那樣核偿,將指針自動(dòng)置為nil,它依然指向原來(lái)的地址嘁锯,可是這塊地址的內(nèi)存已經(jīng)被系統(tǒng)回收了,再訪問(wèn)就是非法的家乘,也就是野指針蝗羊,再執(zhí)行后面的addObject方法自然會(huì)出錯(cuò)了。
__weak
主要用于解決循環(huán)引用仁锯,用__weak修飾的變量 當(dāng)對(duì)象釋放后耀找,指針自動(dòng)設(shè)置為nil,當(dāng)后面繼續(xù)使用該指針變量的時(shí)候不會(huì)造成crash,更不會(huì)造成強(qiáng)引用使該釋放的對(duì)象無(wú)法釋放野芒,造成內(nèi)存泄露蓄愁。
__weak typeof(self) weakSelf = self;
__strong
相反與__weak,主要用于當(dāng)使用某個(gè)對(duì)象時(shí),希望它沒(méi)有提前被釋放狞悲。強(qiáng)引用該對(duì)象使其無(wú)法釋放撮抓。例如在block內(nèi)部,希望block調(diào)用時(shí)該對(duì)象不會(huì)被提前釋放造成錯(cuò)誤摇锋〉ふ可以使用強(qiáng)引用。
TestAlertView *alertView = [TestAlertView new];
alertView = ^()
{
//當(dāng)block內(nèi)部需要使用本身這個(gè)局部對(duì)象時(shí)荸恕,需要用強(qiáng)引用方式乖酬,讓alertView在傳遞完block后不會(huì)被釋放依然可以執(zhí)行setTitle操作
__strong typeof(alertView) strongAlertView = alertView;
[strongAlertView setTitle:@"1234"];
}
[alertView show];
3 MRC與ARC區(qū)別
3.1 MRC手動(dòng)內(nèi)存管理
引用計(jì)數(shù)器:在MRC時(shí)代,系統(tǒng)判定一個(gè)對(duì)象是否銷(xiāo)毀是根據(jù)這個(gè)對(duì)象的引用計(jì)數(shù)器來(lái)判斷的融求。
1.每個(gè)對(duì)象被創(chuàng)建時(shí)引用計(jì)數(shù)都為1
2.每當(dāng)對(duì)象被其他指針引用時(shí)咬像,需要手動(dòng)使用[obj retain];讓該對(duì)象引用計(jì)數(shù)+1。
3.當(dāng)指針變量不在使用這個(gè)對(duì)象的時(shí)候生宛,需要手動(dòng)釋放release這個(gè)對(duì)象县昂。 讓其的引用計(jì)數(shù)-1.
4.當(dāng)一個(gè)對(duì)象的引用計(jì)數(shù)為0的時(shí)候,系統(tǒng)就會(huì)銷(xiāo)毀這個(gè)對(duì)象陷舅。
NSMutableArray *array = [NSMutableArray array];//[NSMutableArray array]創(chuàng)建后引用計(jì)數(shù)器為1
NSLog(@"array的對(duì)象地址:%p,array的retainCount:%zd",array,[array retainCount]);
[array release];//調(diào)用release后[NSMutableArray array]創(chuàng)建的對(duì)象引用計(jì)數(shù)-1.
//當(dāng)程序執(zhí)行到[array addObject:@"1234"];這里是就會(huì)崩潰七芭。因?yàn)榇藭r(shí)array指針指向的內(nèi)存地址中沒(méi)有任何對(duì)象,該指針是一個(gè)野指針蔑赘。
//因?yàn)閞elease后[NSMutableArray array]創(chuàng)建的對(duì)象引用計(jì)數(shù)變?yōu)榱?.系統(tǒng)就會(huì)銷(xiāo)毀這個(gè)內(nèi)存地址的對(duì)象。
[array addObject:@"1234"];
NSLog(@"array的對(duì)象地址:%p,array的retainCount:%zd",array,[array retainCount]);
NSLog(@"%@",array);
在MRC模式下必須遵循誰(shuí)創(chuàng)建预明,誰(shuí)釋放缩赛,誰(shuí)引用,誰(shuí)管理
在MRC下使用ARC
在Build Phases的Compile Sources中選擇需要使用MRC方式的.m文件撰糠,然后雙擊該文件在彈出的會(huì)話框中輸入 -fobjc-arc
3.2 ARC自動(dòng)內(nèi)存管理
WWDC2011和iOS5所引入自動(dòng)管理機(jī)制——自動(dòng)引用計(jì)數(shù)(ARC)酥馍,它不是垃圾回收機(jī)制而是編譯器的一種特性。ARC管理機(jī)制與MRC手動(dòng)機(jī)制差不多阅酪,只是不再需要手動(dòng)調(diào)用retain旨袒、release、autorelease术辐;當(dāng)你使用ARC時(shí)砚尽,編譯器會(huì)在在適當(dāng)位置插入release和autorelease;ARC時(shí)代引入了strong強(qiáng)引用來(lái)帶代替retain必孤,引入了weak弱引用赡勘。
在ARC下使用MRC方法
在ARC工程中如果要使用MRC的需要在工程的Build Phases的Compile Sources中選擇需要使用MRC方式的.m文件几迄,然后雙擊該文件在彈出的會(huì)話框中輸入 -fno-objc-arc
在非MRC文件中無(wú)法使用retain release retainCount 方法,無(wú)法再dealloc方法中調(diào)用[super dealloc];方法
3.3 autoreleasepool自動(dòng)釋放池
自動(dòng)釋放池始于MRC時(shí)代弛姜,主要是用于 自動(dòng) 對(duì) 釋放池內(nèi) 對(duì)象 進(jìn)行引用計(jì)數(shù)-1的操作荠商,即自動(dòng)執(zhí)行release方法饰躲。
autorelease焦蘑、autoreleasepool宁舰、release
autorelease:本質(zhì)是將對(duì)象放入當(dāng)前的自動(dòng)釋放池中
autoreleasepool:本質(zhì)是當(dāng)前runloop銷(xiāo)毀自動(dòng)釋放池時(shí)會(huì)自動(dòng)對(duì)自動(dòng)釋放池內(nèi)的對(duì)象調(diào)用release
release:是對(duì)當(dāng)前對(duì)象的引用計(jì)數(shù)進(jìn)行減1的操作壤蚜,如果對(duì)象引用計(jì)算為0莺丑,則會(huì)釋放對(duì)象的內(nèi)存空間昏名。autoreleasepool的原理
autoreleasepool本質(zhì)上是一個(gè)__AtAutoreleasePool的結(jié)構(gòu)體仑扑,通過(guò)__AtAutoreleasePool的構(gòu)造函數(shù)和析構(gòu)函數(shù)去實(shí)現(xiàn)對(duì)池內(nèi)的對(duì)象進(jìn)行自動(dòng)釋放盒让。1.當(dāng)對(duì)象調(diào)用autorelease方法時(shí),會(huì)將對(duì)象加入autoreleasepool的棧中彤路,而autoreleasepool會(huì)在__AtAutoreleasePool結(jié)構(gòu)體的構(gòu)造函數(shù)中調(diào)用autoreleasepoolPage::push方法將自動(dòng)釋放池中的對(duì)象加入到雙向鏈表中洲尊,每個(gè)autoreleasepoolpage大小都是4096個(gè)字節(jié)远豺。所以如果autoreleasepool中對(duì)象超過(guò)4096個(gè)字節(jié)。那么就會(huì)再創(chuàng)建一個(gè)autoreleasepoolpage坞嘀。因此autoreleasepoolpage有多個(gè)躯护。
2.當(dāng)__AtAutoreleasePool的結(jié)構(gòu)體被銷(xiāo)毀時(shí),會(huì)自動(dòng)調(diào)用析構(gòu)函數(shù)丽涩,通過(guò)析構(gòu)函數(shù)調(diào)用autoreleasepoolPage::pop方法會(huì)向棧中的對(duì)象發(fā)送release消息
在MRC中使用autoreleasepool必須在代碼塊內(nèi)部手動(dòng)為對(duì)象調(diào)用autorelease把對(duì)象加入到的自動(dòng)釋放池棺滞,系統(tǒng)會(huì)自動(dòng)在代碼塊結(jié)束后,對(duì)加入自動(dòng)釋放池中的對(duì)象發(fā)送一個(gè)release消息内狸。無(wú)需手動(dòng)調(diào)用release
int main(int argc, const char * argv[])
{
Person *father = [[Person alloc] init];//引用計(jì)數(shù)為1
@autoreleasepool {//這里創(chuàng)建自動(dòng)釋放池
int a = 10; // a在棧检眯,10在常量區(qū)
int b = 20; //b在棧,20在常量區(qū)
// p : 棧
// [[Person alloc] init]創(chuàng)建的對(duì)象(引用計(jì)數(shù)器==1) : 在堆
Person *p = [[Person alloc] init];
//MRC下需要手動(dòng)讓對(duì)象加入自動(dòng)釋放池
[p autorelease];
Person *pFather = father;
[father retain];//father指針指向的對(duì)象被pFather引用昆淡,在MRC下需要手動(dòng)讓被引用對(duì)象引用計(jì)數(shù)+1
NSLog(@"pFather對(duì)象內(nèi)存地址:%p,pFather的引用計(jì)數(shù):%zd",pFather,[pFather retainCount]);
NSLog(@"father對(duì)象內(nèi)存地址:%p,father的引用計(jì)數(shù):%zd",father,[father retainCount]);
}//這里釋放 自動(dòng)釋放池
// 當(dāng)autoreleasepool內(nèi)部代碼塊執(zhí)行完畢后上面代碼塊后, 棧里面的變量a锰瘸、b、p 都會(huì)被回收
// 但是堆里面的Person對(duì)象還會(huì)留在內(nèi)存中,因?yàn)樗怯?jì)數(shù)器依然是1昂灵。當(dāng)autoreleasepool代碼塊執(zhí)行完畢后避凝,會(huì)對(duì)釋放池內(nèi)部的所有對(duì)象執(zhí)行一個(gè)release消息。如果發(fā)送release消息后眨补,對(duì)象引用計(jì)數(shù)為0了管削,那么就會(huì)被系統(tǒng)回收。
NSLog(@"father對(duì)象內(nèi)存地址:%p,father的引用計(jì)數(shù):%zd",father,[father retainCount]);
return 0;
}
在ARC中對(duì)@autoreleasepool的使用相比MRC不太多撑螺。主要用于一些大內(nèi)存消耗對(duì)象的重復(fù)創(chuàng)建時(shí)含思,保證內(nèi)存處于比較優(yōu)越的狀態(tài)。常用于創(chuàng)建對(duì)象較多的for循環(huán)中甘晤。在ARC下不要手動(dòng)的為@autoreleasepool代碼塊內(nèi)部對(duì)象添加autorelease含潘,ARC下自動(dòng)的把@autoreleasepool代碼塊中創(chuàng)建的對(duì)象加入了自動(dòng)釋放池中。
for (int i = 0; i < 10000000; i++)
{
@autoreleasepool{
NSMutableArray *array = [NSMutableArray new];
NSMutableDictionary *dic = [NSMutableDictionary new];
NSMutableArray *array1 = [NSMutableArray new];
NSMutableDictionary *dic1 = [NSMutableDictionary new];
NSMutableArray *array2 = [NSMutableArray new];
NSMutableDictionary *dic2 = [NSMutableDictionary new];
NSData *data = UIImageJPEGRepresentation([UIImage imageNamed:@"testimage"], 1);
NSError *error;
NSURL *url = [NSURL URLWithString:@"www.baidu.com"];
NSString *fileContents = [NSString stringWithContentsOfURL:url
encoding:NSUTF8StringEncoding
error:&error];
}
}
可以從上圖看出线婚,使用autoreleasepool時(shí)遏弱,內(nèi)存一直保持穩(wěn)定狀態(tài),上次幾乎沒(méi)浮動(dòng)
因?yàn)闆](méi)有使用 autoreleasepool塞弊,隨著for循環(huán)一直執(zhí)行下去漱逸,內(nèi)存一直在上升泪姨。
autorelease對(duì)象釋放的時(shí)機(jī)
1.系統(tǒng)自行創(chuàng)建的@autoreleasepool釋放時(shí)機(jī)
線程與Runloop是一對(duì)一關(guān)系,主線程中會(huì)自動(dòng)創(chuàng)建Runloop饰抒,而子線程需要自行調(diào)用Runloop來(lái)讓子線程自動(dòng)創(chuàng)建Runloop肮砾。當(dāng)@autoreleasepool加入到某個(gè)線程時(shí),該線程的Runloop會(huì)
借用runloop的Autorelease對(duì)象釋放的背后的解釋(ARC環(huán)境下)
圖中第1步 Observer 監(jiān)視的事件是 Entry(即將進(jìn)入Loop)循集,其回調(diào)內(nèi)會(huì)調(diào)用 _objc_autoreleasePoolPush() 創(chuàng)建自動(dòng)釋放池唇敞。其 order 是-2147483647,優(yōu)先級(jí)最高咒彤,保證創(chuàng)建釋放池發(fā)生在其他所有回調(diào)之前
圖中第6步 Observer 監(jiān)視了兩個(gè)事件: BeforeWaiting(準(zhǔn)備進(jìn)入休眠) 時(shí)調(diào)用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 釋放舊的池并創(chuàng)建新池疆柔;Exit(即將退出Loop) 時(shí)調(diào)用 _objc_autoreleasePoolPop() 來(lái)釋放自動(dòng)釋放池。這個(gè) Observer 的 order 是 2147483647镶柱,優(yōu)先級(jí)最低旷档,保證其釋放池子發(fā)生在其他所有回調(diào)之后。
圖中第10 Observer 監(jiān)視事件是exit(即講退出runloop)歇拆,其回調(diào)內(nèi)會(huì)調(diào)用 _objc_autoreleasePoolpop() 釋放自動(dòng)釋放池鞋屈。
從上面就能看出,Runloop中系統(tǒng)自動(dòng)創(chuàng)建的@autoreleasepool是在準(zhǔn)備進(jìn)入休眠狀態(tài)才被銷(xiāo)毀的故觅。所以在ARC下厂庇,在線程中的臨時(shí)對(duì)象是在當(dāng)前線程的Runloop進(jìn)入休眠或者退出loop或者退出線程時(shí)被執(zhí)行release的。
2.自己創(chuàng)建的@autoreleasepool
@autoreleasepool
{//這個(gè){開(kāi)始創(chuàng)建的自動(dòng)釋放池输吏,這里開(kāi)始內(nèi)部的對(duì)象自動(dòng)加入autorelease
}//這個(gè)}開(kāi)始权旷,不必再對(duì) 對(duì)象加入autorelease,自動(dòng)釋放池被銷(xiāo)毀贯溅。
從上面就能看出拄氯,自行創(chuàng)建的@autoreleasepool,是在}后被釋放的它浅,而其中的autorelease對(duì)象译柏,也是在這個(gè)時(shí)候自動(dòng)執(zhí)行的release操作。
從 1
, 2
兩點(diǎn)可以看出姐霍,雖然autorelease對(duì)象釋放的時(shí)機(jī)并不都是在代碼塊結(jié)束后就釋放鄙麦。但是他們有一個(gè)共同特性,那就是必定是在@autoreleasepool被銷(xiāo)毀時(shí)镊折,釋放的黔衡。 所以要清楚autorelease對(duì)象什么時(shí)候被釋放,只需要搞清楚@autoreleasepool什么時(shí)候被銷(xiāo)毀即可
主線程中既有系統(tǒng)創(chuàng)建的@autoreleasepool也有開(kāi)發(fā)者自行創(chuàng)建的@autoreleasepool腌乡。那么他的釋放順序是怎樣的呢?
因?yàn)锧autoreleasepool是以棧的形式存儲(chǔ)的夜牡,按照先進(jìn)后出的規(guī)則釋放棧中每個(gè)@autoreleasepool与纽。主線程的@autoreleasepool是在Runloop一開(kāi)始就創(chuàng)建了所以侣签,它必然是棧最里面的,而自行創(chuàng)建的@autoreleasepool是在Runloop運(yùn)行中創(chuàng)建的急迂,所以在棧上面一點(diǎn)影所。按照棧的規(guī)則,@autoreleasepool是先釋放自行創(chuàng)建的@autoreleasepool僚碎,在釋放系統(tǒng)創(chuàng)建的猴娩。
NSString 單獨(dú)說(shuō)
為什么要單獨(dú)說(shuō)NSString呢,因?yàn)镹SString在內(nèi)存上與其他類(lèi)型存在很大的不同
//該代碼是在MRC環(huán)境下測(cè)試用
NSString *str1 = @"123456789";//用@""方法創(chuàng)建一個(gè) 固定長(zhǎng)度為9的字符串
NSString *str2 = @"1234567890";//用@""方法創(chuàng)建一個(gè) 固定長(zhǎng)度為10的字符串
NSString *str3 = [NSString stringWithFormat:@"234567890"];//用stringWithFormat方法創(chuàng)建一個(gè) 固定長(zhǎng)度為9的字符串
NSString *str4 = [NSString stringWithFormat:@"2345678901"];//用stringWithFormat方法創(chuàng)建一個(gè) 固定長(zhǎng)度為10的字符串
NSString *str5 = [[NSString alloc] initWithString:@"345678901"];//用initWithString方法創(chuàng)建一個(gè) 固定長(zhǎng)度為9的字符串
NSString *str6 = [[NSString alloc] initWithString:@"3456789012"];//用initWithString方法創(chuàng)建一個(gè) 固定長(zhǎng)度為9的字符串
NSString *str7 = [[NSString alloc] initWithFormat:@"456789012"];//用initWithFormat方法創(chuàng)建一個(gè) 固定長(zhǎng)度為9的字符串
NSString *str8 = [[NSString alloc] initWithFormat:@"4567890123"];//用initWithFormat方法創(chuàng)建一個(gè) 固定長(zhǎng)度為9的字符串
NSString *str9 = [NSString stringWithFormat:@"1234567890"];//用stringWithFormat方法創(chuàng)建一個(gè) 固定長(zhǎng)度為10的字符串并與str2字符串一樣的字符串
NSString *str10 = [[NSString alloc] initWithString:@"1234567890"];//用initWithString方法創(chuàng)建一個(gè) 固定長(zhǎng)度為10的字符串并與str2字符串一樣的字符串
NSLog(@"str1 用@"" 的retainCount為:%ld \n 對(duì)象內(nèi)地地址:%p",[str1 retainCount],str1);
NSLog(@"str2 用@"" 的retainCount為:%ld \n 對(duì)象內(nèi)地地址:%p",[str2 retainCount],str2);
NSLog(@"-----------------------------------------------------------------");
NSLog(@"str3 用stringWithFormat 的retainCount為:%ld \n 對(duì)象內(nèi)地地址:%p",[str3 retainCount],str3);
NSLog(@"str4 用stringWithFormat 的retainCount為:%ld \n 對(duì)象內(nèi)地地址:%p",[str4 retainCount],str4);
NSLog(@"-----------------------------------------------------------------");
NSLog(@"str5 用initWithString 的retainCount為:%ld \n 對(duì)象內(nèi)地地址:%p",[str5 retainCount],str5);
NSLog(@"str6 用initWithString 的retainCount為:%ld \n 對(duì)象內(nèi)地地址:%p",[str6 retainCount],str6);
NSLog(@"-----------------------------------------------------------------");
NSLog(@"str7 用initWithFormat 的retainCount為:%ld \n 對(duì)象內(nèi)地地址:%p",[str7 retainCount],str7);
NSLog(@"str8 用initWithFormat 的retainCount為:%ld \n 對(duì)象內(nèi)地地址:%p",[str8 retainCount],str8);
NSLog(@"-----------------------------------------------------------------");
NSLog(@"使用lu看一下 str1 的retainCount為:%lu \n 對(duì)象內(nèi)地地址:%p",[str1 retainCount],str1);
NSLog(@"使用lu看一下 str4 的retainCount為:%lu \n 對(duì)象內(nèi)地地址:%p",[str4 retainCount],str4);
NSLog(@"-----------------------------------------------------------------");
NSLog(@"str9 字符串與str2一樣 的retainCount為:%lu \n 對(duì)象內(nèi)地地址:%p",[str9 retainCount],str9);
NSLog(@"str10 字符串與str2一樣 的retainCount為:%lu \n 對(duì)象內(nèi)地地址:%p",[str10 retainCount],str10);
輸出結(jié)果
1.為什么用 @""
, stringWithFormat
, initWithString
, initWithFormat
四種方式勺阐。
因?yàn)榉绞讲煌碇校瑒?chuàng)建的對(duì)象所存在內(nèi)存區(qū)域不同。你會(huì)發(fā)現(xiàn)str2 與 str9 字符串內(nèi)容一樣渊抽。為什么對(duì)象地址一個(gè)是0x1053674f8
另一個(gè)是0x604000033580
蟆豫。正常情況下,字符串內(nèi)容一樣懒闷,應(yīng)該取的是同一個(gè)內(nèi)存地址十减。就像str2與str10一樣。雖然創(chuàng)建的方法不一樣愤估,但是字符串一樣帮辟,內(nèi)存地址就是一樣的。 這就是區(qū)別玩焰。 @“”與initWithString方法創(chuàng)建的字符串于stringWithFormat由驹、initWithFormat創(chuàng)建的字符串所在的內(nèi)存區(qū)域不同。
2.為什么要區(qū)分長(zhǎng)度9 和 長(zhǎng)度10的字符串震捣?
因?yàn)樽址L(zhǎng)度不一樣荔棉,字符串所在內(nèi)存區(qū)域不同,retainCount也不同蒿赢。從上面結(jié)果中可以看出润樱,str3和str4是同一種方式創(chuàng)建的字符串,但一個(gè)內(nèi)存是0xa287dcaecc2ac5d9
一個(gè)是0x6040000339a0
羡棵。因?yàn)榍罢呤窃谖宕髤^(qū)域之外的內(nèi)存區(qū)壹若,而后者在堆中。
由上面連2點(diǎn)結(jié)合可知皂冰,由initWithString和stringWithString創(chuàng)建的NSString對(duì)象店展,不管字符串的內(nèi)容和長(zhǎng)度怎么變化,該字符串對(duì)象始終是存儲(chǔ)在常量區(qū)的秃流,引用計(jì)數(shù)為-1赂蕴;從用%lu打印來(lái)看initWithString和stringWithString創(chuàng)建的字符串retainCount是無(wú)符號(hào)長(zhǎng)整型的最大值。所以可以說(shuō)他們沒(méi)有引用計(jì)數(shù)這個(gè)概念
而由initWithFormat和stringWithFormat創(chuàng)建的對(duì)象舶胀,如果字符串內(nèi)容是非漢字的概说,那么當(dāng)字符串長(zhǎng)度小于10個(gè)時(shí)碧注,該字符串存儲(chǔ)區(qū)域在五大區(qū)域之外,且隨著字符串長(zhǎng)度的變化糖赔,存儲(chǔ)地址會(huì)有很大變化萍丐。當(dāng)字符串長(zhǎng)度超過(guò)10個(gè)以后,該字符串在堆中放典,與正常的OC對(duì)象一樣逝变。這里為什么要說(shuō)非漢字呢,因?yàn)槿绻址畠?nèi)容是漢字奋构,不管字符串的內(nèi)容和長(zhǎng)度怎么變化壳影,該字符串都是在堆中,與正常OC對(duì)象一樣声怔。
App占用手機(jī)內(nèi)存最大限制記錄
型號(hào) | 最大限制 |
---|---|
iPad1 | 127MB/256MB/49% |
iPad2 | 275MB/512MB/53% |
iPad3 | 645MB/1024MB/62% |
iPad4 | 585MB/1024MB/57% (iOS 8.1) |
iPad Mini 1st Generation | 297MB/512MB/58% |
iPad Mini retina | 696MB/1024MB/68% (iOS 7.1) |
iPad Air | 697MB/1024MB/68% |
iPad Air 2 | 1383MB/2048MB/68% (iOS 10.2.1) |
iPad Pro 9.7" | 1395MB/1971MB/71% (iOS 10.0.2 (14A456)) |
iPad Pro 10.5” | 3057/4000/76% (iOS 11 beta4) |
iPad Pro 12.9” (2015) | 3058/3999/76% (iOS 11.2.1) |
iPad Pro 12.9” (2017) | 3057/3974/77% (iOS 11 beta4) |
iPad Pro 11.0” (2018) | 2858/3769/76% (iOS 12.1) |
iPad Pro 12.9” (2018) | 4598/5650/81% (iOS 12.1) |
iPod touch 4th gen | 130MB/256MB/51% (iOS 6.1.1) |
iPod touch 5th gen | 286MB/512MB/56% (iOS 7.0) |
iPhone4 | 325MB/512MB/63% |
iPhone4s | 286MB/512MB/56% |
iPhone5 | 645MB/1024MB/62% |
iPhone5s | 646MB/1024MB/63% |
iPhone6 | 645MB/1024MB/62% (iOS 8.x) |
iPhone6+ | 645MB/1024MB/62% (iOS 8.x) |
iPhone6s | 1396MB/2048MB/68% (iOS 9.2) |
iPhone6s+ | 1392MB/2048MB/68% (iOS 10.2.1) |
iPhoneSE | 1395MB/2048MB/69% (iOS 9.3) |
iPhone7 | 1395/2048MB/68% (iOS 10.2) |
iPhone7+ | 2040MB/3072MB/66% (iOS 10.2.1) |
iPhone X | 1392/2785/50% (iOS 11.2.1) |
iPhone XS Max | 2039/3735/55% (iOS 12.1) |