iOS-MRC與ARC區(qū)別以及五大內(nèi)存區(qū)

技 術(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í)

可以從上圖看出线婚,使用autoreleasepool時(shí)遏弱,內(nèi)存一直保持穩(wěn)定狀態(tài),上次幾乎沒(méi)浮動(dòng)

不使用autoreleasepool

因?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)境下)

Runloop

圖中第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é)果


結(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)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末态贤,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子醋火,更是在濱河造成了極大的恐慌悠汽,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,324評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件芥驳,死亡現(xiàn)場(chǎng)離奇詭異柿冲,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)兆旬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)假抄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人丽猬,你說(shuō)我怎么就攤上這事宿饱。” “怎么了脚祟?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,328評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵谬以,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我由桌,道長(zhǎng)为黎,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,147評(píng)論 1 292
  • 正文 為了忘掉前任行您,我火速辦了婚禮铭乾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘娃循。我一直安慰自己炕檩,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布捌斧。 她就那樣靜靜地躺著笛质,像睡著了一般吹泡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上经瓷,一...
    開(kāi)封第一講書(shū)人閱讀 51,115評(píng)論 1 296
  • 那天,我揣著相機(jī)與錄音洞难,去河邊找鬼舆吮。 笑死,一個(gè)胖子當(dāng)著我的面吹牛队贱,可吹牛的內(nèi)容都是我干的色冀。 我是一名探鬼主播,決...
    沈念sama閱讀 40,025評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼柱嫌,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼锋恬!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起编丘,我...
    開(kāi)封第一講書(shū)人閱讀 38,867評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤与学,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后嘉抓,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體索守,經(jīng)...
    沈念sama閱讀 45,307評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評(píng)論 2 332
  • 正文 我和宋清朗相戀三年抑片,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了卵佛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,688評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡敞斋,死狀恐怖截汪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情植捎,我是刑警寧澤衙解,帶...
    沈念sama閱讀 35,409評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站鸥跟,受9級(jí)特大地震影響丢郊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜医咨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評(píng)論 3 325
  • 文/蒙蒙 一枫匾、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧拟淮,春花似錦干茉、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,657評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)沾谓。三九已至,卻和暖如春戳鹅,著一層夾襖步出監(jiān)牢的瞬間均驶,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,811評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工枫虏, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留妇穴,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,685評(píng)論 2 368
  • 正文 我出身青樓隶债,卻偏偏與公主長(zhǎng)得像腾它,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子死讹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評(píng)論 2 353