1. 程式人生 > >iOS-MRC與ARC區別以及五大記憶體區

iOS-MRC與ARC區別以及五大記憶體區

個人覺得要更加深入直觀瞭解MRC與ARC的區別建議先從記憶體分析開始所以文章開始會從記憶體起
文章目錄

1.五大記憶體區域

1.1 棧區

1.2 堆區

1.3 全域性區

1.4 常量區

1.5 程式碼區

1.6 自由儲存區

1.7 static靜態變數

1.8 extern全域性變數

1.9 const常量

2.屬性識別符號

2.1 @property@synthesize@dynamic

2.2 nonatomic與atomic

2.3 strongweak、retain、assgin、copy、unsafe_unretained

2.4 readOnly、readWrite、getter=、setter=

2.5
__unsafe_unretained、__weak、__strong 3.MRC與ARC區別 3.1 MRC手動記憶體管理 3.2 ARC自動記憶體管理 3.3 autoreleasepool自動釋放池 4.NSString單獨說

1.五大記憶體區域

棧區,堆區,全域性區,常量區,程式碼區

五大記憶體區域之外還有 自由儲存區也稱之五大區域之外區

1.1棧區:
建立臨時變數時由編譯器自動分配,在不需要的時候自動清除的變數的儲存區。

裡面的變數通常是區域性變數、函式引數等。在一個程序中,位於使用者虛擬地址空間頂部的是使用者棧,編譯器用它來實現函式的呼叫。和堆一樣,使用者棧在程式執行期間可以動態地擴充套件和收縮。

@interface TestObject()

