亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

iOS開發(fā)之MRC(手動內存管理)詳解

 更新時間:2022年08月18日 09:01:04   作者:任淏  
這篇文章主要介紹了?iOS開發(fā)之MRC(手動內存管理)詳解的相關資料,需要的朋友可以參考下

前言:

在iOS中,使用引用計數來管理OC對象內存

一個新創(chuàng)建的OC對象引用計數默認是1,當引用計數減為0,OC對象就會銷毀,釋放其占用的內存空間。

調用retain會讓OC對象的引用計數+1,調用release會讓OC對象的引用計數-1。

內存管理的經驗總結

當調用alloc、new、copy、mutableCopy方法返回了一個對象,在不需要這個對象時,要調用release或者autorelease釋放它。
想擁有某個對象,就讓他的引用計數+1;不想再擁有某個對象,就讓他的引用計數-1。

一、 MRC 手動管理內存(Manual Reference Counting)

1、引用計數器

引用計數器:

一個整數,表示為「對象被引用的次數」。系統(tǒng)需要根據對象的引用計數器來判斷對象是否需要被回收。

關于「引用計數器」,有以下幾個特點:

  • 每個 OC 對象都有自己的引用計數器。
  • 任何一個對象,剛創(chuàng)建的時候,初始的引用計數為 1。
  • 即使用 alloc、new 或者 copy 創(chuàng)建一個對象時,對象的引用計數器默認就是 1。
  • 當沒有任何人使用這個對象時,系統(tǒng)才會回收這個對象。也就是說:
  • 當對象的引用計數器為 0 時,對象占用的內存就會被系統(tǒng)回收。
  • 如果對象的引用計數器不為 0 時,那么在整個程序運行過程,它占用的內存就不可能被回收(除非整個程序已經退出)。

2、引用計數器操作

  • 為保證對象的存在,每當創(chuàng)建引用到對象需要給對象發(fā)送一條 retain 消息,可以使引用計數器值 +1 ( retain 方法返回對象本身)。
  • 當不再需要對象時,通過給對象發(fā)送一條 release 消息,可以使引用計數器值 -1。
  • 給對象發(fā)送 retainCount 消息,可以獲得當前的引用計數器值。
  • 當對象的引用計數為 0 時,系統(tǒng)就知道這個對象不再需要使用了,所以可以釋放它的內存,通過給對象發(fā)送 dealloc 消息發(fā)起這個過程。
  • 需要注意的是:release 并不代表銷毀 / 回收對象,僅僅是將計數器 -1。
// 創(chuàng)建一個對象,默認引用計數器是 1
RHPerson *person1 = [[RHPerson alloc] init];
NSLog(@"retainCount = %zd", [person1 retainCount]);

// 只要給對象發(fā)送一條 retain 消息,引用計數器加1
[person1 retain];
NSLog(@"retainCount = %zd", [person1 retainCount]);

// 只要給對象發(fā)送一條 release 消息,引用計數器減1
[person1 release];
NSLog(@"retainCount = %zd", [person1 retainCount]);

// 當 retainCount 等于0時,對象被銷毀
[person1 release];

NSLog(@"--------------");
2022-07-11 16:09:24.102850+0800 Interview01-內存管理[8035:264221] retainCount = 1
2022-07-11 16:09:24.103083+0800 Interview01-內存管理[8035:264221] retainCount = 2
2022-07-11 16:09:24.103126+0800 Interview01-內存管理[8035:264221] retainCount = 1
2022-07-11 16:09:24.103231+0800 Interview01-內存管理[8035:264221] -[RHPerson dealloc]
2022-07-11 16:09:24.103259+0800 Interview01-內存管理[8035:264221] --------------
Program ended with exit code: 0

3、dealloc 方法

  • 當一個對象的引用計數器值為 0 時,這個對象即將被銷毀,其占用的內存被系統(tǒng)回收。
  • 對象即將被銷毀時系統(tǒng)會自動給對象發(fā)送一條 dealloc 消息(因此,從 dealloc 方法有沒有被調用,就可以判斷出對象是否被銷毀)
  • dealloc 方法的重寫(注意是在 MRC 中)

一般會重寫 dealloc 方法,在這里釋放相關資源,dealloc 就是對象的遺言
一旦重寫了 dealloc 方法,就必須調用 [super dealloc],并且放在最后面調用

