? ? ? 在運行程序的過程中,需要創建大量的對象。對象從創建出來、使用之后,就需要對內存中的對象進行釋放,不然內存中的垃圾對象會越來越多,造成內存泄漏,從而嚴重影響程序的運行效率,甚至會引起系統的crash。而C#、JAVA等高級語言都有較為完善的回收機制(GC)來解決無效對象的問題。但是在Ojbc中卻缺少相應的較為完善的內存管理機制。在早先的Xcode版本中,對象的釋放需要開發者純手動進行釋放。目前Apple已經在近些年開始引入的自動回收的機制。但是并不是全自動的,所以主要講解一下Objc的內存管理問題。
首先,需要說明的是,Xcode4.2之后引入了ARC機制,是Xcode項目在啟動時默認的選項。需要自己手動關閉ARC功能。在Xcode中關閉ARC:項目屬性—Build Settings--搜索“garbage”找到Objective-C Automatic Reference Counting設置為No即可。
引用計數器
首先,在JAVA等高級語言中,GC會自動管理內存。當實例化一個對象之后會有一個變量來引用該對象(引用的實質是變量會存儲對象的物理地址),當該變量不再引用的時候,GC就會自動回收之前的對象。也就是說,創建的對象一旦沒有任何變量引用他,那么就將被回收。
但是,Ojbc中沒有類似的垃圾回收機制。而OC引入了對象引用計數器來管理內存。在OC中,每一個對象在生成之時都會有一個與他對應的整數(retainCount),叫做“引用計數器”,用來實時記錄改對象被引用與釋放的次數,當對象創建之初,就會被默認為reainCount為1。內存在判斷一個對象何時釋放的唯一依據就是判斷該對象的引用計數是否為0。在OC中,調用對象的alloc、retain、new、copy時,計數器都會自動的在原基礎上+1,當調用release時計數器-1。當release到對象的retainCount為0時,系統對調用該對象的dealloc方法將其銷毀。
舉例:
我創建一個ARCObject類,并且復寫它的dealloc對象方法
//? ARCObject.m
//? ARCTest
//
//? Created by workMAC on 16/8/12.
//? Copyright ? 2016年 yichen Wang. All rights reserved.
//
#import "ARCObject.h"
@implementation ARCObject
@synthesize number;
-(void)dealloc
{
NSLog(@"銷毀對象");
[super dealloc];
}
@end
在主函數做如下代碼調用:
ARCObject * ARCStr = [[ARCObject alloc] init];
[ARCStr retain];
NSLog(@"retainCount=%lu",[ARCStr retainCount]);
[ARCStr release];
NSLog(@"retainCount=%lu",[ARCStr retainCount]);
此時打印的retainCount為
2016-08-15 16:15:37.092 ARCTest[3717:184315] retainCount=2
2016-08-15 16:15:37.092 ARCTest[3717:184315] retainCount=1
可以看到在retain方法被調用后的retainCount加1,release減1。而此時再次release,則引用計數將變為0,代碼將執行ARCObject的dealloc方法。這時可以看到控制臺打?。?/p>
2016-08-15 16:22:10.215 ARCTest[3773:188463] 銷毀對象
注意:
在最終release銷毀對象成功以后,需要我們手動為ARCStr設置其變量為nil。否則會出現野指針的情況(即該對象已經不屬于本程序,在對他進行調用等操作是很危險的)還有一點就是,Objc中給空對象發送消息是不會引起什么錯誤的,所以應該注意釋放的嚴謹性和完整性。完整的釋放一個對象應該如此:
ARCObject * ARCStr = [[ARCObject alloc] init];
[ARCStr retain];
NSLog(@"retainCount=%lu",[ARCStr retainCount]);
[ARCStr release];
NSLog(@"retainCount=%lu",[ARCStr retainCount]);
[ARCStr release];
ARCStr = nil;
[ARCStr release];
內存釋放的原則
在項目中,手動管理面臨許多問題,尤其在對象之間來回引用的時候會造成很多不準確,即retain和release的計數最終并不能對應上。因此就需要遵循一個原則:誰創建,誰釋放。誰引用,誰釋放。
屬性參數
這里要講到的是我們在定義類所使用的對像時,@property括號中的內容問題。
在進行計數的時候,都是我們在方法里對對象進行retain、release的操作來控制它的引用計數器。那么通過@property其實是可以實現不調用對象的setter、getter方法來自動實現的。自動處理而不會造成內存泄漏。括號內的參數的意義如下:
從上邊也可以看到,@property的參數分為三類,也就是說它可以被三個不同種類的詞修飾。三個詞如果不設置,或者只設置一個,那么程序會默認三個的默認參數:(atomic,readwrite,assign),一個最安全,最基本的參數類型。
首先原子性來說,一般除非開啟線程使用atomic,否則請考慮使用nonatomic來修飾,它的性能更好,速度更快。
讀寫屬性,通俗講:readwrite可以對改對象之行讀寫操作,而readonly只能讀取不可寫入更改。
set的方法處理里,assign是直接賦值的。一般來說,在int等類型修飾較多。retain多用于非字符串的對象。copy多用來修飾字符串對象、block、NSArray、NSDictionary。
自動釋放池
Xcode4.2以后引入的ARC機制叫做“自動引用計數”(或“自動釋放池”),它相比前邊提到的JAVA等語言的內存管理差很多,其實也可以說是半自動的一種內存管理。它通過對@autoreleasepool聲明一個函數塊。
//? main.m
//? guigutang
//
//? Created by 王一臣 on 16/6/16.
//? Copyright ? 2016年 tuzhi Information Technology. All rights reserved.//#import#import "AppDelegate.h"
int main(int argc, char * argv[])
{
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
那么生成一個對象,在初試進入這個方法時調用了autorelease,那么代碼執行完畢之后,在@autoreleasepool塊中,調用過autorelease的對象都將自動調用一次release。看到這里應該能明白了,OC中的自動管理,指的就是提對象自動釋放一次。所以它并不能保證對象一定會被回收,因為它只是調用一個release,并不是保證reatinCount為0。
注意:
1.自動緩存機制中,對象調用autorelease并不會引起retainCount的改變,只是把改對象放在了自動釋放池中。在@autoreleasepool塊執行完畢,最后對釋放池的對象統一release一次。
2.自動釋放池的ARC機制,實質是在程序最后統一調用對象的release方法一次,并不表示一定會銷毀對象(前頭有說過,銷毀對象的唯一依據是retainCount為0)
3.Objc中的類庫的靜態方法一般自帶autorelease方法,不需要手動釋放。
4.由于自動釋放池都是在程序最后統一銷毀對象,所以如果一個操作很占內存,,或者其對象在過程中非常占用內存,那么請考慮把對象不要放自動釋放池,或者說放在多個。最好是不好以來Xcode的ARC機制。比如SDWebImage在UITableview的框架下加載大量的高質量gif時,其實會有很大的內存漏洞,會直接導致APP內存爆炸,程序會出現crash。所以,內存管理還是需要非常重視的!