所有內(nèi)容引用自
《Objective-C 高級編程 iOS與OS X多線程和內(nèi)存管理》
成艘,加入了自己的部分理解。
本節(jié)小結(jié)断箫,點(diǎn)小1
跳到底部[1]
第一節(jié)瑰枫、思考方式
第三節(jié)光坝、autorelease實(shí)現(xiàn)
這里分兩種實(shí)現(xiàn)盯另,GNUstep
實(shí)現(xiàn)鸳惯,蘋果的實(shí)現(xiàn)芝发。
為啥要研究GNUstep
實(shí)現(xiàn)辅鲸?Cocoa
不開源独悴,沒辦法知道蘋果實(shí)現(xiàn)锣尉。GNUstep
開源自沧,并且是Cocoa
的互換框架拇厢。
GNUstep
實(shí)現(xiàn)
1旺嬉、alloc
實(shí)現(xiàn)
// GNUstep/modules/core/base/Source/NSObject.m
+ (id) alloc{
return [self allocWithZone:NSDefaultMallocZone()];
}
+ (id)allocWithZone:(NSZone *)z{
return NSAllocateObject (self, 0 , z);
}
struct obj_layout{
NSUInteger retained;
};
inline id
NSAllocateObject(Class aClass, NSUInteger extraBytes, NSZone *zone){
int size = 計(jì)算容納對象所需的內(nèi)存大小;
// 先分配內(nèi)存
id new = NSZoneMalloc(zone, size);
// 內(nèi)存置0
memset(new, 0, size);
// 返回指針
new = (id) & ((struct obj_layout *) new)[1];
}
NSZone
是防止內(nèi)存碎片化引入的結(jié)構(gòu)邪媳。對內(nèi)存分配的區(qū)域進(jìn)行多重化管理,根據(jù)使用對象的目的、對象大小分配內(nèi)存雨效,提高內(nèi)存管理的效率迅涮。(就是小內(nèi)存是一段連續(xù)空間,大內(nèi)存是一段連續(xù)空間徽龟。)
因?yàn)?code>iOS運(yùn)行時系統(tǒng)的內(nèi)存管理已經(jīng)極具效率叮姑,所以目前已經(jīng)忽略了區(qū)域的概念。
alloc
簡化區(qū)域后的實(shí)現(xiàn):
struct obj_layout{
NSUInteger retained;
};
+ (id) alloc{
int size = sizeof(struct obj_layout) + 對象大小;
struct obj_layout *p = (struct obj_layout *) calloc(1, size);
return (id)(p + 1);
}
其中retained
保存引用計(jì)數(shù)据悔,寫入到對象頭部传透。
對象引用計(jì)數(shù),可以使用retainCount
獲取到极颓。實(shí)現(xiàn)如下:
- (NSUInteger)retainCount{
return NSExtraRefCount(self) + 1;
}
inline NSUInteger
NSExtraRefCount(id anObject){
// 由對象尋址找到對象頭部朱盐,取出retained值返回
return ((struct obj_layout *) anObject) [-1].retained;
}
2菠隆、retain
實(shí)現(xiàn)
- (id)retain{
NSIncrementExtraRefCount(self);
return self;
}
inline void
NSIncrementExtraRefCount(id anObject){
// 如果超過最大值,給出提示
if(((struct obj_layout *) anObject) [-1].retained == UNNT_MAX - 1){
[NSException raise:NSInternalInconsistencyException format:@"NSIncrementExtraRefCount() asked to increment too far"];
}else{ // +1
((struct obj_layout *) anObject) [-1].retained++;
}
}
3、release
實(shí)現(xiàn)
- (void)release{
if (NSIncrementExtraRefCountWasZero(self)){
[self dealloc];
}
}
BOOL
NSIncrementExtraRefCountWasZero(id anObject){
if (((struct obj_layout *) anObject) [-1].retained == 0){
return YES;
}else{
((struct obj_layout *) anObject) [-1].retained --;
return NO;
}
}
4、dealloc
實(shí)現(xiàn)
- (void)dealloc{
NSDeallocateObject(self);
}
inline void
NSDeallocateObject(id anObject){
struct obj_layout *o = &((struct obj_layout *) anObject[-1];
free(o);
}
小結(jié):
-
Objective-C
對象中存有引用計(jì)數(shù)整數(shù)值 -
alloc
或retain
迄沫,引用計(jì)數(shù)+1 -
release
引用計(jì)數(shù)-1 - 引用計(jì)數(shù)為0時盼砍,調(diào)用
dealloc
廢棄對象
蘋果實(shí)現(xiàn)
NSObject
源碼沒有公開睬捶,只能通過斷點(diǎn)來追蹤程序的執(zhí)行臀晃。
1座韵、alloc
的實(shí)現(xiàn)
+alloc
+allocWithZone:
// 創(chuàng)建對象
class_createInstance
// 分配內(nèi)存
calloc
2宦棺、retainCount/retain/release
的實(shí)現(xiàn)
// retainCount
__CFDoExternRefOperation
CFBasicHashGetCountOfKey
// retain
__CFDoExternRefOperation
CFBasicHashAddValue
// release
__CFDoExternRefOperation
CFBasicHashRemoveValue
// CFBasicHashRemoveValue返回0時蹬屹,會調(diào)用dealloc
CF
前綴,是使用了Core Foundation
框架。
__CFDoExternRefOperation
在CFRuntime.c
中铡买。便于理解,簡化實(shí)現(xiàn)如下景埃。
int __CFDoExternRefOperation(uintptr_t op,id obj){
CFBasicHashRef table = 取得對象對應(yīng)的哈希表(obj);
int count;
switch(op){
case OPERATION_retainCount:
count = CFBasicHashGetCountOfKey(table, obj);
return count;
case OPERATION_retain:
CFBasicHashAddValue(table,obj);
case OPERATION_release:
count = CFBasicHashRemoveValue(table,obj);
return 0 == count;
}
}
大致可以得出完慧,采用的是哈希表來管理引用計(jì)數(shù)的蛤织,表健值是內(nèi)存塊地址乞巧。
前面GNUstep
實(shí)現(xiàn),是把引用計(jì)數(shù)放在對象內(nèi)存塊頭部。和蘋果相比各自的好處:
???
放在頭部的好處:
- 節(jié)約代碼量旁蔼。少量代碼就可以實(shí)現(xiàn)
- 能統(tǒng)一管理引用計(jì)數(shù)內(nèi)存塊和對象內(nèi)存塊贞谓。
?????
使用哈希表作為引用計(jì)數(shù)表的好處:
- 對象內(nèi)存塊分配不再考慮內(nèi)存塊頭部。
- 哈希表存有內(nèi)存塊地址,可以追溯到各個對象的內(nèi)存塊广辰。
第二點(diǎn)特別重要,即使內(nèi)存塊損壞了,只要引用計(jì)數(shù)表沒有破壞囱持,就可以確認(rèn)內(nèi)存塊的位置晴弃。特別是檢測內(nèi)存泄露時,可以幫助定位各對象是否持有世曾。
小結(jié)
1蕊程、可以通過GNUstep
源碼來推測蘋果實(shí)現(xiàn)。
2、alloc\retain
引用計(jì)數(shù)+1。
3、release
引用計(jì)數(shù)-1,為0時,調(diào)用dealloc
廢棄對象风喇。
4豁鲤、引用計(jì)數(shù)由哈希表管理,方便追溯內(nèi)存塊,定位內(nèi)存泄露。
-
??假裝是錨點(diǎn)的腳注 ?