@end
@implementation TestObject
- (void)testMethodWithName:(NSString *)name
{
   //方法引數name是一個指標,指向傳入的引數指標所指向的物件記憶體地址。name是在棧中
  //通過列印地址可以看出來,傳入引數的物件記憶體地址與方法引數的物件記憶體地址是一樣的。但是指標地址不一樣。
  NSLog(@"name指標地址:%p,name指標指向的物件記憶體地址:%p",&name,name);


  //*person 是指標變數,在棧中, [Person new]是建立的物件,放在堆中。
//person指標指向了[Person new]所建立的物件。 //那麼[Person new]所建立的物件的引用計數器就被+1了,此時[Person new]物件的retainCount為1 Person *person = [Person new]; }

1.2堆區:

就是那些由 new alloc 建立的物件所分配的記憶體塊,它們的釋放系統不會主動去管,由我們的開發者去告訴系統什麼時候釋放這塊記憶體(一個物件引用計數為0是系統就會回銷毀該記憶體區域物件)。一般一個 new 就要對應一個 release。在ARC下編譯器會自動在合適位置為OC物件新增release操作。會在當前執行緒Runloop退出或休眠時銷燬這些物件,MRC則需程式設計師手動釋放。

堆可以動態地擴充套件和收縮。

//alloc是為Person物件分配記憶體,init是初始化Person物件。本質上跟[Person new]一樣。
Person *person = [[Person alloc] init];

1.3全域性/靜態儲存區,

全域性變數和靜態變數被分配到同一塊記憶體中,在以前的 C 語言中,全域性變數又分為初始化的和未初始化的(初始化的全域性變數和靜態變數在一塊區域,

未初始化的全域性變數與靜態變數在相鄰的另一塊區域,

同時未被初始化的物件儲存區可以通過 void* 來訪問和操縱,

程式結束後由系統自行釋放),在 C++ 裡面沒有這個區分了,

他們共同佔用同一塊記憶體區。

1.4常量儲存區

這是一塊比較特殊的儲存區,他們裡面存放的是常量,不允許修改。一般值都是放在這個地方的

1.5程式碼區

存放函式的二進位制程式碼

1.6自由儲存區,

就是那些由 malloc 等分配的記憶體塊,他和堆是十分相似的,不過它是用 free 來結束自己的生命的。

NSString *string1;//string1 這個NSString 型別的指標,未初始化存在於<全域性區>的

NSString *string2 = @"1234";//string2 這個NSString型別的指標,已初始化存在與<全域性區>的,@“1234”存在與堆區,因為@代表了物件。 

static NSString *string3;//string3 這個NSString 型別的指標存在於<全域性區>的

static NSString *string4 = @"1234";//string4 這個NSString型別的指標存在與<全域性區>的,@“1234”存在與堆區,因為@代表了物件。stiring2和string4的值地址是一樣的

static const NSString *string5 = @"654321";//const 修飾後  string5不能修改值。 其他的與string4一樣

- (void)test
{
int  a;//a這個int型別的變數 是存在與<棧區>的
a = 10;//10這個值是存在與 <常量區>的

NSStirng *str//str這個NSString型別的指標 存在於<棧區>
str = @“1234”;//@“1234”這個@物件存在於 <堆區>

static NSString *str1;//str1這個NSString型別的指標 存在於<全域性區>的
static NSString *str2 = @"4321';//str2這個NSString型別的指標 存在於<全域性區>的

NSString *str3;//str3這個NSString型別的指標 存在於<棧區>
str3 = [[NSString alloc]initWithString:@"1234"];//[[NSString alloc]initWithString:@"1234"]這個物件 存在於<堆區>

}

1.7 static靜態變數

靜態變數有兩種

全域性靜態變數
優點:不管物件方法還是類方法都可以訪問和修改全域性靜態變數,並且外部類無法呼叫靜態變數,定義後只會指向固定的指標地址,供所有物件使用,節省空間。

缺點:存在的生命週期長,從定義直到程式結束。

建議:從記憶體優化和程式編譯的角度來說,儘量少用全域性靜態變數,因為存在的宣告週期長,一直佔用空間。程式執行時會單獨載入一次全域性靜態變數,過多的全域性靜態變數會造成程式啟動慢,當然就目前的手機處理器效能,幾十幾百個估計也影響不大吧。

區域性靜態變數
優點:定義後只會存在一份值,每次呼叫都是使用的同一個物件記憶體地址的值,並沒有重新建立,節省空間,只能在該區域性程式碼塊中使用。

缺點:存在的生命週期長,從定義直到程式結束,只能在該區域性程式碼塊中使用。

建議:區域性和全域性靜態變數從本根意義上沒有什麼區別,只是作用域不同而已。如果值僅一個類中的物件和類方法使用並且值可變,可以定義全域性靜態變數,如果是多個類使用並可變,建議值定義在model作為成員變數使用。如果是不可變值,建議使用巨集定義

static NSString *name;

1.8 extern全域性變數

全域性變數有兩種

對內的全域性變數:沒有用extern在.h中修飾的變數,僅定義在.m中讓該變數只能在該類使用
優點:不管物件方法還是類方法都可以訪問和修改全域性靜態變數,並且外部類無法呼叫靜態變數,定義後只會存一份值,供所有物件使用,節省空間。跟全域性靜態變數一樣,只是少了static修飾少了static特性

缺點:存在的生命週期長,從定義直到程式結束

建議:跟全域性靜態變數都一樣了,還需要用對內的全域性變數嗎?不用extern修飾就少了extern的特性,還不如用全域性靜態變數,至少能明確的知道static是對內使用的

外部全域性變數:除了該類,其他檔案也可以訪問該變數
優點:除了該類,其他檔案也可以訪問該變數

缺點:存在的生命週期長,從定義直到程式結束。並且外部可以修改其值,出現錯誤不容易定位

建議:使用全域性變數的原因就在於其對外的特性,但是其使用的方便性沒有使用model的屬性或巨集來得方便。程式啟動的時候會單獨載入全域性變數,同理與全域性靜態變數,少使用。

.m中要定義
NSString *name;

.h中同時要定義
extern NSString *name;

全域性靜態變數與全域性變數 其實本質上是沒有區別的,只是存在修飾區別,一個static讓其只能內部使用,一個extern讓其可以外部使用
const常量

不同於變數,常量的值是固定不可變的,一般用於只讀值。

優點:只可以讀取值,不能修改。一般用於介面或者文字顯示這種固定值。新增extern可以對外全域性常量,任意位置都可以訪問

缺點:存在的生命週期長,從定義直到程式結束。需要在.h .m中分別定義程式碼較多

建議:看個人習慣吧,使用巨集或者常量只是編譯載入方式不一樣而已

.h中定義extern
extern NSString *const name;
.m中定義值
NSString *const name = @"123";

2.屬性識別符號

2.1 @property、@synthesize、@dynamic

@synthesize:在物件屬性使用@synthesize宣告的時候編譯器會自動為該屬性按照固有規則生成相應的getter setter方法。如果有手動生成getter setter方法也不會報錯。

@dynamic:相反與@synthesize,使用@dynamic宣告時相當於告訴編譯器getter setter方法由使用者自己生成。如果宣告為@dynamic而沒有手動生成getter setter方法編譯的時候不報錯,但是在執行時如果使用.語法去呼叫該屬性時會崩潰。之所以在執行時才會發生崩潰是因為OC具有動態繫結特性。只有在執行時才會去確認具體的呼叫方法。

@property:相對於@dynamic 和 @synthesize ,@property宣告的作用區域在@interface內部。 它會告訴編譯器自動生成getter setter方法。也允許使用者手動生成getter setter中的一個方法,用@property宣告的屬性不能手動同時寫getter setter方法,否則編譯器會報錯。@property更好的宣告屬性變數。因為訪問方法的命名約定,可以很清晰的看出getter和setter的用處,會傳遞一些額外資訊,後面會跟相應的各種資訊例如:@property (nonatomic, strong,onlyread) NSString *name;大多數時候都用的@property宣告

@interface TestObject()
//因為使用了@property宣告,編譯器會自動生成相應getter setter方法。
//使用@property不能手動同時生成getter  setter方法,編譯器會報錯
//nonatomic表示屬性是非原子操作,strong表示強引用,
//readonly表示該屬性許可權為僅讀,那麼編譯器只會生成getter方法,不會生成setter方法
@property (nonatomic, strong,readonly) NSString *name;

@property (nonatomic, strong) NSMutableArray *array;

@property (nonatomic, strong) NSMutableDictionary *dic;

@end
@implementation TestObject

//如果上面的array使用的@property宣告,而使用者又要手動同時生成getter  setter方法
//可以使用@synthesize 告訴編譯器 該屬性getter setter方法如果沒有手動宣告就自動建立,有就不知道生成。
@synthesize array = _array;

//如果dic用@property宣告過了,會自動生成getter  setter方法。但是又不希望它自動生成getter setter方法。
//可以用@dynamic 宣告。告訴編譯器 該屬性的getter  setter方法不自動生成
@dynamic dic = _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 _array;
}