- (void)dealloc {
? ? NSLog(@"%s", __func__);
? ? [super dealloc];
}

dealloc 使用注意:

不能直接調用 dealloc 方法。

一旦對象被回收了, 它占用的內存就不再可用,堅持使用會導致程序崩潰(野指針錯誤)。

4、野指針和空指針

只要一個對象被釋放了,我們就稱這個對象為「僵尸對象(不能再使用的對象)」。

當一個指針指向一個僵尸對象(不能再使用的對象),我們就稱這個指針為「野指針」。

只要給一個野指針發(fā)送消息就會報錯(EXC_BAD_ACCESS 錯誤)。

RHPerson *person1 = [[RHPerson alloc] init];
[person1 release];
[person1 release];
[person1 release];

為了避免給野指針發(fā)送消息會報錯,一般情況下,當一個對象被釋放后我們會將這個對象的指針設置為空指針。

空指針:

沒有指向存儲空間的指針(里面存的是 nil, 也就是 0)。

給空指針發(fā)消息是沒有任何反應的。

RHPerson *person1 = [[RHPerson alloc] init];
[person1 release];
person1 = nil;
[person1 release];

二、內存管理思想

1、單個對象內存管理思想

思想一:自己創(chuàng)建的對象,自己持有,自己負責釋放

通過 alloc、new、copy 或 mutableCopy 方法創(chuàng)建并持有對象。
當自己持有的對象不再被需要時,必須調用 release 或 autorelease 方法釋放對象。

id obj1 = [[NSObject alloc] init];
[obj1 release];

id obj2 = [NSObject new];
[obj2 release];

思想二:非自己創(chuàng)建的對象,自己也能持有

除了用上面方法(alloc / new / copy / mutableCopy 方法)所取得的的對象,因為非自己生成并持有,所以自己不是該對象的持有者。
通過調用 retain 方法,即便是非自己創(chuàng)建的對象,自己也能持有對象。
同樣當自己持有的對象不再被需要時,必須調用 release 方法來釋放對象。

id obj3 = [NSArray array];
[obj3 retain];
[obj3 release];

無論是否是自己創(chuàng)建的對象,自己都可以持有,并負責釋放。
計數器有加就有減。
曾經讓對象的計數器 +1,就必須在最后讓對象計數器 -1。

2、多個對象內存管理思想

多個對象之間往往是通過 setter 方法產生聯系的,其內存管理的方法也是在 setter 方法、dealloc 方法中實現的。所以只有了解了 setter 方法是如何實現的,我們才能了解到多個對象之間的內存管理思想。

#import <Foundation/Foundation.h>

#import "RHRoom.h"

NS_ASSUME_NONNULL_BEGIN

@interface RHPerson : NSObject

{
? ? RHRoom *_room;
}

- (void)setRoom:(RHRoom *)room;

- (RHRoom *)room;
@end

NS_ASSUME_NONNULL_END
#import "RHPerson.h"

@implementation RHPerson

- (void)setRoom:(RHRoom *)room {
? ? if (_room != room) {
? ? ? ? [_room release];
? ? ? ? _room = [room retain];
? ? }
}

- (RHRoom *)room {
? ? return _room;
}

- (void)dealloc {
? ? [_room release];
? ? [super dealloc];
? ? NSLog(@"%s", __func__);
}

@end

三、 @property 參數

在成員變量前加上 @property,系統(tǒng)就會自動幫我們生成基本的 setter / getter 方法,但是不會生成內存管理相關的代碼。
@property(nonatomic) int val;
同樣如果在 property 后邊加上 assign,系統(tǒng)也不會幫我們生成 setter 方法內存管理的代碼,僅僅只會生成普通的 getter / setter 方法,默認什么都不寫就是 assign。
@property(nonatomic, assign) int val;
如果在 property 后邊加上 retain,系統(tǒng)就會自動幫我們生成 getter / setter 方法內存管理的代碼,但是仍需要我們自己重寫 dealloc 方法。
@property(nonatomic, retain) RHRoom *room;

四、自動釋放池

1、自動釋放池

當我們不再使用一個對象的時候應該將其空間釋放,但是有時候我們不知道何時應該將其釋放。為了解決這個問題,Objective-C 提供了 autorelease 方法。

