iOS內(nèi)存管理-整理中

一, iOS的內(nèi)存管理規(guī)則

1 基本原則

移動設(shè)備的內(nèi)存極其有限,每個app所能占用的內(nèi)存是有限制的

當(dāng)app所占用的內(nèi)存較多時,系統(tǒng)會發(fā)出內(nèi)存警告,這時得回收一些不需要再使用的內(nèi)存空間。比如回收一些不需要使用的對象、變量等

管理范圍:任何繼承了NSObject的對象,對其他基本數(shù)據(jù)類型(int、char、float、double、struct、enum等)無效

QQ堂開房間原理:只要房間還有人在用,就不會解散

只要還有人在用某個對象,那么這個對象就不會被回收
只要你想用這個對象,就讓對象的計數(shù)器+1
當(dāng)你不再使用這個對象時,就讓對象的計數(shù)器-1

  • 1, 你想使用(占用)某個對象,就應(yīng)該讓對象的計數(shù)器+1(讓對象做一次retain操作)
  • 2, 你不想再使用(占用)某個對象,就應(yīng)該讓對象的計數(shù)器-1(讓對象做一次release)
  • 3, 誰alloc,誰release

如果你通過alloc、new或[mutable]copy來創(chuàng)建一個對象,那么你必須調(diào)用release或autorelease

換句話說,不是你創(chuàng)建的,就不用你去[auto]release

  • 4, 誰retain,誰release

只要你調(diào)用了retain,無論這個對象是如何生成的,你都要調(diào)用release

  • 5, 總結(jié)

有始有終,有加就有減
曾經(jīng)讓對象的計數(shù)器+1,就必須在最后讓對象計數(shù)器-1

所以,一般需要在三個地方進行release

  • 在main或其他函數(shù)創(chuàng)建對象后.
  • 在dealloc方法內(nèi)
  • 在set方法內(nèi),進行if判斷

2 ARC

ARC是編譯器(時)特性,而不是運行時特性,更不是垃圾回收器

2.1、 自動釋放池

autorelease 其實并不是自動釋放,而是把對release的調(diào)用延遲了

autorelease 返回的是對象本身.
// autorelease方法會返回對象本身        
// 調(diào)用完autorelease方法后,對象的計數(shù)器不變        
// autorelease會將對象放到一個自動釋放池中        
// 當(dāng)自動釋放池被銷毀時,會對池子里面的所有對象做一次release操作
1、 autorelease的基本用法
  • 給某個對象發(fā)送一條autorelease消息時,就會將這個對象加到一個* 自動釋放池中
  • 當(dāng)自動釋放池銷毀時,會給池子里面的所有對象發(fā)送一條release消息
  • 調(diào)用autorelease方法時并不會改變對象的計數(shù)器,
  • 調(diào)用autorelease方法并且會 會返回對象本身autorelease實際上只是把對release的調(diào)用延遲了,對于每一次autorelease,系統(tǒng)只是把該對象放入了當(dāng)前的autorelease pool中,當(dāng)該pool被釋放時,該pool中的所有對象會被調(diào)用Release
2、autorelease的好處

1> 不用再關(guān)心對象釋放的時間
2> 不用再關(guān)心什么時候調(diào)用release

3、autorelease的使用注意

1> 占用內(nèi)存較大的對象不要隨便使用 autorelease
2> 占用內(nèi)存較小的對象使用autorelease,沒有太大影響

4、autorelease 應(yīng)用的場景
1,開發(fā)中經(jīng)常會提供一些類方法,快速創(chuàng)建一個已經(jīng)autorelease過的對象

一般可以為類添加一個快速創(chuàng)建對象的類方法
創(chuàng)建一個類方法,直接返回一個扔進釋放池里面的對象

聲明:
+ (id)person; 一般是和類同名的小寫.

實現(xiàn):

+ (id)person{
     創(chuàng)建對象時不要直接用類名,一般用self 
     //因為這個self代表類 解決了子類調(diào)用父類的問題,滿足子類需求 ,避免了重寫
    return [[[self alloc] init] autorelease];
}

調(diào)用: 創(chuàng)建對象

外界調(diào)用[Person person]時,根本不用考慮在什么時候釋放返回的Person對象

Person *p2 = [Person person];
以前的是:  Person *p2 = [[[Person alloc] init] autorelease];

也可以擴充方法