@end

2.2 nonatomic與atomic

nonatomic(非原子性):在呼叫用nonatomic宣告的物件屬性時是非執行緒安全性的。最為直觀的就是NSMutableArray的使用。當同時在子執行緒去增刪陣列元素,在主執行緒中去遍歷陣列元素就會出現陣列越界或者陣列沒有遍歷完。因為採用的nonatomic,不同操作可以同時執行,而不需要等前面的操作完成後在進行下一步操作。所以稱之為非執行緒安全。非原子性的執行效率更高不會阻塞執行緒

atomic(原子性):相反與非原子性,atomic是具有執行緒安全性的。他會在getset方法中加入執行緒操作。每當對用atomic宣告的物件屬性操作時,會根據操作加入執行緒的順序一步一步完成操作,而不是非原子性的同時操作。執行效率較低一般來說很少用atomic。

2.3 strong、weak、retain、assgin、copy、unsafe_unretained

retain:釋放舊物件,提高輸入物件的引用計數+1,將輸入物件的值賦值於舊物件,只能使用者宣告OC物件

@property (nonatomic, retain) Room *room;
- (void)setRoom:(Room *)room // room = r
{
    // 只有房間不同才需用release和retain
    if (_room != room) {  
        // 將以前的房間釋放掉 -1,將舊物件釋放
        [_room release];

        // MRC中需要手動對房間的引用計數器+1
        [room retain];

        _room = room;
    }
}

strong:強引用,它是ARC特有。在MRC時代沒有,相當於retain。由於MRC時代是靠引用計數器來管理物件什麼時候被銷燬所以用retain,而ARC時代管理物件的銷燬是有系統自動判斷,判斷的依據就是該物件是否有強引用物件。如果物件沒有被任何地方強引用就會被銷燬。所以在ARC時代基本都用的strong來宣告代替了retain。只能用於宣告OC物件(ARC特有)

assgin:簡單的賦值操作,不會更改引用計數,用於基本的資料型別宣告。

weak:弱引用,表示該屬性是一種“非擁有關係”。為這種屬性設定新值時既不會保留新值也不會釋放舊值,類似於assgin。 然而在物件被摧毀時,屬性也會被清空(nil out)。這樣可以有效的防止崩潰(因為OC中給沒有物件地址的指標傳送訊息不會崩潰,而給有記憶體地址但地址中是空物件的指標發訊息會崩潰,野指標),該宣告必須作用於OC物件。對於 weak 物件會放入一個 hash 表中。 用 weak 指向的物件記憶體地址作為 key,當此物件的引用計數為0的時候會 dealloc,假如 weak 指向的物件記憶體地址是a,那麼就會以a為鍵, 在這個 weak 表中搜索,找到所有以a為鍵的 weak 物件,從而設定為 nil。(ARC特有)