autorelease 是一種支持引用計數的內存管理方式,只要給對象發(fā)送一條 autorelease 消息,會將對象放到一個自動釋放池中,當自動釋放池被銷毀時,會對池子里面的「所有對象」做一次 release 操作。
注意:這里只是發(fā)送 release 消息,如果當時的引用計數(reference-counted)依然不為 0,則該對象依然不會被釋放。

autorelease 方法會返回對象本身,且調用完 autorelease 方法后,對象的計數器不變。

NSObject *obj = [NSObject new];
[obj autorelease];
NSLog(@"obj.retainCount = %zd", obj.retainCount);

2、使用 autorelease 有什么好處呢?

不用再關心對象釋放的時間
不用再關心什么時候調用release

3、autorelease 的原理實質上是什么?

? autorelease 實際上只是把對 release 的調用延遲了,對于每一個 autorelease,系統(tǒng)只是把該對象放入了當前的 autorelease pool 中,當該 pool 被釋放時,該 pool 中的所有對象會被調用 release 方法。

4、autorelease 的創(chuàng)建方法

// 第一種方式:使用 NSAutoreleasePool 創(chuàng)建
// 創(chuàng)建自動釋放池
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// 銷毀自動釋放池
[pool release];
// 第二種方式:使用 @autoreleasepool 創(chuàng)建
@autoreleasepool {
// 開始代表創(chuàng)建自動釋放池
// 結束代表銷毀自動釋放池
}

5、autorelease 的使用方法

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
RHPerson *p = [[[RHPerson alloc] init] autorelease];
[pool release];
@autoreleasepool {
// 開始代表創(chuàng)建自動釋放池
RHPerson *p = [[[RHPerson alloc] init] autorelease];
// 結束代表銷毀自動釋放池
}

6、autorelease 的注意事項

并不是放到自動釋放池代碼中,都會自動加入到自動釋放池

@autoreleasepool {
// 因為沒有調用 autorelease,所以沒有加入到自動釋放池中
RHPerson *p = [[RHPerson alloc] init];
// 結束代表銷毀自動釋放池
}

在自動釋放池的外部發(fā)送 autorelease 不會被加入到自動釋放池中
autorelease 是一個方法,只有在自動釋放池中調用才有效。

@autoreleasepool {
}
// 沒有與之對應的自動釋放池, 只有在自動釋放池中調用autorelease才會放到釋放池
Person *p = [[[Person alloc] init] autorelease];
[p run];
?
// 正確寫法
@autoreleasepool {
? ? Person *p = [[[Person alloc] init] autorelease];
?}
?
// 正確寫法
Person *p = [[Person alloc] init];
@autoreleasepool {
? ? [p autorelease];
}

7、自動釋放池的嵌套使用

自動釋放池是以棧的形式存在。
由于棧只有一個入口,所以調用 autorelease 會將對象放到棧頂的自動釋放池。
棧頂就是離調用 autorelease 方法最近的自動釋放池。

@autoreleasepool { // 棧底自動釋放池
? ? @autoreleasepool {
? ? ? ? @autoreleasepool { // 棧頂自動釋放池
? ? ? ? ? ? Person *p = [[[Person alloc] init] autorelease];
? ? ? ? }
? ? ? ? Person *p = [[[Person alloc] init] autorelease];
? ? }
}

自動釋放池中不適宜放占用內存比較大的對象。
盡量避免對大內存使用該方法,對于這種延遲釋放機制,還是盡量少用。
不要把大量循環(huán)操作放到同一個 @autoreleasepool 之間,這樣會造成內存峰值的上升。

// 內存暴漲
@autoreleasepool {
? ? for (int i = 0; i < 99999; ++i) {
? ? ? ? Person *p = [[[Person alloc] init] autorelease];
? ? }
}
// 內存不會暴漲
for (int i = 0; i < 99999; ++i) {
? ? @autoreleasepool {
? ? ? ? Person *p = [[[Person alloc] init] autorelease];
? ? }
}

8、autorelease 錯誤用法

不要連續(xù)調用 autorelease。

@autoreleasepool {
?// 錯誤寫法, 過度釋放
? ? Person *p = [[[[Person alloc] init] autorelease] autorelease];
?}

