OC基礎(chǔ)總結(jié)
重新回過頭看這些基礎(chǔ)知識,對許多知識點都有新的認識,擁有堅實的基礎(chǔ)才能更快的成長。
OC內(nèi)存管理 - 基礎(chǔ)與MRC
內(nèi)存管理概述
- 內(nèi)存管理
內(nèi)存的作用:存儲數(shù)據(jù)。
1). 如何將數(shù)據(jù)存儲到內(nèi)存之中。
聲明1個變量,然后將數(shù)據(jù)存儲進去。
2). 當數(shù)據(jù)不再被使用的時候,占用的內(nèi)存空間如何被釋放。 - 內(nèi)存中的五大區(qū)域
棧: 局部變量,當局部變量的作用域被執(zhí)行完畢之后,這個局部變量就會被系統(tǒng)立即回收。
堆: OC對象,使用C函數(shù)申請的空間。需要我們自己進行內(nèi)存管理
BSS段: 未初始化的全局變量、靜態(tài)變量。一旦初始化就回收,并轉(zhuǎn)存到數(shù)據(jù)段之中。
數(shù)據(jù)段: 已經(jīng)初始化的全局變量、靜態(tài)變量。直到程序結(jié)束的時候才會被回收。
代碼段: 代碼,程序結(jié)束的時候系統(tǒng)會自動回收存儲在代碼段中的數(shù)據(jù)。
棧、BSS段、數(shù)據(jù)段、代碼段存儲在它們中的數(shù)據(jù)的回收,是由系統(tǒng)自動完成的,不需要我們干預(yù)。
分配在堆區(qū)中的OC對象,是肯定需要被回收的。
iPhone 內(nèi)存機制,會在占用內(nèi)存40M及45M時警告,達到120M時就會直接閃退。
而存儲在堆中的OC對象,系統(tǒng)不會自動回收,直到程序結(jié)束的時候才會被回收。內(nèi)存管理的范圍:
只需要管理存儲在堆中的OC對象的回收,其他區(qū)域中的數(shù)據(jù)的回收是系統(tǒng)自動管理的,不需要我們進行管理。對象應(yīng)該什么時候被回收?
當有人使用這個對象的時候,這個對象就千萬不能回收。只有在沒有任何人使用這個對象的時候,才可以回收。引用計數(shù)器
1). 每1個對象都有1個屬性,叫做retainCount引用計數(shù)器,類型是unsigned long占據(jù)8個字節(jié)。
引用計數(shù)器的作用: 用來記錄當前這個對象有多少個人在使用它,默認情況下,創(chuàng)建1個對象出來,這個對象的引用計數(shù)器的默認值是1。
2). 當多1個人使用這個對象的時候,應(yīng)該先讓這個對象的引用計數(shù)器的值+1代表這個對象多1個人使用。
3). 當這個對象少1個人使用的時候,應(yīng)該先讓這個對象的引用計數(shù)器的值-1代表這個對象少1個人使用。
4). 當這個對象的引用計數(shù)器變?yōu)?的時候,代表這個對象無人使用,這個時候系統(tǒng)就會自動回收這個對象。如何操作引用計數(shù)器.
1). 為對象發(fā)送1條retain消息,對象的引用計數(shù)器就會加1,當多1個人使用對象的時候才發(fā)。
2). 為對象發(fā)送1條release消息,對象的引用計數(shù)器就會減1,當少1個人使用對象的時候才發(fā)。
3). 為對象發(fā)送1條retainCount消息,就可以取到對象的引用計數(shù)器的值。
4). 對象的引用計數(shù)器變?yōu)?的時候,對象就會被系統(tǒng)立即回收,在對象被回收的時候,會自動調(diào)用對象的dealloc方法。內(nèi)存管理的分類
MRC: Manual Reference Counting 手動引用計數(shù)-手動內(nèi)存管理-當多1個人使用對象的時候,要求程序員手動的發(fā)送retain消息,少1個人使用的時候程序員手動的發(fā)送relase消息。
ARC: Automatic Reference Counting 自動引用計數(shù)-自動內(nèi)存管理-系統(tǒng)自動的在合適的地方發(fā)送retain relase消息。MRC
1). 新創(chuàng)建1個對象,這個對象的引用計數(shù)器的值默認是1。
2). 當對象的引用計數(shù)器變?yōu)?的時候,對象就會被系統(tǒng)立即回收并自動調(diào)用dealloc方法。
3). 為對象發(fā)送retain消息,對象的引用計數(shù)器就會+1。
4). 為對象發(fā)送release消息,并不是回收對象,而是讓對象的引用計數(shù)器-1
當對象的引用計數(shù)器的值變?yōu)?的時候,對象才會被系統(tǒng)立即回收。
MRC中重寫dealloc方法的規(guī)范:
必須要調(diào)用父類的dealloc方法,并且要放在最后一句代碼。內(nèi)存管理的重點及原則
1). 有對象的創(chuàng)建,就要匹配1個release。
2). retain的次數(shù)和release的次數(shù)要匹配。
3). 誰用誰retain,誰不用誰release,誰負責retain誰就負責relase。
4). 只有在多1個人用的時候才retain,少1個人使用的時候才release。有加就有減,有retain就應(yīng)該匹配1個release一定要平衡。
5). 在ARC機制下,retain release dealloc這些方法方法無法調(diào)用.野指針
C語言中的野指針: 定義1個指針變量,沒有初始化,這個指針變量的值是1個垃圾值,指向1塊隨機的空間,那么這個指針就叫做野指針。
OC中的野指針: 指針指向的對象已經(jīng)被回收了,這樣的指針就叫做野指針。對象及內(nèi)存回收的本質(zhì)
申請1個變量,實際上就是向系統(tǒng)申請指定字節(jié)數(shù)的空間,這些空間系統(tǒng)就不會再分配給別人了。當變量被回收的時候,代表變量占用的字節(jié)空間從此以后系統(tǒng)可以分配給別人使用了。但是字節(jié)空間中存儲的數(shù)據(jù)還在。
所謂的對象的回收,指的是對象占用的空間可以分配給別人。當這個對象占用的空間沒有分配給別人之前 其實對象數(shù)據(jù)還在.僵尸對象
1個已經(jīng)被釋放的對象,但是這個對象所占的空間還沒有分配給別人,這樣的對象叫做僵尸對象。
我們通過野指針去訪問僵尸對象的時候,如果僵尸對象占用的空間還沒有分配給別人的時候,這時是可以的,而當僵尸對象占用的空間分配給了別人使用的時候,是萬萬不可的。
因此只要對象成為了僵尸對象,無論如何都不允許訪問了。
Xcode提供了僵尸對象的實時檢查機制,可以將這個機制打開,打開之后,只要訪問的是僵尸對象,無論空間是否分配就會報錯。為什么不默認打開僵尸對象檢測.
一旦打開僵尸對象檢測那么在每訪問1個對象的時候,都會先檢查這個對象是否為1個僵尸對象。這樣是極其消耗性能的。
打開僵尸對象檢測:Product -> Scheme -> EditScheme -> Diagnostict -> Zombie Objects使用野指針訪問僵尸對象會報錯,如何避免僵尸對象錯誤。
當1個指針稱為野指針以后,將這個指針的值設(shè)置nil。
當1個指針的值為nil,通過這個指針去調(diào)用對象的方法(包括使用點語法)的時候,不會報錯,只是沒有任何反應(yīng),但是如果通過直接訪問屬性 -> 就會報錯。內(nèi)存泄露
指的是1個對象沒有被及時的回收,在該回收的時候而沒有被回收,一直駐留在內(nèi)存中,直到程序結(jié)束的時候才回收。
單個對象的內(nèi)存泄露的情況:
1). 有對象的創(chuàng)建,而沒有對應(yīng)的relase。
2). retain的次數(shù)和relase的次數(shù)不匹配。
3). 在不適當?shù)臅r候,為指針賦值為nil。
4). 在方法中為傳入的對象進行不適當?shù)膔etain。當屬性是1個OC對象的時候 setter方法的寫法
將傳進來的對象賦值給當前對象的屬性,代表傳入的對象多了1個人使用,所以我們應(yīng)該先為這個傳入的對象發(fā)送1條retain消息再賦值。
當為對象的這個屬性多次賦值的時候,代表舊對象少1個人用,新對象多1個人使用,應(yīng)該relase舊的 retain新的。
當當前對象銷毀的時候,代表屬性指向的對象少1個人使用,就應(yīng)該在dealloc中relase。
代碼寫法:
- (void)setCar:(Car *)car
{
if(_car != car)
{
[_car release];
_car = [car retain];
}
}
還要重寫dealloc方法.
- (void)dealloc
{
[_car release];
[super delloc];
}
@property
- @property 作用
1). 自動生成私有屬性。
2). 自動生成這個屬性的getter setter方法的聲明。
3). 自動生成這個屬性的getter setter方法的實現(xiàn)。 - @property參數(shù)
1). @property可以帶參數(shù)的.
@property(參數(shù)1,參數(shù)2,參數(shù)3......)數(shù)據(jù)類型 名稱;
2). 介紹一下@property的四組參數(shù).
與多線程相關(guān)的兩個參數(shù) nonatomic - atomic
與生成的setter方法的實現(xiàn)相關(guān)的參數(shù) assign - retain
與生成只讀、讀寫相關(guān)的參數(shù) readonly - readwrite
是與生成的getter setter方法名字相關(guān)的參數(shù) getter - setter
3). 介紹與多線程相關(guān)的參數(shù).
atomic: 默認值,如果寫atomic,這個時候生成的setter方法的代碼就會被加上一把線程安全鎖。
特點: 安全、但是效率低下。
nonatomic: 如果寫nonatomic這個時候生成的setter方法的代碼就不會加線程安全鎖。
特點: 不安全,但是效率高。
4). 與生成的setter方法的實現(xiàn)相關(guān)的參數(shù)。
assign: 默認值,生成的setter方法的實現(xiàn)就是直接賦值。
retain: 生成的setter方法的實現(xiàn)就是標準的MRC內(nèi)存管理代碼,也就是先判斷新舊對象是否為同1個對象,如果不是 release舊的 retain新的。
當屬性的類型是OC對象類型的時候,那么就使用retain。
當屬性的類型是非OC對象的時候,使用assign。
千萬注意:
retain參數(shù),只是生成標準的setter方法為標準的MRC內(nèi)存管理代碼,不會自動的再dealloc中生成relase的代碼。所以,還要自己手動的在dealloc中release。
5). 與生成只讀、讀寫的封裝。
readwrite: 默認值,代表同時生成getter setter。
readonly: 只會生成getter,不會生成setter
6). 生成getter、setter方法名稱相關(guān)的參數(shù)。
默認情況下@property生成的getter setter方法的名字都是最標準的名字。同時我們可以通過參數(shù)來指定@property生成的方法的名字。
getter = getter方法名字 用來指定@property生成的getter方法的名字。
setter = setter方法名字 用來指定@property生成的setter方法的名字。注意.setter方法是帶參數(shù)的所以要加1個冒號。
注意:如果使用getter setter修改了生成的方法的名字,在使用點語法的時候,編譯器會轉(zhuǎn)換為調(diào)用修改后的名字的代碼。
注意:無論什么情況都不要改setter方法的名字,因為默認情況下生成的名字就已經(jīng)是最標準的了。
當屬性的類型是1個BOOL類型的時候,可以修改這個getter的名字以is開頭以提高代碼的閱讀性。
7). 同1組參數(shù)只能使用1個,參數(shù)的順序可以隨意。
@class與#import的區(qū)別
- 當兩個類相互包含的時會出現(xiàn)循環(huán)引用的問題,造成無限遞歸,而導(dǎo)致無法編譯通過。
- 解決方案:
其中一邊不要使用#import引入對方的頭文件,而是使用@class 類名;
來標注這是1個類,這樣就可以在不引入對方頭文件的情況下,告訴編譯器這是1個類。
在.m文件中再#import對方的頭文件即可。 - 區(qū)別
1). #import是將指定的文件的內(nèi)容拷貝到寫指令的地方。
2). @class 并不會拷貝任何內(nèi)容,只是告訴編譯器,這是1個類,這樣編譯器在編譯的時候才可以知道這是1個類。
OC內(nèi)存管理 - ARC與分類
自動釋放池的原理
存入到自動釋放池中的對象,在自動釋放池被銷毀的時候,會自動調(diào)用存儲在該自動釋放池中的所有對象的release方法。
可以解決的問題:
將創(chuàng)建的對象,存入到自動釋放池之中,就不再需要手動的relase這個對象了,因為池子銷毀的時候,就會自動的調(diào)用池中所有的對象的relase。
自動釋放池的好處: 將創(chuàng)建的對象存儲到自動釋放池中,不需要再寫release。-
如何創(chuàng)建自動釋放池
@autoreleasepool{} //這對大括弧代表這個自動釋放池的范圍。
如何將對象存儲到自動釋放池之中
在自動釋放池之中調(diào)用對象的autorelease方法,就會將這個對象存入到當前自動釋放池之中。
//autorealse方法返回的是對象本身。
@autoreleasepool{
Person *p1 = [[[Person alloc] init] autorelease];
}
當這個自動釋放池執(zhí)行完畢之后,就會立即為這個自動釋放池中的對象發(fā)送1條release消息。使用注意
1). 只有在自動釋放池中調(diào)用了對象的autorelease方法,這個對象才會被存儲到這個自動釋放池之中,如果只是將對象的創(chuàng)建代碼寫在自動釋放之中,而沒有調(diào)用對象的autorelease方法,是不會將這個對象存儲到這個自動釋放池之中的。
2). 對象的創(chuàng)建可以在自動釋放池的外面,在自動釋放池之中,調(diào)用對象的autorelease方法,就可以將這個對象存儲到這個自動釋放池之中。
3). autorelease在外面是無法將對象存在自動釋放池之中的,當自動釋放池結(jié)束的時候,僅僅是對存儲在自動釋放池中的對象發(fā)送1條release消息,而不是銷毀對象。
4). 如果在自動釋放池中,調(diào)用同1個對象的autorelease方法多次,就會將對象存儲多次到自動釋放池之中。在自動釋放池結(jié)束的時候,會為對象發(fā)送多條release消息,那么這個是就會出現(xiàn)僵尸對象錯誤。所以,1個自動釋放池之中,只autorelease1次,只將這個對象放1次,否則就會出現(xiàn)野指針錯誤。
5). 如果在自動釋放池中,調(diào)用了存儲到自動釋放池中的對象的release方法,在自動釋放池結(jié)束的時候,還會再調(diào)用對象的release方法。這個時候就有可能會造成野指針操作。
也可以調(diào)用存儲在自動釋放池中的對象的retain方法.
6). 將對象存儲到自動釋放池,并不會使對象的引用計數(shù)器+1
所以其好處就是:創(chuàng)建對象將對象存儲在自動釋放池,就不需要在寫個release了。 省略創(chuàng)建對象匹配的那個release
7). 自動釋放池可以嵌套
調(diào)用對象的autorelease方法,會將對象加入到當前自動釋放池之中,只有在當前自動釋放池結(jié)束的時候才會像對象發(fā)送release消息。-
autorelease的規(guī)范
使用類方法創(chuàng)建的對象,要求這個對象在方法中就已經(jīng)被autorelease過了,這樣,我們只要在自動釋放池中,調(diào)用類方法來創(chuàng)建對象,那么創(chuàng)建的對象就會被自動的加入到自動釋放池中。
而使用對象方法不會自動autorelease。提供1個類方法來快速的得到1個對象. 規(guī)范 a. 這個類方法以類名開頭. 如果沒有參數(shù)就直接是類名 如果有參數(shù)就是 類名WithXX: b. 使用類方法得到的對象,要求這個對象就已經(jīng)被autorelease過了. + (instancetype)person { return [[[self alloc] init] autorelease]; } 這樣,我們直接調(diào)用類方法.就可以得到1個已經(jīng)被autorelease過的對象. @autoreleasepool { Person *p1 = [Person person]; //這個p1對象已經(jīng)被autorelase過了.不需要再調(diào)用autorelase //這個p1對象就被存儲到當前自動釋放池之中. }//當自動釋放池結(jié)束.就會為存儲在其中的p1對象發(fā)送release消息.
Apple的框架中的類也是遵守這個規(guī)范的。通過類方法創(chuàng)建的對象都是已經(jīng)被autorelease過的了。
所以,我們也要遵守這個規(guī)范,類方法返回的對象也要被autorealse過。我們凡是創(chuàng)建對象是調(diào)用類方法創(chuàng)建的對象,這個對象已經(jīng)是被autorelease過的。
ARC
- ARC - Automatic Reference Counting,自動引用計數(shù),即ARC。系統(tǒng)自動的幫助我們?nèi)ビ嬎銓ο蟮囊糜嫈?shù)器的值。
在程序中使用ARC非常簡單,只需要像往常那樣編寫代碼,只不過永遠不要寫retain、release、autorelease 永遠不要手動的調(diào)用 dealloc 這是ARC的最基本的原則。
特別注意的是ARC是編譯器機制,當ARC開啟時,編譯器會自動的在合適的地方插入retain、release、autorelase代碼,自動為對象做引用計數(shù)。 - ARC機制下,對象何時被釋放
本質(zhì): 對象的引用計數(shù)器為0的時候,自動釋放。
表象: 只要沒有強指針指向這個對象,這個對象就會立即回收。 - 強指針與弱指針
強指針: 默認情況下,聲明1個指針,這個指針就是1個強指針。
我們也可以使用__strong來顯示的聲明這是1個強指針。
Person *p1; // 這是1個強指針. 指針默認情況下都是1個強指針。
__strong Person *p2; // 這也是1個強指針.使用__strong來顯示的聲明強指針。
弱指針: 使用__weak標識的指針就叫做弱指針。
無論是強指針還是弱指針,都是指針,都可以用來存儲地址,這1點沒有任何區(qū)別,都可以通過這個指針訪問對象的成員。
唯一的區(qū)別就是在ARC模式下,他們用來作為回收對象的基準。
如果1個對象沒有任何強類型的指針指向這個對象的時候,對象就會被立即自動釋放
ARC下的單個對象的內(nèi)存管理
在ARC的機制下: 當1個對象沒有任何的強指針指向它的時候,這個對象就會被立即回收。
1). 當1個對象沒有任何的強指針指向它的時候,這個對象就會被立即回收。
int main(int argc, const char * argv[])
{
@autoreleasepool
{
Person *p1 = [Person new];//p1是1個強指針.
Person *p2 = p1;//p2也是個強指針.p1和p2都指向Person對象.
//因為我們說過,每1個指針變量默認情況下都是1個強指針變量.
NSLog(@"------");
}//當執(zhí)行到這里的時候.p1指針被回收,p2指針也被回收.那么Person對象就沒有任何
//強指針指向它了. 對象就在這被回收.
return 0;
}
2).將所有指向?qū)ο蟮膹娭羔樫x值為nil的時候.對象就會被立即回收.
int main(int argc, const char * argv[])
{
@autoreleasepool
{
Person *p1 = [Person new];//p1是1個強指針.
//因為我們說過,每1個指針變量默認情況下都是1個強指針變量.
p1 = nil;//當執(zhí)行到這句話的時候.p1賦值為nil.
//p1指針不再指向Person對象.
//Person對象沒有被任何的指針所指向,所以.Person對象在這里被釋放.
NSLog(@"------");
}
return 0;
}
這兩種情況就叫做沒有任何強指針指向?qū)ο?
1). 指向?qū)ο蟮乃袕娭羔槺换厥盏? 2). 指向?qū)ο蟮乃械膹娭羔樫x值為nil
ARC機制下釋放1個對象的標準是: 沒有任何強指針指向?qū)ο蟮臅r候,對象就會被釋放,如果這個時候有弱指針指向,也會被釋放.
int main(int argc, const char * argv[])
{
@autoreleasepool
{
//使用__strong來標識p1指針是強類型的,其實不寫__strong也是強類型的.
__strong Person *p1 = [[Person alloc] init];
//使用__weak標識指針p2的類型是弱類型指針.
__weak Person *p2 = p1;
//這個時候,p2指針和p1指針都指向Person對象.
//這個時候如果設(shè)置p1的值為nil
p1 = nil;
//這個時候Person對象只有被1個弱指針p2指向,沒有任何強指針指向
//所以Person對象在這里被回收.
}
return 0;
}
3). 最重要的1點:不能創(chuàng)建對象用1個弱指針存儲這個對象的指針,這樣的話,剛創(chuàng)建出來的對象,就沒有任何強指針指向,創(chuàng)建出來就會被回收。
int main(int argc, const char * argv[])
{
@autoreleasepool
{
//創(chuàng)建1個對象,將這個對象的地址賦值給1個弱指針
//后果就是創(chuàng)建出來的這個對象沒有被任何強指針指向.
//剛創(chuàng)建出來就會被釋放.
__weak Person *p1 = [[Person alloc] init];
}
return 0;
}
4). 在ARC機制下,當對象被回收的時候,原來指向這個對象的弱指針會被自動設(shè)置為nil。
Person *p1 = [Person new];
__weak Person p2 = p1;
p1 = nil;
[p2 sayHi];
ARC機制下的重點
當1個類的屬性是1個OC對象的時候,這個屬性應(yīng)該聲明為強類型的還是弱類型的。很明顯,應(yīng)該聲明為1個強類型的。
使用參數(shù),strong和weak控制@property生成的私有屬性是1個強類型的還是1個弱類型的。
@property(nonatomic,strong)Car *car;
代表生成的私有屬性_car 是1個強類型的.
@property(nonatomic,weak)Car *car;
代表生成的私有屬性_car 是1個弱類型的.
如果不寫,默認是strong.
使用建議
1). 在ARC機制下,如果屬性的類型是OC對象類型的,絕大多數(shù)場景下使用strong。
2). 在ARC機制下,如果屬性的類型不是OC對象類型的,使用assign。
3). strong和weak都是應(yīng)用在屬性的類型是OC對象的時候,屬性的類型不是OC對象的時候就使用assign。
4). 在ARC機制下,當兩個對象相互引用的時候,如果兩邊都使用strong 那么就會內(nèi)存泄露。解決方案: 1端使用strong 1端使用weak。
5). 使用命令-fno-objc-arc
設(shè)置部分類使用MRC。
6). 可以將整個MRC程序,轉(zhuǎn)換為ARC程序;
Edit -> Convert ->To Objective-C ARC (不建議使用)
分類 - category
將1個類分為多個模塊
使用分類的幾個注意點:
分類只能增加方法,不能增加屬性
在分類之中可以寫@property 但是不會自動生成私有屬性,也不會自動生成getter setter的實現(xiàn),只會生成getter setter的聲明。
所以需要自己寫getter 和 setter的聲明,如果也需要自己定義屬性,這個屬性就必須在本類中。在分類的方法實現(xiàn)中不可以直接訪問本類的真私有屬性(定義在本類的@implementation之中),但是可以調(diào)用本類的getter setter來訪問屬性。
分類中可以存在和本類同名方法的
當分類中有和本類中同名的方法的時候,優(yōu)先調(diào)用分類的方法,哪怕沒有引入分類的頭文件。
如果多個分類中有相同的方法,優(yōu)先調(diào)用最后編譯的分類。什么時候需要使用分類
當1個類的方法很多很雜的時候,當1個類很臃腫的時候。那么這個時候我們就可以使用分類,將這個類分為多個模塊,將功能相似的方法寫在同1個模塊之中。非正式協(xié)議 - 為系統(tǒng)自帶的類寫分類就叫做非正式協(xié)議。
ARC機制與垃圾回收機制的區(qū)別
垃圾回收機制 - GC: 程序在運行的期間,有1個東西叫做垃圾回收器,不斷的掃描堆中的對象是否無人使用。
ARC: 不是運行時,而在編譯的時候就在合適的地方插入retain 等操作,插入的代碼足以讓對象無人使用的時候,引用計數(shù)器為0,對象被回收。
文中如果有不對的地方歡迎指出。我是xx_cc,一只長大很久但還沒有二夠的家伙。