copy:不同於其他宣告,copy會根據宣告的屬性是否是可變型別而進行不同操作。如果物件是一個不可變物件,例如NSArray NSString 等,那麼copy等同於retain、strong。如果物件是一個可變物件,例如:NSMutableArray,NSMutableString等,它會在記憶體中重新開闢了一個新的記憶體空間,用來 儲存新的物件,和原來的物件是兩個不同的地址,引用計數分別為1. 這就是所謂的深拷貝淺拷貝,淺拷貝只是copy了物件的記憶體地址,而深拷貝是重新在記憶體中開闢新空間,新空間物件值與拷貝物件的值一樣。但是是完全不同的2個記憶體地址。 例如copy修飾的型別為 NSString不可變物件時,copy可以保護其封裝性,當賦值物件是一個 NSMutableString 類時(NSMutableString是 NSString 的子類,表示一種可修改其值的字串),此時若是不用copy修飾拷貝字串,那麼賦值該物件之後,賦值物件字串的值就可能會在其他地方被修改,修改後賦值後物件也會改變,造成值不對。所以,這時就要拷貝一份“不可變” (immutable)的字串,確保物件中的字串值不會無意間變動。只要實現屬性所用的物件是“可變的” (mutable),就應該在設定新屬性值時拷貝一份。

unsafe_unretained:和weak 差不多,唯一的區別便是,物件即使被銷燬,指標也不會自動置空, 物件被銷燬後指標指向的是一個無用的記憶體地址(野地址)。如果物件銷燬後後還使用此指標,程式會丟擲 BAD_ACCESS 的異常。 所以一般不使用unsafe_unretained。目前我還未在實際專案中使用過該宣告。(ARC特有)

2.4 readOnly、readWrite、getter=、setter=

readOnly表示屬性僅能讀不能設定其值。告訴編譯器只生成getter方法不生成setter方法。

readWrite預設值,表示屬性可讀可寫。編譯器會自動生成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修飾符的變數一樣,因為自己生成並持有的物件不能繼續為自己持有,所以生成的物件會立即被釋放。也就是說在執行完init方法以後,物件指標所指向的記憶體就已經釋放掉了,但因為用的__unsafe_unretained修飾指標並沒不像__weak的指標那樣,將指標自動置為nil,它依然指向原來的地址,可是這塊地址的記憶體已經被系統回收了,再訪問就是非法的,也就是野指標,再執行後面的addObject方法自然會出錯了。
__weak

主要用於解決迴圈引用,用__weak修飾的變數 當物件釋放後,指標自動設定為nil,當後面繼續使用該指標變數的時候不會造成crash,更不會造成強引用使該釋放的物件無法釋放,造成記憶體洩露。

__weak typeof(self) weakSelf = self;

__strong
相反與__weak,主要用於當使用某個物件是,希望它沒有提前被釋放。強引用該物件使其無法釋放。例如在block內部,希望block呼叫時該物件不會被提前釋放造成錯誤。可以使用強引用。

TestAlertView *alertView = [TestAlertView new];
alertView = ^()
{
  //當block內部需要使用本身這個區域性物件時,需要用強引用方式,讓alertView在傳遞完block後不會被釋放依然可以執行setTitle操作
   __strong typeof(alertView) strongAlertView = alertView;
  [strongAlertView setTitle:@"1234"];

}
[alertView show];

3 MRC與ARC區別

3.1 MRC手動記憶體管理
引用計數器:在MRC時代,系統判定一個物件是否銷燬是根據這個物件的引用計數器來判斷的。

每個物件被建立時引用計數都為1
每當物件被其他指標引用時,需要手動使用[obj retain];讓該物件引用計數+1。
當指標變數不在使用這個物件的時候,需要手動釋放release這個物件。 讓其的引用計數-1.
當一個物件的引用計數為0的時候,系統就會銷燬這個物件。
NSMutableArray *array = [NSMutableArray array];//[NSMutableArray array]建立後引用計數器為1
NSLog(@”array的物件地址:%p,array的retainCount:%zd”,array,[array retainCount]);
[array release];//呼叫release後[NSMutableArray array]建立的物件引用計數-1.

    //當程式執行到[array addObject:@"1234"];這裡是就會崩潰。因為此時array指標指向的記憶體地址中沒有任何物件,該指標是一個野指標。
     //因為release後[NSMutableArray array]建立的物件引用計數變為了0.系統就會銷燬這個記憶體地址的物件。
    [array addObject:@"1234"];
    NSLog(@"array的物件地址:%p,array的retainCount:%zd",array,[array retainCount]);
    NSLog(@"%@",array);

在MRC模式下必須遵循誰建立,誰釋放,誰引用,誰管理
在MRC下使用ARC

