【IOS学习】之五:引用计数
来源:互联网 发布:java io异常 编辑:程序博客网 时间:2024/06/05 01:10
arc automatic reference counting 内存管理中对引用采取自动计数。
apple官方文档: 在oc中采用arc机制,让编译器来进行内存管理,在新一代apple llvm编译器中设置arc为有效状态,就无需再次键入retain或release代码,降低程序崩溃,内存泄露等风险的同时,很大程度上减少了开发程序的工作量。编译器完全清楚目标对象,并能立刻释放那些不再被使用的对象。如此一来,应用程序将具有可预测性,并且能流程运行,运行速度也将大幅提升。
来说一下引用计数: 比如上班, 最早进入办公室的人需要开灯,之后进入办公室的人需要照明, 下班离开办公室的人不需要照明,最后离开办公室的人需要关灯。
这样对应的引用计数就是:第一个人进入办公室开灯,引用计数是1. 之后进入办公室需要照明 引用计数是2 。 下班一个人离开办公室 引用计数变成了1 最后一个离开了办公室,引用计数变成了0 。
在内存管理中: 自己生成的对象,自己持有。
不是自己生成的对象,自己也能持有。
不再需要自己持有的对象就是放。
不是自己持有的对象无法释放。
生成并持有对象:alloc/new/copy/mutableCopy
持有对象:retain
释放对象: release
废弃对象: dealloc
这些内存管理是在 cocoa框架 中的foundation框架类库的NSObject类担负的。
autorelease 是使对象在超出指定的生存范围时能够自动并正确地释放。
我们在释放对象的时候,不能释放不是自己持有的对象。
ex:
//自己生成并持有对象id obj = [[NSObject alloc] init];//自己持有对象[obj release];//对象已释放[obj release];//释放之后再次释放已非自己持有的对象,应用程序崩溃。 //崩溃情况: 再度废弃已经废弃了的对象时崩溃, 访问已经废弃的对象时崩溃我们取得对象,但是自己不持有对象://取得对象,但是自己并不持有对象id obj1 = [obj0 object];//释放不是自己持有的对象,应用程序崩溃[obj1 release];
来说一个框架:GNUstep 他是和 cocoa框架的互换框架。他们的行为和实现方式是一样的,相似的。
在gnusetp中,alloc的实现如下:
/*** Allocates a new instance of the receiver from the default* zone, by invoking +allocWithZone: with* <code>NSDefaultMallocZone()</code> as the zone argument.<br />* Returns the created instance.*/+ (id) alloc{ return [self allocWithZone: NSDefaultMallocZone()];} * <p>* If you have turned on debugging of object allocation (by* calling the <code>GSDebugAllocationActive</code>* function), this method will also update the various* debugging counts and monitors of allocated objects, which* you can access using the <code>GSDebugAllocation...</code>* functions.* </p>*/+ (id) allocWithZone: (NSZone*)z{ return NSAllocateObject (self, 0, z);}
通过allocWithZone: 类方法调用NSAllocateObject函数分配对象。
/** Now do the REAL version - using the other version to determine* what padding (if any) is required to get the alignment of the* structure correct.*/struct obj_layout { char padding[__BIGGEST_ALIGNMENT__ - ((UNP % __BIGGEST_ALIGNMENT__) ? (UNP % __BIGGEST_ALIGNMENT__) : __BIGGEST_ALIGNMENT__)]; NSUInteger retained;};inline idNSAllocateObject (Class aClass, NSUInteger extraBytes, NSZone *zone) { int size = 计算容纳对象所需内存大小. id new = NSZoneMalloc(zone, size); memset(new, 0, size); new = (id) & ((struct obj_layout *)new)[1];}
这里是通过NSZoneMalloc来分配存放对象所需的内存空间,之后将该内存空间置0 最后返回作为对象而是用的指针。
这里的NSZone解释一下: 是为了防止内存碎片化而引入的结构,对内存分配的区域本身进行多重化管理,根据使用对象的目的、对象的大小分配内存,从而提高了内存管理的效率。
去掉NSZone的源代码:
struct obj_layout { NSUInteger retained;};+ (id) alloc { int size = sizeof (struct obj_layout) + 对象大小; struct obj_layout *p = (struct obj_alyout*)calloc(1, size); return (id)(p + 1);}
这里是用struct obj_layout中的retained整数来保存引用计数,并将其写入对象内存头部。 对象内存块 全部置0后返回。
通过retainCount来返回:
/*** Returns the reference count for the receiver. Each instance has an* implicit reference count of 1, and has an 'extra reference count'* returned by the NSExtraRefCount() function, so the value returned by* this method is always greater than zero.<br />* By convention, objects which should (or can) never be deallocated* return the maximum unsigned integer value.*/- (NSUInteger) retainCount{#if GS_WITH_GC return UINT_MAX;#else return NSExtraRefCount(self) + 1;#endif}/*** Return the extra reference count of anObject (a value in the range* from 0 to the maximum unsigned integer value minus one).<br />* The retain count for an object is this value plus one.*/inline NSUIntegerNSExtraRefCount(id anObject){#ifdef __OBJC_GC__ if (objc_collecting_enabled()) { return UINT_MAX-1; }#endif#if GS_WITH_GC return UINT_MAX - 1;#else /* GS_WITH_GC */ return ((obj)anObject)[-1].retained;#endif /* GS_WITH_GC */}
由对象寻址找到对象内存头部,从而访问其中的retained变量。
retain方法:/*** Increments the reference count and returns the receiver.<br />* The default implementation does this by calling NSIncrementExtraRefCount()*/- (id) retain{#if (GS_WITH_GC == 0) NSIncrementExtraRefCount(self);#endif return self;}/*** Increments the extra reference count for anObject.<br />* The GNUstep version raises an exception if the reference count* would be incremented to too large a value.<br />* This is used by the [NSObject-retain] method.*/inline voidNSIncrementExtraRefCount(id anObject)#endif{#if GS_WITH_GC || __OBJC_GC__ return;#else /* GS_WITH_GC */ if (allocationLock != 0) {#if defined(GSATOMICREAD) /* I've seen comments saying that some platforms only support up to * 24 bits in atomic locking, so raise an exception if we try to * go beyond 0xfffffe. */ if (GSAtomicIncrement((gsatomic_t)&(((obj)anObject)[-1].retained)) > 0xfffffe) { [NSException raise: NSInternalInconsistencyException format: @"NSIncrementExtraRefCount() asked to increment too far"]; }#else /* GSATOMICREAD */ NSLock *theLock = GSAllocationLockForObject(anObject); [theLock lock]; if (((obj)anObject)[-1].retained == UINT_MAX - 1) { [theLock unlock]; [NSException raise: NSInternalInconsistencyException format: @"NSIncrementExtraRefCount() asked to increment too far"]; } ((obj)anObject)[-1].retained++; [theLock unlock];#endif /* GSATOMICREAD */ } else { if (((obj)anObject)[-1].retained == UINT_MAX - 1) { [NSException raise: NSInternalInconsistencyException format: @"NSIncrementExtraRefCount() asked to increment too far"]; } ((obj)anObject)[-1].retained++; }#endif /* GS_WITH_GC */}
release的实现:
/*** Decrements the retain count for the receiver if greater than zero,* otherwise calls the dealloc method instead.<br />* The default implementation calls the NSDecrementExtraRefCountWasZero()* function to test the extra reference count for the receiver (and* decrement it if non-zero) - if the extra reference count is zero then* the retain count is one, and the dealloc method is called.<br />* In GNUstep, the [NSObject+enableDoubleReleaseCheck:] method may be used* to turn on checking for ratain/release errors in this method.*/- (oneway void) release{#if (GS_WITH_GC == 0) if (NSDecrementExtraRefCountWasZero(self)) {# ifdef OBJC_CAP_ARC objc_delete_weak_refs(self);# endif [self dealloc]; }#endif}BOOL NSDecrementExtraRefCountWasZero(id anObject) { if (((struct obj_layout *)anObject)[-1].retained == 0) { return YES; } else { ((struct obj_layout *)anObject)[-1].retained--; return NO; }}
dealloc实现:
* <p>* If you have allocated the memory using a non-standard mechanism, you* will not call the superclass (NSObject) implementation of the method* as you will need to handle the deallocation specially.<br />* In some circumstances, an object may wish to prevent itself from* being deallocated, it can do this simply be refraining from calling* the superclass implementation.* </p>*/- (void) dealloc{ NSDeallocateObject (self);}inline void NSDeallocateObject(id anObject) { struct obj_layout *o = &((struct obj_layout*)anObject)[-1]; free(o);}
在oc的对象中存有引用计数这一整数值。
调用alloc或者retain方法后,引用计数+1;
调用release后,引用计数-1;
引用计数值为0时,调用dealloc方法废弃对象。
分析其cocoa的实现:
在NSObject的alloc上下断点,可以看到调用函数:
+alloc
+allocWithZone:
class_createInstance
calloc
这里alloc类方法首先调用allocWithZone:类方法, 跟GNUstep相同。然后调用class_createInstance函数。最后用calloc来分配内存。
retainCount:
-retainCount
__CFDoExternRefOperation
CFBasicHashGetCountOfKey
retain:
-retain
__CFDoExternRefOperation
CFBasicHashAddValue
release:
-release
__CFDoExternRefOperation
CFBasicHashRemoveValue (CFBasicHashRemoveValue返回0时, -release调用dealloc)
int __CFDoExternRefOperation(uintptr_t op, id obj) { CFBasicHashRef table = init; int count; switch (op) { case OPERATION_retainCount; count = CFBasicHashGetCountOfKey(table, obj); return count; case OPERATION_retain: CFBasicHashAddValue(table, obj); return obj; case OPERATION_release: count = CFBasicHashRemoveValue(table, obj); return 0 == count; }}- (NSUInteger)retainCount { return (NSUInteger) __CFDoExternRefOperation(OPERATION_retainCount, self);}- (id)retain { return (id)__CFDoExternRefOperation(OPERATION_retain, self);}- (void)release { return __CFDoExternRefOperation(OPERATION_release, self);}
从函数中看出,apple用的是hash来管理引用计数。
来说一下两种内存管理:
1、通过内存块头部管理引用计数好处:
少量代码就能完成。
能够统一管理引用计数用内存块与对象用内存块。
2、通过引用计数表管理引用计数好处:
对象用内存块的分配无需考虑内存块头部。
引用计数表各项记录中存有内存块地址,可从各个记录追溯到各个对象的内存块。
在第二条中,追溯到内存块 在调试中是很重要的。只要引用计数表没有被破坏就能找到内存块的位置。
autorelease的实现: 类比c的作用域概念。
使用方法:1、生成并持有NSAutoreleasePool对象。
2、调用已分配对象的autorelease实例方法。
3、废弃NSAutoreleasePool对象。
在cocoa框架中,程序主循环的NSRunLoop或者在其他程序可运行的地方,对NSAutoreleasePool对象进行生成、持有和废弃处理。
当我们大量产生autorelease对象时,只要不废弃NSAutoreleasePool对象,那么生成的对象就不能被释放。有时候会产生内存不足的情况。
我们可以在必要的地方持有,废弃:
for (int i = 0; i < count; ++i) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; *** [pool drain];}
在NSObject中,aurelease是这样运作的:
- (id) autorelease { [NSAutoreleasePool addObject:self];}
addObject 是将对象连接上去,即GNUstep使用的是连接列表。 如NSMutableArray也是一样的。
apple对autorelea的实现:
class AutoreleasePoolPage { static inline void *push() { 生成或持有NSAutoreleasePool类对象。 } static inline void *pop(void *token) { 废弃NSAutoreleasePool类对象; releaseAll(); } static inline id autorelease(id obj) { 相当于NSAutoreleasePool类的addObject类方法。 } id *add(id obj) { 追加; } void releaseAll() { 调用内部数组中的对象的release实例方法。 }}void *objc_autoreleasePoolPush(void) { return AutoreleasePoolPage::push();}void objc_autoreleasePoolPop(void *ctxt) { AutoreleasePoolPage:pop(ctxt);}id *obj_autorelease( id obj) { return AutoreleasePoolPage::autorelease(obj);}
要注意的是当我们 pool autorelease会怎样?
这样做会发生异常, 在ob中,也就是foundation框架,无论调用那个对象的autorelease实例方法,实现上是调用的都是NSObject类的autorelease实例方法。
但是对于NSAutoreleasePool类,autorelease实例方法已经被该类重载了,所以出现了错误。
最后: 如何提高调用oc方法的速度
gnustep中,autorelease是用IMP(函数指针) Caching来实现的,他能高效地运行os x,ios应用程序频繁调用autorelease方法。
在方法调用时,为了解决类名、方法名以及取得方法运行时的函数指针,要在框架初始化时对其结果值进行缓存。
id autorelease_class [NSAutoreleasePool class];SEL autorelease_sel = @selector(addObject:);IMP autorelease_imp = [autorelease_class methodForSelector:autorelease_sel];实际:- (id)autorelease { (*autorelease_imp)(autorelease_class, autorelease_sel, self);}与- (id)autorelease { [NSAutoreleasePool addObject:self];}
作用相同,但是第一种方法运行效率会快2倍。 但是他依赖于运行环境。
-----2014、3、14 beijing
1 0
- 【IOS学习】之五:引用计数
- IOS 学习 之 引用计数
- iOS之引用计数
- ios学习笔记之OC篇(二):引用计数
- iOS 之ARC(自动引用计数)
- iOS内存管理之引用计数初识
- Swift学习之自动引用计数
- swift学习笔记之自动引用计数
- IOS引用计数
- 【读书笔记】iOS-引用计数
- iOS引用计数
- iOS对象引用计数
- 引用计数学习
- COM学习:引用计数
- iOS-学习笔记-内存管理 (1)自动引用计数
- iOS开发之ARC(自动引用计数)
- iOS开发之ARC(自动引用计数)
- iOS开发之ARC(自动引用计数)
- 从12306的余票查询谈数据库的承载问题
- eclipse远程调试Linux下tomcat
- yt-2488: 实数四舍五入
- Html5属性 align="center"无效问题
- hive优化方式和使用技巧
- 【IOS学习】之五:引用计数
- 同花顺扑克牌游戏-C++
- C++中主函数
- 自然数拆分
- linux下TIME_WAIT过多的解决办法(ip_local_port_range)(转)
- Oracle11g数据库导入Oracle10g数据库操作笔记
- PHP导出CSV文件
- 学习编程,不能止步,同时,也不能气馁!
- wifi共享精灵轻松搞定wifi网络