前言:自從蘋果開發(fā)出 ARC這個(gè)后,基本上使用 MRC開發(fā)的就很少了它呀,但是我們還是有必要了解一下原理以及過去的使用。
1. MRC是什么
MRC(MannulReference Counting)簡(jiǎn)而言之就是手動(dòng)計(jì)數(shù)棒厘,手動(dòng)管理 對(duì)象的釋放以及引用計(jì)數(shù)
2. ARC是什么
ARC(Automatic Reference Counting) 自動(dòng)管理計(jì)數(shù)纵穿,不需要寫多余的代碼。
我們簡(jiǎn)單的來了解一下MRC的工作
現(xiàn)在我們來新建一個(gè)工程 并且在如圖的地方輸入 "-fno-objc-arc"
然后我們?cè)?appdelegate.m文件里面輸入如下的代碼
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSObject *object=[[NSObject alloc]init];
///這個(gè)時(shí)候 object 沒有被別的對(duì)象持有 僅僅調(diào)用了 [object retainCount] 方法 所以 object的引用計(jì)數(shù)為1
NSLog(@"objectCount:%ld",[object retainCount]);
NSObject *another=[object retain];
///這個(gè)時(shí)候 object 被 another 持有 所以引用計(jì)數(shù)+1 此時(shí)引用計(jì)數(shù)為 2
NSLog(@"objectCount:%ld",[object retainCount]);
[another release];
///執(zhí)行完[another release]這句代碼后 another 的被釋放 所以 object 的引用計(jì)數(shù)變成1了
NSLog(@"objectCount:%ld",[object retainCount]);
///執(zhí)行完這句代碼后, object 引用計(jì)數(shù)就為0了
[object release];
return YES;
}
輸出結(jié)果如下:
我們通過這個(gè)例子可以看出來,對(duì)于 MRC來說,需要手動(dòng)維護(hù)對(duì)象的計(jì)數(shù),管理對(duì)象的存在和銷毀的問題.如果稍有不注意的地方,就會(huì)存在內(nèi)存泄露和循環(huán)引用的問題,那么蘋果為了避免這一套繁瑣的開發(fā)任務(wù),方便開發(fā)者去更好的進(jìn)行開發(fā)任務(wù),于是開發(fā)出了一套 ARC,這是我們今天重點(diǎn)學(xué)習(xí)的內(nèi)容
在此之前,我們需要很好的了解什么引用計(jì)數(shù):
一個(gè)對(duì)象被持有的數(shù)量,打個(gè)比方來說,有一根繩子,沒有人握住的時(shí)候,引用計(jì)數(shù)是0,當(dāng)1個(gè)人握住的時(shí)候 引用計(jì)數(shù)+1 ,往后每疊加一個(gè)人,引用計(jì)數(shù)變依次+1,當(dāng)有一個(gè)人松手的時(shí)候引用計(jì)數(shù)-1,直到?jīng)]有繩子沒有人握住的時(shí)候,繩子掉下來 銷毀了,這就是引用計(jì)數(shù)的概念.
所以引用計(jì)數(shù)的管理方法是
每個(gè)對(duì)象都有一個(gè)與之關(guān)聯(lián)的整數(shù),這個(gè)整數(shù)被稱為引用計(jì)數(shù),在Objective-C中,通過不同的方法可以對(duì)引用計(jì)數(shù)進(jìn)行操作,具體的處理如下表:
| 對(duì)象操作 | Objective-C方法 | 對(duì)應(yīng)的操作結(jié)果 |
| ------------- |:-------------|: -----|
| 生成并持有對(duì)象 | alloc, new, copy,mutableCopy等方法 | 生成對(duì)象并設(shè)置引用計(jì)數(shù) =1 |
| 持有對(duì)象 | reatain方法 | 使引用計(jì)數(shù) +1 |
| 釋放對(duì)象 | release方法 | 使引用計(jì)數(shù) -1 |
| 廢棄對(duì)象 | dealloc方法---系統(tǒng)自動(dòng)調(diào)用 | 引用計(jì)數(shù) =0 時(shí)調(diào)用 |
來講一下 alloc/reatain/release/dealloc方法的實(shí)現(xiàn)
-
alloc
我們看一下 alloc 如何實(shí)現(xiàn)的+(id)alloc
{
return [self allocWithZone:NSDefaultMallocZone()];
}
+(instancetype)allocWithZone:(struct _NSZone *)zone
{
return NSAllocateObject(self, 0, zone);
}
```
通過allocWithZone類方法調(diào)用 NSAllocateObject方法來開辟了一塊內(nèi)存空間,我們來看一下NSAllocateObject方法是如何實(shí)現(xiàn)的
struct obj_layout{
NSUInteger retained;
};
inline id
NSAllocateObject(Class _Nonnull aClass, NSUInteger extraBytes, NSZone * _Nullable zone)
{
int size = 計(jì)算容納對(duì)象所需要的內(nèi)存大小
id new = NSZoneMalloc(zone, size);
memset(new,0,size);
new = (id)&((struct obj_layout *)new)[1];
}
NSAllocateObject通過調(diào)用NSZoneMalloc函數(shù)分配存放對(duì)象所需要的內(nèi)存控件,之后將該內(nèi)存空間置0,最后返回作為對(duì)象而使用的指針
我們可以執(zhí)行一下如下代碼
id obj = [[NSObject alloc]init];
NSLog(@"retainCount = %lu",(unsigned long)[obj retainCount]);
執(zhí)行結(jié)果為:
2017-06-28 18:15:04.009 MRCTest[12244:1459486] retainCount = 1
reatian 和 release 的方法剛剛在 MRC已經(jīng)看過 我們看一下 dealloc 方法
-(void)dealloc
{
NSDeallocateObject(self);
}
inline void
NSDeallocateObject(id _Nonnull object)
{
struct obj_layout *o = ((struct obj_layout*) anObject)[-1];
free(o);
}
每個(gè)對(duì)象都具備 dealloc 方法,當(dāng)一個(gè)對(duì)象的引用計(jì)數(shù)為0的時(shí)候,也就意味著沒有任何地方需要該對(duì)象,系統(tǒng)會(huì)自動(dòng)回收對(duì)該對(duì)象所占用的內(nèi)存,在系統(tǒng)銷毀對(duì)象的時(shí)候,會(huì)自動(dòng)調(diào)用該對(duì)象的 dealloc 方法來執(zhí)行一些回收的操作,如果此時(shí)該對(duì)象還對(duì)其他對(duì)象有引用的話,那么就需要重寫 dealloc 方法來釋放該對(duì)象對(duì)其他對(duì)象的引用 以確保該對(duì)象能正常釋放銷毀
如何重寫 dealloc 方法
- (void)dealloc {
// 處理該對(duì)象的其他引用(通過release方法)
/** 回調(diào)父類的dealloc方法 */
[super dealloc];
}
對(duì)于alloc/reatain/release/dealloc可以總結(jié)如下:
- 在 Objective-C的對(duì)象中存有引用計(jì)數(shù)這一整數(shù)值
- 調(diào)用 alloc或者是 retain 方法后,引用計(jì)數(shù)值加1
- 調(diào)用 release 后,引用計(jì)數(shù)值減1
- 引用計(jì)數(shù)為0時(shí),調(diào)用 dealloc 方法銷毀此對(duì)象
蘋果對(duì)這四個(gè)方法的使用
-
alloc
+alloc +allocWithZone: class_createInstance //此方法可以通過objc4中的runtime/objc-runtime-new.mm確認(rèn) calloc // 分配內(nèi)存塊
retainCount
-retainCount
__CFDoExternRefOperation // 此函數(shù)根據(jù)retain,retainCount,release操作進(jìn)行分發(fā),調(diào)用__CFBasicHashXXX方法
CFBasicHashGetCountOfKey
- retain
-retain
__CFDoExternRefOperation
CFBasicHashAddValue
- release
__CFDoExternRefOperation
CFBasicHashRemoveValue // 當(dāng)此函數(shù)返回0時(shí), -release調(diào)用dealloc方法
可以從__CFDoExternRefOperation函數(shù)以及由此函數(shù)調(diào)用的哥哥函數(shù)名來看,蘋果的實(shí)現(xiàn)大概就是采用的引用計(jì)數(shù)表來管理引用計(jì)數(shù)的
如圖
好了今天這幾個(gè)方法就講到這里,明天我們將來學(xué)習(xí)一下AutoreleasePool