在Build Phases的Compile Sources中選擇需要使用MRC方式的.m檔案,然後雙擊該檔案在彈出的會話框中輸入 -fobjc-arc
3.2 ARC自動記憶體管理

WWDC2011和iOS5所引入自動管理機制——自動引用計數(ARC),它不是垃圾回收機制而是編譯器的一種特性。ARC管理機制與MRC手動機制差不多,只是不再需要手動呼叫retain、release、autorelease;當你使用ARC時,編譯器會在在適當位置插入release和autorelease;ARC時代引入了strong強引用來帶代替retain,引入了weak弱引用。

在ARC下使用MRC方法

在ARC工程中如果要使用MRC的需要在工程的Build Phases的Compile Sources中選擇需要使用MRC方式的.m檔案,然後雙擊該檔案在彈出的會話框中輸入 -fno-objc-arc
這裡寫圖片描述
在非MRC檔案中無法使用retain release retainCount 方法,無法再dealloc方法中呼叫[super dealloc];方法
3.3 autoreleasepool自動釋放池
自動釋放池始於MRC時代,主要是用於 自動 對 釋放池內 物件 進行引用計數-1的操作,即自動執行release方法。

在MRC中使用autoreleasepool必須在程式碼塊內部手動為物件呼叫autorelease把物件加入到的自動釋放池,系統會自動在程式碼塊結束後,對加入自動釋放池中的物件傳送一個release訊息。無需手動呼叫release

int main(int argc, const char * argv[])
{
Person *father =  [[Person alloc] init];//引用計數為1
    @autoreleasepool {//這裡建立自動釋放池
        int a = 10; // a在棧,10在常量區
        int b = 20; //b在棧,20在常量區
        // p : 棧
        // [[Person alloc] init]建立的物件(引用計數器==1) : 在堆
        Person *p = [[Person alloc] init];
       //MRC下需要手動讓物件加入自動釋放池
       [p autorelease];

        Person *pFather = father;
        [father retain];//father指標指向的物件被pFather引用,在MRC下需要手動讓被引用物件引用計數+1

        NSLog(@"pFather物件記憶體地址:%p,pFather的引用計數:%zd",pFather,[pFather retainCount]);
        NSLog(@"father物件記憶體地址:%p,father的引用計數:%zd",father,[father retainCount]);

    }//這裡釋放 自動釋放池
    // 當autoreleasepool內部程式碼塊執行完畢後上面程式碼塊後, 棧裡面的變數a、b、p 都會被回收
    // 但是堆裡面的Person物件還會留在記憶體中,因為它是計數器依然是1。當autoreleasepool程式碼塊執行完畢後,會對釋放池內部的所有物件執行一個release訊息。如果傳送release訊息後,物件引用計數為0了,那麼就會被系統回收。


NSLog(@"father物件記憶體地址:%p,father的引用計數:%zd",father,[father retainCount]);
    return 0;
}

在ARC中對@autoreleasepool的使用相比MRC不太多。主要用於一些大記憶體消耗物件的重複建立時,保證記憶體處於比較優越的狀態。常用於建立物件較多的for迴圈中。在ARC下不要手動的為@autoreleasepool程式碼塊內部物件新增autorelease,ARC下自動的把@autoreleasepool程式碼塊中建立的物件加入了自動釋放池中。

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時
這裡寫圖片描述
可以從上圖看出,使用autoreleasepool時,記憶體一直保持穩定狀態,上次幾乎沒浮動

不使用autoreleasepool
這裡寫圖片描述
因為沒有使用 autoreleasepool,隨著for迴圈一直執行下去,記憶體一直在上升。

autorelease物件釋放的時機

1.系統自行建立的@autoreleasepool釋放時機

執行緒與Runloop是一對一關係,主執行緒中會自動建立Runloop,而子執行緒需要自行呼叫Runloop來讓子執行緒自動建立Runloop。當@autoreleasepool加入到某個執行緒時,該執行緒的Runloop會

借用runloop的Autorelease物件釋放的背後的解釋(ARC環境下)
這裡寫圖片描述
圖中第1步 Observer 監視的事件是 Entry(即將進入Loop),其回撥內會呼叫 _objc_autoreleasePoolPush() 建立自動釋放池。其 order 是-2147483647,優先順序最高,保證建立釋放池發生在其他所有回撥之前

圖中第6步 Observer 監視了兩個事件: BeforeWaiting(準備進入休眠) 時呼叫_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 釋放舊的池並建立新池;Exit(即將退出Loop) 時呼叫 _objc_autoreleasePoolPop() 來釋放自動釋放池。這個 Observer 的 order 是 2147483647,優先順序最低,保證其釋放池子發生在其他所有回撥之後。