+ (id)personWithAge:(int)age{    
    Person *p = [self person];    
    p.age = age;    
    return p;
}
2、規(guī)律

系統(tǒng)自帶的方法里面沒有包含alloc、new、copy,說明返回的對象都是autorelease的
一般來說,除了alloc、new或copy之外的方法創(chuàng)建的對象都被聲明了autorelease

比如下面的對象都已經(jīng)是autorelease的,不需要再release

NSNumber *n = [NSNumber numberWithInt:100];        
NSString *s = [NSString stringWithFormat:@"jack"];        
NSString *s2 = @"rose”;

2.2. ARC的修飾符

ARC提供四種修飾符,分別是strong, weak, autoreleasing, unsafe_unretained
__strong:強引用,持有所指向?qū)ο蟮乃袡?quán),無修飾符情況下的默認值。如需強制釋放,可置nil。

比如我們常用的定時器
NSTimer * timer = [NSTimer timerWith...];

相當(dāng)于
NSTimer * __strong timer = [NSTimer timerWith...];

當(dāng)不需要使用時,強制銷毀定時器
[timer invalidate];  
timer = nil;

__weak:弱引用,不持有所指向?qū)ο蟮乃袡?quán),引用指向的對象內(nèi)存被回收之后,引用本身會置nil,避免野指針。

比如避免循環(huán)引用的弱引用聲明:
__weak __typeof(self) weakSelf = self;

__autoreleasing:自動釋放對象的引用,一般用于傳遞參數(shù)

比如一個讀取數(shù)據(jù)的方法
- (void)loadData:(NSError **)error;

當(dāng)你調(diào)用時會發(fā)現(xiàn)這樣的提示
NSError * error; [dataTool loadData:(NSError *__autoreleasing *)]

這是編譯器自動幫我們插入以下代碼
NSError * error; 
NSError * __autoreleasing tmpErr = error;
[dataTool loadData:&tmpErr];

__unsafe_unretained:為兼容iOS5以下版本的產(chǎn)物,可以理解成MRC下的weak,現(xiàn)在基本用不到,這里不作描述。

2.3. ARC的開啟與關(guān)閉

設(shè)置方法:
Targets -->Build Phases -->Compile Sources
如果你的工程是開啟ARC的, 那就需要對某些文件禁用ARC, (-fno-objc-arc)
如果你的工程是關(guān)閉ARC的, 那就需要對某些文件開啟ARC.(-fobjc-arc)

3. @property屬性的內(nèi)存管理

1、控制 set方法內(nèi)存管理相關(guān)的參數(shù)

  • retain : release舊值,retain新值(適用于OC對象類型)
  • assign : 直接賦值(默認,適用于非OC對象類型)
  • copy : release舊值,copy新值(一般用于 NSString * )

2、控制 是否要生成set方法

  • readwrite : 同時生成setter和getter的聲明、實現(xiàn)(默認)
  • readonly : 只會生成getter的聲明、實現(xiàn)

3、控制 多線程管理

  • nonatomic : 性能高 (一般就用這個) 不加鎖
  • atomic : 性能低(默認) 加鎖

4、控制 setter和getter方法的名稱

  • setter : 決定了set方法的名稱,一定要有個冒號 :
  • getter : 決定了get方法的名稱(一般用在BOOL類型)
// 返回BOOL類型的方法名一般以is開頭  
@property (getter = isRich, setter = setIsRich:) BOOL rich;   
@property (nonatomic, assign, readwrite) int weight;

4. block的內(nèi)存管理

Block的類型與內(nèi)存管理

根據(jù)Block在內(nèi)存中的位置分為三種類型NSGlobalBlock,NSStackBlock, NSMallocBlock。

NSGlobalBlock:類似函數(shù),位于text段;
NSStackBlock:位于棧內(nèi)存,函數(shù)返回后Block將無效;
NSMallocBlock:位于堆內(nèi)存。

  • 無論當(dāng)前環(huán)境是ARC還是MRC,只要block沒有訪問外部變量,block始終在全局區(qū)
  • MRC情況下
    1.block如果訪問外部變量,block在棧里
    2.不能對block使用retain,否則不能保存在堆里
    3.只有使用copy,才能放到堆里
  • ARC情況下
    1.block如果訪問外部變量,block在堆里
    2.block可以使用copy和strong,并且block是一個對象

