//--------------------內(nèi)存管理
內(nèi)存管理范圍:
管理任何繼承NSObject的對象,基本數(shù)據(jù)類型不需要內(nèi)存管理。
對象類型是程序運(yùn)行過程中動態(tài)分配的,存儲在堆區(qū),內(nèi)存管理主要是對"堆區(qū)中的對象"進(jìn)行內(nèi)存管理。
//--------------------概念
1)對象的所有權(quán)及引用計數(shù)
任何對象都可能擁有一個或多個所有者。只要一個對象至少還擁有一個所有者,它就會繼續(xù)存在
2)對象的引用計數(shù)器
每個OC對象都有自己的引用計數(shù)器,是一個整數(shù)表示對象被引用的次數(shù),即現(xiàn)在有多少東西在使用這個對象。對象剛被創(chuàng)建時,默認(rèn)計數(shù)器值為1,當(dāng)計數(shù)器的值變?yōu)?時,則對象銷毀。
在每個OC對象內(nèi)部,都專門有8個字節(jié)的存儲空間來存儲引用計數(shù)器。
3)引用計數(shù)器的作用
判斷對象要不要回收的唯一依據(jù)
(存在一種例外:對象值為nil時,引用計數(shù)為0,但不回收空間)就是計數(shù)器是否為0,若不為0則存在。
//----------------------對引用計數(shù)器的操作3個方法
給對象發(fā)送消息,進(jìn)行相應(yīng)的計數(shù)器操作。
retain消息:使計數(shù)器+1
release消息:使計數(shù)器-1(并不代表釋放對象)
retainCount消息:獲得對象當(dāng)前的引用計數(shù)器值rc
//--------------------------對象的銷毀
當(dāng)一個對象的引用計數(shù)器為0時,那么它將被銷毀,其占用的內(nèi)存被系統(tǒng)回收。
//1.對象銷毀就自動執(zhí)行dealloc方法
當(dāng)對象被銷毀時,系統(tǒng)會自動向?qū)ο蟀l(fā)送一條dealloc消息,一般會重寫dealloc方法,在這里釋放相關(guān)的資源,dealloc就像是對象的"臨終遺言"。
//2.重寫dealloc方法
一旦重寫了dealloc方法就必須調(diào)用[superdealloc],并且放在代碼塊的最后調(diào)用(不能直接調(diào)用dealloc方法)。
//3.對象銷毀了存儲空間不可用
一旦對象被回收了,那么他所占據(jù)的存儲空間就不再可用,堅持使用會導(dǎo)致程序崩潰(野指針錯誤)。
#注意:
1)如果對象的計數(shù)器不為0,那么在整個程序運(yùn)行過程,它占用的內(nèi)存就不可能被回收(除非整個程序已經(jīng)退出)
2)任何一個對象,剛生下來的時候,引用計數(shù)器都為1。(對象一旦創(chuàng)建好,默認(rèn)引用計數(shù)器就是1? )
3)當(dāng)使用"alloc"、"new"或者"copy"("mutablecopy")創(chuàng)建一個對象時,對象的引用計數(shù)器默認(rèn)就是1 ;
對應(yīng)的方法:
-retain ??????? +1
-release??????? -1
-retainCount當(dāng)前引用技數(shù)為多少0 --->系統(tǒng)釋放這個對象
-dealloc
#warning開放如何使用:需要理解MRC,但實際使用時盡量用ARC
//------------------------MRC
target ? Build Settings ? Basic ? Leveis搜索auto-->NO
內(nèi)存管理的關(guān)鍵是"如何判斷對象被回收"
問題:
1.對象創(chuàng)建完之后默認(rèn)引用計數(shù)是多少?????? 1
2.當(dāng)對象的引用計數(shù)為多少? ??? 0系統(tǒng)就會回收這個對象的空間
3.怎么知道這個對象空間被釋放了?
重寫dealloc方法在dealloc方法中打印以下信息
-------------------關(guān)于dealloc方法
代碼規(guī)范:
(1)一定要[superdealloc],而且要放到最后,意義是:"先釋放子類占用的空間再釋放父類占用的空間"
(2)對self(當(dāng)前)所擁有的的其他對象做一次release操作
-(void)dealloc
{
//先釋放子類擁有的對象屬性
[superdealloc];
}
注意:
永遠(yuǎn)不要直接通過對象調(diào)用dealloc方法(對象的引用計數(shù)為0系統(tǒng)會自動調(diào)用此方法)
-------------------------------------
原則
1.只要還有人在使用某個對象,那么這個對象就不會被回收;
2.只要你想使用這個對象,那么就應(yīng)該讓這個對象的引用計數(shù)器+1;
3.當(dāng)你不想使用這個對象時,應(yīng)該讓對象的引用計數(shù)器-1;
記住兩點:
1,誰創(chuàng)建"alloc","new",誰"release";
2,誰"retain",誰"release";
正確的手動管理操作:
一個alloc/new對應(yīng)一個release
一個retain對應(yīng)一個release
總結(jié)
有始有終,有加就應(yīng)該有減,曾經(jīng)讓某個對象計數(shù)器加1,就應(yīng)該讓其在最后減1;
//---------------------僵尸對象與內(nèi)存泄露
區(qū)分以下2個概念:
1)"僵尸對象"(野指針)(沒有初始化的指針變量;指向的內(nèi)存空間已經(jīng)被釋放的指針變量)
棧區(qū),系統(tǒng)會自動管理
//空指針:沒有指向任何東西的指針,給空指針發(fā)送消息不會報錯對象= nil
2)"內(nèi)存泄露"(棧區(qū)的指向已經(jīng)釋放,堆區(qū)的空間沒有釋放,這時堆區(qū)的空間就被泄露了)
堆區(qū),需要程序員手動管理
#不管是多個對象還是單個對象,只要我們研究它的內(nèi)存管理,就兩點:
#1.僵尸對象(野指針)
#2.內(nèi)存泄露
//---------------------------關(guān)于僵尸對象問題
首先什么是僵尸對象:
"僵尸對象"(野指針)(沒有初始化的指針變量;指向的內(nèi)存空間已經(jīng)被釋放的指針變量)
棧區(qū),系統(tǒng)會自動管理
#檢測野指針
(檢測僵尸對象,比較耗費(fèi)性能,所以Xcode默認(rèn)是不檢測的)
打開方法:
Edit Scheme? -->? Run Debug ? --->? Diagnostics? -->? Enable Zombie Objects(打勾)
對象一旦成為了僵尸對象,就不能再使用這個僵尸對象了
#判斷是否是僵尸對象:
person(對象)? -->看什么?
看是否執(zhí)行了dealloc方法--->重寫dealloc方法打印一句話提醒
dealloc方法一旦調(diào)用了,意味著堆區(qū)的空間已經(jīng)被釋放,釋放就意味著這個對象是僵尸對象
#避免使用僵尸對象的方法
1)僵尸對象調(diào)用方法,會報錯,訪問成員變量有時是可以的(但是這個是未知的)。
2)為了防止不小心調(diào)用了僵尸對象,可以將對象賦值nil(對象的空值)
用nil調(diào)用方法是不會報錯的。
#關(guān)鍵:
3)總而言之要合理使用release和retain
//-----------------nil和Nil及NULL、NSNull的區(qū)別:
了解:
1.nil對象的值person =nil;
2.Nil類對象的值
3.NULLC語言的關(guān)鍵字通用空指針
4.? NSNull空對象用在不能使用nil的地方
//---------------------------關(guān)于內(nèi)存泄露問題
首先:
什么是內(nèi)存泄露:
"內(nèi)存泄露"(棧區(qū)的指向已經(jīng)釋放,堆區(qū)的空間沒有釋放,這時堆區(qū)的空間就被泄露了)
堆區(qū),需要程序員手動管理
#對象的內(nèi)存泄露的幾種情況
1) retain和release不匹配,retain多余release導(dǎo)致的內(nèi)存泄露;
2)對象使用過程中,沒有被release,而被賦值為nil;
3)在方法中不當(dāng)?shù)氖褂昧藃etain;
#關(guān)鍵:
3)總而言之要合理使用release和retain
還是記住兩點:
1.誰創(chuàng)建"alloc","new",誰"release";
2.誰"retain",誰"release";
內(nèi)存管理的驗證:
1.查看retainCount的值
2.重寫dealloc方法,查看對象是否調(diào)用了dealloc方法
/*
一個alloc/new對應(yīng)一個release
一個retain對應(yīng)一個release
*/
//-----------------------多對象的內(nèi)存管理
#多個對象的僵尸對象問題
假設(shè)對象A與對象B有(關(guān)聯(lián))關(guān)系,如果對象B獨自銷毀,會影響對象A的操作.
{
Car *_car;
}
假設(shè):person擁有車
{
//創(chuàng)建一個人對象rc = 1
Person *person = [Person new];
//創(chuàng)建一個車對象rc = 1
Car *car = [Car new];
//讓人擁有這輛車
person.car = car;
//人調(diào)用開車這個方法
[person driver];
[car release];
#關(guān)鍵的地方在這里看這里!!
#??? [person driver]不能再調(diào)用這個方法人是擁有車的,人都還沒釋放,就不能調(diào)用與car有關(guān)系的driver方法是不科學(xué)的
[person release];
}
#解決方案
1.在人的實例變量_car的set方法中set方法中[_car retain]; ??? +1
2.在人的dealloc方法中先把自己擁有的對象先釋放[_car release];? -1
總結(jié):
凡事關(guān)聯(lián)關(guān)系,對象A與對象B有(關(guān)聯(lián))關(guān)系,就應(yīng)該在set方法中讓關(guān)聯(lián)的對象+1,然后在對象A的dealloc方法中讓關(guān)聯(lián)的對象-1.
//------------------------------------------------
#多個對象的內(nèi)存泄露問題
問題:
就上面的解決方法,還是有弊端,會造成原對象泄露
比如說人中途換車了多次調(diào)用了關(guān)于_car的set方法導(dǎo)致_car多次retain而沒有相對應(yīng)的release方法,導(dǎo)致rc沒變?yōu)?,對象沒有釋放,就說內(nèi)存泄露了
1.人第"1"次使用set方法賦值第一次把byd賦值給人的_car
2.人第"2"次使用set方法賦值第二次把byd賦值給人的_car
3.人第"3"次使用set方法賦值第三次把bigben賦值給人的_car
1.byd(首次賦值)? _car =nil_car != bydif執(zhí)行[_car release]由于_car是nil[nilrelease]賦值賦值之后_car retain??? byd+1
2.byd(多次調(diào)用)? _car = byd? _car == bydif不執(zhí)行
3.bigBen(換車)? _car = byd? car = bigBen? _car != carif執(zhí)行[_car release] --> [byd release]賦值賦值指針bigBen retain ? bigBen + 1
在dealloc方法中:
如果只執(zhí)行了第1步或者第1第2步則:
[_car release];? _car (byd) release??? -1
如果執(zhí)行了第1,2,3步則:
[_car release];//? _car? ---> bigBen ??? -1
-(void)setCar:(Car *)car{
if(_car != car){//? _car != car ????????? _car實例變量!=??? car參數(shù)
[_car release];//先release舊值
_car = car;//賦新值_car = [car retain];
[_car retain];//再retain新值
}
#判斷是否是同一個對象,release舊值,retain新值
}
-(void)dealloc{
//該對象在被釋放之前,必須先把自己所擁有的對象釋放,也就是release一次自己的實例變量(對象)
[_car release];
[superdealloc];
}
//----------------------------set方法內(nèi)存管理
#基本數(shù)據(jù)類型: int float double long struct enum基本數(shù)據(jù)類型做實例變量,就正常寫法
-(void)setAge:(int)age{
_age = age;
}
#對象類型:對于對象作為另一個類的實例變量
判斷是否是同一個對象,release舊值,retain新值
-(void)setCar:(Car *)car{
if(_car != car){
[_car release];//先release舊值
_car = car;//賦新值
[_car retain];//再retain新值
}
}
//------------------------@property參數(shù)
@property幫我們生成get和set方法的聲明和實現(xiàn)
#格式: @property (參數(shù)1,參數(shù)2)數(shù)據(jù)類型實例變量名(記住:此處不要帶下劃線)
#原子性: ? (習(xí)慣不加鎖)
"atomic":默認(rèn)值,對屬性加鎖,多線程下線程安全
"nonatomic":對屬性不加鎖,多線程下不安全,但速度快
#讀寫屬性: (用得不多)
"readwrite":默認(rèn)值,生成getter,setter方法.
"readonly":只生成getter方法
#? setter方法的處理
1)基本數(shù)據(jù)類型或者C語言的構(gòu)造類型:直接賦值
intfloatdoublelongstructenum等BOOL/ Boolean
"assign":@property的默認(rèn)值,直接賦值
//如果使用assign? set方法就會生成如下代碼
-(void)setAge:(int)age{
_age = age;
}
2)OC對象類型
"retain":先release原來的值,再retain新值
"copy":先release原來的值,再copy新值(NSString *)
//如果使用retain? set方法就會生成如下代碼
-(void)setCar:(Car *)car{
if(_car != car){
[_car release];
_car = [car retain];
}
}
練習(xí)
{
Car *_car;
int_age;
enumSex _sex;
Dog *_dog;
NSString *_name;
}
@property(retain,nonatomic) Car *car;
@property(nonatomic,assign)intage;
@property(nonatomic,assign)enumSex sex;
@property(nonatomic,retain) Dog *dog;
@property(nonatomic,copy) NSString *name;
@property(nonatomic,assign,setter= setIsVip:,getter= haha)BOOLvip;
原方法名
-(void)setVip:(BOOL)vip;
-(BOOL)vip;
setter = setIsVip:,getter = haha
// setter=setIsVip:
// getter=haha方法名改了
如果使用中括號調(diào)用方法,要必須把方法名寫正確了
如果使用點語法,就可以.實例變量名