圖中第10 Observer 監視事件是exit(即講退出runloop),其回撥內會呼叫 _objc_autoreleasePoolpop() 釋放自動釋放池。

從上面就能看出,Runloop中系統自動建立的@autoreleasepool是在準備進入休眠狀態才被銷燬的。所以在ARC下,線上程中的臨時物件是在當前執行緒的Runloop進入休眠或者退出loop或者退出執行緒時被執行release的。

2.自己建立的@autoreleasepool

@autoreleasepool
{//這個{開始建立的自動釋放池,這裡開始內部的物件自動加入autorelease

}//這個}開始,不必再對 物件加入autorelease,自動釋放池被銷燬。

從上面就能看出,自行建立的@autoreleasepool,是在}後被釋放的,而其中的autorelease物件,也是在這個時候自動執行的release操作。

從 1, 2 兩點可以看出,雖然autorelease物件釋放的時機並不都是在程式碼塊結束後就釋放。但是他們有一個共同特性,那就是必定是在@autoreleasepool被銷燬時,釋放的。 所以要清楚autorelease物件什麼時候被釋放,只需要搞清楚@autoreleasepool什麼時候被銷燬即可

主執行緒中既有系統建立的@autoreleasepool也有開發者自行建立的@autoreleasepool。那麼他的釋放順序是怎樣的呢?

因為@autoreleasepool是以棧的形式儲存的,按照先進後出的規則釋放棧中每個@autoreleasepool。主執行緒的@autoreleasepool是在Runloop一開始就建立了所以,它必然是棧最裡面的,而自行建立的@autoreleasepool是在Runloop執行中建立的,所以在棧上面一點。按照棧的規則,@autoreleasepool是先釋放自行建立的@autoreleasepool,在釋放系統建立的。

NSString 單獨說

為什麼要單獨說NSString呢,因為NSString在記憶體上與其他型別存在很大的不同

  NSString *str1 = @"123456789";//用@""方法建立一個  固定長度為9的字串
    NSString *str2 = @"1234567890";//用@""方法建立一個  固定長度為10的字串


    NSString *str3 = [NSString stringWithFormat:@"234567890"];//用stringWithFormat方法建立一個  固定長度為9的字串
    NSString *str4 = [NSString stringWithFormat:@"2345678901"];//用stringWithFormat方法建立一個  固定長度為10的字串

    NSString *str5 = [[NSString alloc] initWithString:@"345678901"];//用initWithString方法建立一個  固定長度為9的字串
    NSString *str6 = [[NSString alloc] initWithString:@"3456789012"];//用initWithString方法建立一個  固定長度為9的字串

    NSString *str7 = [[NSString alloc] initWithFormat:@"456789012"];//用initWithFormat方法建立一個  固定長度為9的字串
    NSString *str8 = [[NSString alloc] initWithFormat:@"4567890123"];//用initWithFormat方法建立一個  固定長度為9的字串

    NSString *str9 = [NSString stringWithFormat:@"1234567890"];//用stringWithFormat方法建立一個  固定長度為10的字串並與str2字串一樣的字串
    NSString *str10 = [[NSString alloc] initWithString:@"1234567890"];//用initWithString方法建立一個  固定長度為10的字串並與str2字串一樣的字串

    NSLog(@"str1 用@"" 的retainCount為:%ld \n  物件內地地址:%p",[str1 retainCount],str1);
    NSLog(@"str2 用@"" 的retainCount為:%ld \n  物件內地地址:%p",[str2 retainCount],str2);
    NSLog(@"-----------------------------------------------------------------");
    NSLog(@"str3 用stringWithFormat 的retainCount為:%ld \n  物件內地地址:%p",[str3 retainCount],str3);
    NSLog(@"str4 用stringWithFormat 的retainCount為:%ld \n  物件內地地址:%p",[str4 retainCount],str4);
    NSLog(@"-----------------------------------------------------------------");
    NSLog(@"str5 用initWithString 的retainCount為:%ld \n  物件內地地址:%p",[str5 retainCount],str5);
    NSLog(@"str6 用initWithString 的retainCount為:%ld \n  物件內地地址:%p",[str6 retainCount],str6);
    NSLog(@"-----------------------------------------------------------------");
    NSLog(@"str7 用initWithFormat 的retainCount為:%ld \n  物件內地地址:%p",[str7 retainCount],str7);
    NSLog(@"str8 用initWithFormat 的retainCount為:%ld \n  物件內地地址:%p",[str8 retainCount],str8);
    NSLog(@"-----------------------------------------------------------------");
    NSLog(@"使用lu看一下 str1 的retainCount為:%lu \n  物件內地地址:%p",[str1 retainCount],str1);
    NSLog(@"使用lu看一下 str4 的retainCount為:%lu \n  物件內地地址:%p",[str4 retainCount],str4);
    NSLog(@"-----------------------------------------------------------------");
    NSLog(@"str9  字串與str2一樣 的retainCount為:%lu \n  物件內地地址:%p",[str9 retainCount],str9);
    NSLog(@"str10  字串與str2一樣 的retainCount為:%lu \n  物件內地地址:%p",[str10 retainCount],str10);

