IOS NSObject Alloc

391 阅读2分钟

最近读《Objective-C高级编程》其中一章介绍alloc实现,读后将两种alloc实现要点简化记录如下:

GNUstep(Cocoa互换框架 )

NSZone是为了防止内存碎片化而引入的结构,通过使用对象的目的,对象大小分配内存,提高效率.苹果官方文档说明, 现在的运行时系统内存管理已极具效率,使用NSZone反而效率低下源码复杂.

alloc简化版,引用计数信息写入对象内存头部

 struct obj_layout{
        NSUinteger retained;
    };

    +(id)alloc{
        int size = sizeof(stuct obj_layout)+对象大小
        struct obj_layout *p = (struct obj_layout*) calloc(1,size);
        return(id)(p+1);
    }

    -(void)release{
        if(decrementeExtraRefCountWasZero(self))
        [self dealloc];
    }

    -(void)dealloc{
        struct obj_layout*p = & ((struct obj_layout *)self)[-1];
        free(p);
    }

Q:当retained变量超出最大值时会发生什么?
A:retain方法会抛出异常

Apple实现

 static struct {
 CFSpinLock_t lock;
 CFBasicHashRef table;
 uint8_t padding[64 - sizeof(CFBasicHashRef) - sizeof(CFSpinLock_t)];

 } __NSRetainCounters[NUM_EXTERN_TABLES];

 CF_EXPORT uintptr_t __CFDoExternRefOperation(uintptr_t op, id obj) {
     if (nil == obj) HALT;
     uintptr_t idx = EXTERN_TABLE_IDX(obj);
     uintptr_t disguised = DISGUISE(obj);
     CFSpinLock_t *lock = &__NSRetainCounters[idx].lock;
     CFBasicHashRef table = __NSRetainCounters[idx].table;
     uintptr_t count;
     switch (op) {
         case 300:   // increment
         case 350:   // increment, no event
             __CFSpinLock(lock);
             CFBasicHashAddValue(table, disguised, disguised);
             __CFSpinUnlock(lock);
             if (__CFOASafe && op != 350) __CFRecordAllocationEvent(__kCFObjectRetainedEvent, obj, 0, 0, NULL);
             return (uintptr_t)obj;
     case 400:   // decrement
         if (__CFOASafe) __CFRecordAllocationEvent(__kCFObjectReleasedEvent, obj, 0, 0, NULL);
     case 450:   // decrement, no event
         __CFSpinLock(lock);
         count = (uintptr_t)CFBasicHashRemoveValue(table, disguised);
         __CFSpinUnlock(lock);
         return 0 == count;
     case 500:
         __CFSpinLock(lock);
         count = (uintptr_t)CFBasicHashGetCountOfKey(table, disguised);
         __CFSpinUnlock(lock);
         return count;
     }
     return 0;

 }

__CFDoExternRefOperation 按照retainCount/retain/release 调用不同的函数.alloc通过多个散列表来管理引用计数,对每个引用计数表的访问都需要配合spinlock. CFBasicHashRef table = __NSRetainCounters[idx].table; 从该行代码看,APPLE是通过多个散列表来管理引用计数,每个NSRetainCounters结构体里有spinlock和table,执行引用计数操作时是线程安全的.

两种实现对比

GNUstep实现简单高效,代码少, 内存块需考虑头部(指针地址偏移);
苹果的实现较为复杂通过引用计数表查到内存块.
苹果实现好处是:方便调试,即使对象内存块损坏, 只要引用计数表没有被破话, 就能够确认内存块位置; 在检测内存泄漏时,有助于检测各对象的持有者是否存在.