block的循環(huán)引用
如果要在block中直接使用外部強指針會發(fā)生錯誤,使用以下代碼在block外部實現(xiàn)可以解決

__weak typeof(self) weakSelf = self;

但是如果在block內(nèi)部使用延時操作還使用弱指針的話會取不到該弱指針,需要在block內(nèi)部再將弱指針強引用一下
__strong typeof(self) strongSelf = weakSelf;
iOS中使用block必須自己管理內(nèi)存,錯誤的內(nèi)存管理將導(dǎo)致循環(huán)引用等內(nèi)存泄漏問題,這里主要說明在ARC下block聲明和使用的時候需要注意的兩點:  1)如果你使用@property去聲明一個block的時候,一般使用copy來進行修飾(當(dāng)然也可以不寫,編譯器自動進行copy操作),盡量不要使用retain。
@property (nonatomic, copy) void(^block)(NSData * data);

2)block會對內(nèi)部使用的對象進行強引用,因此在使用的時候應(yīng)該確定不會引起循環(huán)引用,當(dāng)然保險的做法就是添加弱引用標記。
__weak typeof(self) weakSelf = self;

有興趣的讀者可以深入了解:
  1、block的內(nèi)部實現(xiàn)原理是什么?  2、從內(nèi)存位置來看block有幾種類型?它們的內(nèi)存管理方式各是怎樣的?  3、對于不同類型的外部變量,block的內(nèi)存管理都是怎樣的?

5. 內(nèi)存管理代碼規(guī)范

  • 1, 函數(shù)或方法 里面只要調(diào)用了alloc,必須有release(autorelease)
    對象不是通過alloc產(chǎn)生的,就不需要release
NSString *s = @"Jack";      
stu.name = s;
  • 2, set方法的代碼規(guī)范

1> 基本數(shù)據(jù)類型:直接復(fù)制

 - (void)setAge:(int)age {    
    _age = age; 
}  

2> OC對象類型

 - (void)setCar:(Car *)car {    
    // 1.先判斷是不是新傳進來對象    
    if ( car != _car )    {        
        // 2.對舊對象做一次release        
        [_car release];         
        // 3.對新對象做一次retain        
        _car = [carretain];    
    } 
} 
  • 3, dealloc方法的代碼規(guī)范 不要直接調(diào)用dealloc
    1> 一定要[super dealloc],而且放到最后面
    2> 對self(當(dāng)前)所擁有的其他對象做一次release
 - (void)dealloc      {
    [_car release];         
    [super dealloc];      
}

</br>


二, 經(jīng)典內(nèi)存泄漏

1 僵尸對象和野指針

</br>

2 循環(huán)引用

</br>

3 循環(huán)中對象占用內(nèi)存大

</br>

4 無限循環(huán)

</br>


3, 項目中的注意點

  • 1, 代理 nil
  • 2, 通知 移除
  • 3, kvo 移除
  • 4, block 循環(huán)引用 __weak mrc __block
  • 5, 對象的循環(huán)引用 一端 assign 一端 strong

參考鏈接
http://www.itdecent.cn/p/8b1ed04b3ba9

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 內(nèi)存管理 簡述OC中內(nèi)存管理機制。與retain配對使用的方法是dealloc還是release,為什么?需要與a...
    丶逐漸閱讀 2,081評論 1 16
  • 為什么進行內(nèi)存管理? 由于移動設(shè)備的內(nèi)存極其有限,所以每個APP所占的內(nèi)存也是有限制的,當(dāng)app所占用的內(nèi)存較多時...
    天天想念閱讀 979評論 1 6
  • 內(nèi)存管理 ARC處理原理 ARC是Objective-C編譯器的特性,而不是運行時特性或者垃圾回收機制,ARC所做...
    b485c88ab697閱讀 11,347評論 3 47
  • iOS內(nèi)存管理 概述 什么是內(nèi)存管理 應(yīng)用程序內(nèi)存管理是在程序運行時分配內(nèi)存(比如創(chuàng)建一個對象,會增加內(nèi)存占用)與...
    蚊香醬閱讀 5,815評論 8 119
  • 內(nèi)存管理是程序在運行時分配內(nèi)存、使用內(nèi)存,并在程序完成時釋放內(nèi)存的過程。在Objective-C中,也被看作是在眾...
    蹲瓜閱讀 3,361評論 1 8

友情鏈接更多精彩內(nèi)容