輸出結果
這裡寫圖片描述
1.為什麼用 @”“, stringWithFormat, initWithString, initWithFormat四種方式。

因為方式不同,建立的物件所存在記憶體區域不同。你會發現str2 與 str9 字串內容一樣。為什麼物件地址一個是0x1053674f8 另一個是0x604000033580。正常情況下,字串內容一樣,應該取的是同一個記憶體地址。就像str2與str10一樣。雖然建立的方法不一樣,但是字串一樣,記憶體地址就是一樣的。 這就是區別。 @“”與initWithString方法建立的字串於stringWithFormat、initWithFormat建立的字串所在的記憶體區域不同。

2.為什麼要區分長度9 和 長度10的字串?

因為字串長度不一樣,字串所在記憶體區域不同,retainCount也不同。從上面結果中可以看出,str3和str4是同一種方式建立的字串,但一個記憶體是0xa287dcaecc2ac5d9一個是0x6040000339a0。因為前者是在五大區域之外的記憶體區,而後者在堆中。

由上面連2點結合可知,由initWithString和stringWithString建立的NSString物件,不管字串的內容和長度怎麼變化,該字串物件始終是儲存在常量區的,引用計數為-1;從用%lu列印來看initWithString和stringWithString建立的字串retainCount是無符號長整型的最大值。所以可以說他們沒有引用計數這個概念

而由initWithFormat和stringWithFormat建立的物件,如果字串內容是非漢字的,那麼當字串長度小於10個時,該字串儲存區域在五大區域之外,且隨著字串長度的變化,儲存地址會有很大變化。當字串長度超過10個以後,該字串在堆中,與正常的OC物件一樣。這裡為什麼要說非漢字呢,因為如果字串內容是漢字,不管字串的內容和長度怎麼變化,該字串都是在堆中,與正常OC物件一樣。

作者:樹下敲程式碼的超人

連結:https://www.jianshu.com/p/5eac83471b23

相關推薦

iOS-MRCARC區別以及五大記憶體

個人覺得要更加深入直觀瞭解MRC與ARC的區別建議先從記憶體分析開始所以文章開始會從記憶體起 文章目錄 1.五大記憶體區域 1.1 棧區 1.2 堆區 1.3 全域性區 1.4 常量區 1.5 程式碼區 1.6 自由儲存區 1.7 stat

Java基本型別引用型別 以及 Java記憶體知識整理

Java基本型別和引用型別是什麼?每種基本型別都佔多少位多少位元組?詳見下表 基本型別 整型 byte 1位元組 -27~27-1   -128~127 short 2位元組 -215~215-1  -32,768~32,767 (3萬多)

actionservlet區別以及action瞭解