調用 autorelease 后又調用 release(錯誤)。

@autoreleasepool {
? ? Person *p = [[[Person alloc] init] autorelease];
? ? [p release]; // 錯誤寫法, 過度釋放
}

五、 MRC 中避免循環(huán)引用

定義兩個類 Person 類和 Dog 類

Person 類:

#import <Foundation/Foundation.h>
@class Dog;
?
@interface Person : NSObject
@property(nonatomic, retain)Dog *dog;
@end

Dog 類:

#import <Foundation/Foundation.h>
@class Person;
?
@interface Dog : NSObject
@property(nonatomic, retain)Person *owner;
@end

執(zhí)行以下代碼:

int main(int argc, const char * argv[]) {
? ? Person *p = [Person new];
? ? Dog *d = [Dog new];
?
? ? p.dog = d; // retain
? ? d.owner = p; // retain ?assign
?
? ? [p release];
? ? [d release];
?
? ? return 0;
}

就會出現 A 對象要擁有 B 對象,而 B 對應又要擁有 A 對象,此時會形成循環(huán) retain,導致 A 對象和 B 對象永遠無法釋放。

那么如何解決這個問題呢?

不要讓 A retain B,B retain A。
讓其中一方不要做 retain 操作即可。
當兩端互相引用時,應該一端用 retain,一端用 assign。

到此這篇關于 iOS開發(fā)之MRC(手動內存管理)詳解的文章就介紹到這了,更多相關MRC 手動內存管理內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 如何在iOS中高效的加載圖片詳解

    如何在iOS中高效的加載圖片詳解

    這篇文章主要給大家介紹了關于如何在iOS中高效的加載圖片的相關資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用iOS具有一定的參考學習價值,需要的朋友可以參考下
    2021-10-10
  • ios百度地圖的使用(普通定位、反地理編碼)

    ios百度地圖的使用(普通定位、反地理編碼)

    iOS定位 - 普通定位(沒有地圖) - 反地理編碼(得到具體位置)使用代碼如何實現呢,下面小編就給大家詳解ios百度地圖的使用(普通定位、反地理編碼,有需要的朋友可以參考下
    2015-08-08
  • iOS Xcode匯編模式切換的方法介紹

    iOS Xcode匯編模式切換的方法介紹

    這篇文章主要給大家介紹了關于iOS Xcode匯編模式切換的相關資料,文中通過圖文介紹的非常非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2018-06-06
  • ios實現簡易隊列

    ios實現簡易隊列

    這篇文章主要為大家詳細介紹了ios實現簡易隊列,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-02-02
  • iOS簡單抽屜效果的實現方法

    iOS簡單抽屜效果的實現方法

    這篇文章主要為大家詳細介紹了iOS簡單抽屜效果的實現方法,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • 淺析iOS應用開發(fā)中線程間的通信與線程安全問題

    淺析iOS應用開發(fā)中線程間的通信與線程安全問題

    這篇文章主要介紹了淺析iOS應用開發(fā)中線程間的通信與線程安全問題,談到了包括互斥鎖的使用等設計要點,需要的朋友可以參考下
    2015-11-11
  • 詳解iOS開發(fā) - 用AFNetworking實現https單向驗證,雙向驗證

    詳解iOS開發(fā) - 用AFNetworking實現https單向驗證,雙向驗證

    這篇文章主要介紹了詳解iOS開發(fā) - 用AFNetworking實現https單向驗證,雙向驗證,具有一定的參考價值,感興趣的小伙伴們可以參考一下。
    2016-12-12
  • iOS通過UIDocumentInteractionController實現應用間傳文件

    iOS通過UIDocumentInteractionController實現應用間傳文件

    這篇文章主要為大家介紹了iOS通過UIDocumentInteractionController實現應用間傳文件示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-01-01
  • iOS UITableView 拖動排序實現代碼

    iOS UITableView 拖動排序實現代碼

    這篇文章主要為大家詳細介紹了iOS UITableView 拖動排序實現代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-09-09
  • iOS使用UIKeyInput自定義密碼輸入框的方法示例

    iOS使用UIKeyInput自定義密碼輸入框的方法示例

    這篇文章主要給大家介紹了關于iOS如何使用UIKeyInput自定義密碼輸入框的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2019-02-02

最新評論