一、內存管理(MRC)
(一) 管理對象
管理對象:OC對象
原因:
? ? ? ? 1、OC對象存放于堆里面
? ? ? ? 2、非OC對象(int、char、float、double、struct、enum)一般放在棧里面(棧內存會被系統自動回收)
(二) 基本原理
1、每個OC對象內部都有4個字節的存儲空間來存放引用計數器
2、誰創建誰release :如果你通過alloc、new或[mutable]copy來創建一個對象(引用計數+1),那么你必須調用release或autorelease(引用計數-1)
3、誰retain誰release:只要你調用了retain(引用計數+1),就必須調用一次release(引用計數-1)
4、release并不代表銷毀\回收對象,僅僅是計數器-1;引用計數為0時,對象被銷毀,系統會自動給對象發送一條dealloc消息。
5、一般來說,除了alloc、new或copy之外的方法創建的對象都被申明了autorelease。
(三) Set方法
- (void) setName:(NSString *)name
{
? ? ? ? if(_name!= name){
? ? ? ? ? ? ? ? [_namerelease];
? ? ? ? ? ? ? ? _name= [name retail];
? ? ? ? }
}
(四) ?一些術語
1、僵尸對象
已經被銷毀的對象(不能再使用的對象)
2、野指針
指向僵尸對象(不可用內存)的指針
給野指針發消息會報EXC_BAD_ACCESS錯誤
3、空指針
沒有指向存儲空間的指針(里面存的是nil,也就是0)
給空指針發消息是沒有任何反應的
objc_msgSend會通過判斷self來決定是否發送消息,如果self為nil,那么selector也會為空,直接返回,所以不會出現問題。視方法返回值,向nil發消息可能會返回nil(返回值為對象)、0(返回值為一些基礎數據類型)或0X0(返回值為id)等。但是對[NSNull null]對象發送消息時,是會crash的,因為這個NSNull類只有一個null方法
為了避免野指針錯誤的常見辦法
在對象被銷毀之后,將指向對象的指針變為空指針
(五) 一些設置
1、關閉ARC功能
Building->AutoMatic Reference Counting->NO
2、開啟僵尸對象監控
Edit Scheme->Diagnostics->Enable Zombie Object
(六) NSString創建類的幾種方式
NSString類型的字符串有三種方法:
方法1.直接賦值:?? ??NSString?*str1=?@"my string";
方法2.類函數初始化生成: ? ??NSString?*str2= [NSString stringWithString:@"my string"];
方法3.實例方法初始化生成: NSString?*str3= [[NSString alloc] initWithString:@"my string"];
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?NSString?*str4?=[[NSString?alloc]initWithFormat:@"my string"];
區別1:方法一生成字符串時,不會初始化內存空間,所以使用結束后不用釋放內存;
? ? ? ? ? 而其他三個都會初始化內存空間,使用結束后要釋放內存;
? ? ? ? ? 在釋放內存時方法2和3也不同,方法2是autorelease類型,內存由系統釋放;方法3則必須手動釋放
區別2:用Format初始化的字符串,需要初始化一段動態內存空間,如:0x6a42a40;
? ? ? ? ? 而用String聲明的字符串,初始化的是常量內存區,如:0x46a8,常量內存區的地址,只要值相同,占用的 ?地址空間是一致的。
所以str3和str1的地址一致,但是str4和str1的地址不一致。
二、內存警告
(一)?app收到Memory Warning后會調用:
UIApplication::didReceiveMemoryWarning->
UIApplicationDelegate::applicationDidReceiveMemoryWarning,
然后調用當前所有的viewController進行處理。
因此處理的主要工作是在viewController。
(二) 當我們的程序在第一次收到內存不足警告時,應該釋放一些不用的資源,以節省部分內存。否則,當內存不足情形依然存在,iOS再次向我們程序發出內存不足的警告時,我們的程序將會被iOS kill掉。
1、iOS3-iOS5.0以前版本收到內存警告:
調用didReceiveMemoryWarning內調用super的didReceiveMemoryWarning會將controller的view進行釋放。所以我們不能將controller的view再次釋放。
處理方法:
-(void)didReceiveMemoryWarning{
? ? ? ? [super?didReceiveMemoryWarning];//如沒有顯示在window上,會自動將self.view釋放。
? ? ? ? //?ios6.0以前,不用在此做處理,self.view釋放之后,會調用下面的viewDidUnload函數,在viewDidUnload函數中做處理就可以了。
}
-(void)viewDidUnload{
? ? ? ? //?Release?any?retained?subviews?of?the?main?view.不包含self.view
? ? ? ? //處理一些內存和資源問題。?
? ? ? ? [super?viewDidUnload];
}
2、iOS6.0及以上版本的內存警告:
調用didReceiveMemoryWarning內調用super的didReceiveMemoryWarning
只是釋放controller的resouse,不會釋放view
處理方法:
-(void)didReceiveMemoryWarning{
? ? ? ? [super didReceiveMemoryWarning];//即使沒有顯示在window上,也不會自動的將self.view釋放。
? ? ? ? // Add code to clean up any of yourown resources that are no longer necessary.
? ? ? ? //此處做兼容處理需要加上ios6.0的宏開關,保證是在6.0下使用的,6.0以前屏蔽以下代碼,否則會在下面使用self.view時自動加載viewDidUnLoad
? ? ? ?if ([[UIDevicecurrentDevice].systemVersion floatValue] >= 6.0) {
? ? ? ? //需要注意的是self.isViewLoaded是必不可少的,其他方式訪問視圖會導致它加載,在WWDC視頻也忽視這一點。
? ? ? ? ? ? ? if (self.isViewLoaded &&!self.view.window)//是否是正在使用的視圖{
? ? ? ? ? ? ? ? ? ? ? // Add code to preserve data storedin the views that might be
? ? ? ? ? ? ? ? ? ? ? // needed later.
? ? ? ? ? ? ? ? ? ? ? // Add code to clean up other strongreferences to the view in
? ? ? ? ? ? ? ? ? ? ? // the view hierarchy.
? ? ? ? ? ? ? ? ? ? ? self.view = nil;//目的是再次進入時能夠重新加載調用viewDidLoad函數。
? ? ? ? ? ? ? ?}
? ? ? ? }
}
但是似乎這么寫相對于以前并不省事。最終我們找到一篇文章,文章中說其實并不值得回收這部分的內存,原因如下:
1. UIView是UIResponder的子類,而UIResponder有一個CALayer的成員變量,CALayer是具體用于將自己畫到屏幕上的。
2. CALayer是一個bitmap圖象的包裝類,當UIView調用自身的drawRect時,CALayer才會創建這個bitmap圖象類。
3.具體占內存的其實是一個bitmap圖象類,CALayer只占48bytes, UIView只占96bytes。而一個iPad的全屏UIView的bitmap類會占到12M的大小!
4.在iOS6時,當系統發出MemoryWarning時,系統會自動回收bitmap類。但是不回收UIView和CALayer類。這樣即回收了大部分內存,又能在需要bitmap類時,根據CALayer類重建。
所以,iOS6這么做的意思是:我們根本沒有必要為了幾十byte而費力回收內存。
生命周期圖
三、內存泄漏分析解決方法
(一) 靜態分析
通過靜態分析我們可以最初步的了解到代碼的一些不規范的地方或者是存在的內存泄漏,這是我們第一步對內存泄漏的檢測。當然有一些警告并不是我們關心的可以略過。
(二) 通過instruments來檢查內存泄漏
這個方法能粗略的定位我們在哪里發生了內存泄漏。方法是完成一個循環操作,如果內存增長為0就證明我們程序在該次循環操作中不存在內存泄漏,如果內存增長不為0那證明有可能存在內存泄漏,當然具體問題需要具體分析。
(三) 代碼測試內存泄漏
在做這項工作之前我們要注意一下,在dealloc的方法中我們是否已經釋放了該對象所擁有的所有對象。觀察對象的生成和銷毀是否配對。準確的說就是init(創建對象的方法)和dealloc是否會被成對觸發(簡單說來就是走一次創建對象就有走一次dealloc該對象)。
四、內存檢查工具
編譯和分析工具Analyze
內存泄漏檢測工具—Leak
內存猛增檢測工具—Allocations