servlet生命週期為init    service    destroy,servlet是單例模式,注意執行緒安全問題,屬性資料(成員變數)是全域性變數,web.xml配置繁瑣,servlet的轉向方法 if(method.equals("aa")){ 轉向頁面;

藍芽、紅外線wifi 區別以及不同頻段無線電磁波的穿牆和繞過障礙物能力(轉)

電磁波按波長由大到小的順序為:無線電波、紅外線、可見光、紫外線、X射線、γ射線 以下是幾種常見的電磁波交流電:波長可達數千公里 (如果需要,還可以製造出波長更長的.總之理論上 無上限)  無線電波:長波(波長在幾公里至幾十公里);中波(波長約在3公里至約50米);短波(波

jquery .attr("value") .val() 區別 以及siblings()用法

.val() 能夠取到 針對text,hidden可輸入的文字框的value值。 而 .attr('value') 可以取到html元素中所設定的屬性 value的值,不能獲取動態的如input type="text" 的文字框手動輸入的值。 siblings()用法

909422229__MysqlOracle區別以及悲觀鎖樂觀鎖機制

1.Mysql與Oracle的區別: 事務: oracle很早就完全支援事務。 mysql在innodb儲存引擎的行級鎖的情況下才支援事務,在安裝Mysql的時候可以選擇是否支援事務,可支援,可不支

【C】C99C89區別以及轉換方法

DATE: 2018.11.14 1、 C99與C89區別: 可變長陣列   C99中,程式設計師宣告陣列時,陣列的維數可以由任一有效的整型表示式確定,包括只在執行時才能確定其值的表示式,這類陣列就叫做可變長陣列,但是隻有區域性陣列才可以是變長的. 可變長陣

iOS framebounds區別詳解

轉自 http://blog.csdn.net/chenyufeng1991/article/details/51764303 在iOS的UI開發中,frame和bounds是兩個非常容易搞混的概念,而很多開發者在實際專案中也很少去區分,因此會導致出現一些意想不

區別,以及runat="server"的作用

<input type="button">與<asp:button>的區別,以及runat="server"的作用 在<input type="button">中只能編寫點選事件onclick,並且只能在js中實現,那麼如何讓<in

MyBatis面試題,'#{}${}的區別'以及'sql預編譯'

這個問題不算複雜,網上答案也比較”豐富”, 之所以寫這篇博文主要是以後查閱方便,自己總結也能加深印象, 畢竟它是面試題中的老相識,以後還要麻煩它關照關照… MyBatis本身是基於JDBC封

執行緒程序區別以及執行緒作用

1 程序與執行緒 (1)程序:正在進行中的程式。 (2)執行緒:就是程序中一個執行單元或執行情景或執行路徑,負責程序中程式執行的控制單元。   一個程序中至少要有一個執行緒。 當一個程序

關於java成員變數區域性變數以及成員方法的記憶體區別問題

今天去面試遇到了有關成員變數與區域性變數以及成員方法的問題,主要是涉及所處記憶體的問題和初始化問題,簡單的總結一下: 1.java中成員變數處在堆記憶體(成員方法不呼叫時存在方法區中的靜態區)中,區域性變數處在棧記憶體中; 知其然知其所以然,我們簡單瞭解一下Java記憶體分配:

五大記憶體分割槽,堆棧的區別

五大記憶體分割槽    在C++中,記憶體分成5個區,他們分別是堆、棧、自由儲存區、全域性/靜態儲存區和常量儲存區。    棧,就是那些由編譯器在需要的時候分配,在不需要的時候自動清楚的變數的儲存區。裡面的變數通常是區域性變數、函式引數等。    堆,就是那些由new分配的記

ios開發中類方法例項方法區別 以及self有什麼不同

Objective-C裡面既有例項方法也類方法。類方法(Class Method) 有時被稱為工廠方法(Factory Method)或者方便方法(Convenience method)。工廠方法的稱謂明顯和一般意義上的工廠方法不同,從本質上來說,類方法可以獨立於物件而執行,所以在其他的語言裡面類方法有的

記憶體管理中自動釋放池ARC區別

手動計數就不用提了,但自動釋放池與arc分不清,一開始聽MJ講課時,總是自己release,當我release物件時,就會出現arc禁止,當時也不知道怎麼回事,後來知道了arc是自動引用計數,但是還是不懂與自動釋放池的區別。 自動釋放池是NSAutorelea

mybatis.xml文件中#$符號的區別以及數學符號的處理

文件 integer order by rep select map 以及 方式 動態 1. #{}表示一個占位符號,通過#{}可以實現preparedStatement向占位符中設置值,自動進行java類型和jdbc類型轉換,#{}可以有效防止sql註入。 #{}可以接收

php session機制cookie機制以及聯系區別

標識 級別 聯系 是什麽 生命周期 技術 路徑 多次 瀏覽器中 session與cookie是在做項目中很常用的會話技術,session與cookie也是面試中被問到頻率最高的問題,有一次我去面試,面試官就懟著我session與cookie一直問(頭都大了),下面總結了一些

mybatis深入理解之 # $ 區別以及 sql 預編譯

tcl nec from esql 校驗 ntp code 理解 替換字符串 mybatis 中使用 sqlMap 進行 sql 查詢時,經常需要動態傳遞參數,例如我們需要根據用戶的姓名來篩選用戶時,sql 如下: select * from user where nam

iOS庫 .a.framework區別

集中 lib -o 我們 一個 cat 不同的 work undle http://blog.csdn.net/lvxiangan/article/details/43115131 一、什麽是庫? 庫是共享程序代碼的方式,一般分為靜態庫和動態庫。 二、靜態庫與動態庫的區別?

margin和padding的用法區別--以及bug處理方式

使用 滿足 左右 ron 相互 一段 布局 方式 ont margin和padding的用法: (1)padding (margin) -left:10px;          左內 (外) 邊距(2)padding (margin) -right:10px;