Maco

闭花羞月

嗨,我是张志方 (@Maco),一名 iOS 开发者。


MRC内存管理

废话不多说,赞么直奔主题.

1.0 苹果的实现

NSObject内存申请过程

+alloc
+allocWithZone
class_createInstance
calloc 	//分配内存块

1.1 autorelease

对象当超出其作用域时,对象实例的release实例方法被调用。

使用方法:

1、生成并持有NSAutoreleasePool对象

2、调用已分配对象的autorelease实例方法

3、废弃NSAutoreleasePool对象

对于所有在autoreleasePool对象的作用域中的对象,在废弃autoreleasePoll对象时,所有调用autorelease的实例对象都会调用release方法。

在Cocoa框架中,相当于程序住循环的NSRunLoop或者在其他程序可运行的地方,对NSAutoreleasePool对象进行生成、持有和废弃处理。

当在一个循环中产生大量的autorelease对象,内存占用很大的时候,可以使用autoreleasePool对象。

1.1.1 autorelease实现

[obj autorelease]; 本质就是调用 [NSAutoreleasePoll addObject:self];

苹果实现

源码是在runtime/NSObject.mm文件夹下,autoreleasePool内部是使用链表实现的,每个节点是autoreleasePoolPage,使用POOL_BOUNDARY作为每个节点中保存数据的边界,当数组中满的时候后,会新生成一个autoreleasePoolPage节点,使当前节点的child指向新创建的page,看下autoreleasePoolPage结构:

class AutoreleasePoolPage 
{
#   define EMPTY_POOL_PLACEHOLDER ((id*)1)
#   define POOL_BOUNDARY nil		//边界
    static pthread_key_t const key = AUTORELEASE_POOL_KEY;
    static uint8_t const SCRIBBLE = 0xA3;  // 0xA3A3A3A3 after releasing
    static size_t const SIZE = 
#if PROTECT_AUTORELEASEPOOL
        PAGE_MAX_SIZE;  // must be multiple of vm page size
#else
        PAGE_MAX_SIZE;  // size and alignment, power of 2
#endif
    static size_t const COUNT = SIZE / sizeof(id);//保存数据大小
    magic_t const magic;					//验证当前poll结构是否完整
    id *next;
    pthread_t const thread;					//当前所在线程
    AutoreleasePoolPage * const parent;	//父节点
    AutoreleasePoolPage *child;			//子节点
    uint32_t const depth;
    uint32_t hiwat;
}

主要方法

//新建autoreleasePoolPage

AutoreleasePoolPage(AutoreleasePoolPage *newParent) 
        : magic(), next(begin()), thread(pthread_self()),
          parent(newParent), child(nil), 
          depth(parent ? 1+parent->depth : 0), 
          hiwat(parent ? parent->hiwat : 0)
    { 
        if (parent) {
            parent->check();
            assert(!parent->child);
            parent->unprotect();
            parent->child = this;
            parent->protect();
        }
        protect();
  }
//添加对象
id *add(id obj)
    {
        assert(!full());
        unprotect();
        id *ret = next;  // faster than `return next-1` because of aliasing
        *next++ = obj;
        protect();
        return ret;
    }
    
//释放所有对象
    void releaseAll() 
    {
        releaseUntil(begin());
    }
void releaseUntil(id *stop) { }

//获取当前正在使用的page
static inline AutoreleasePoolPage *hotPage() 
    {
        AutoreleasePoolPage *result = (AutoreleasePoolPage *)
            tls_get_direct(key);
        if ((id *)result == EMPTY_POOL_PLACEHOLDER) return nil;
        if (result) result->fastcheck();
        return result;
    }

//设置当前page
    static inline void setHotPage(AutoreleasePoolPage *page) 
    {
        if (page) page->fastcheck();
        tls_set_direct(key, (void *)page);
    }
static inline id *autoreleaseFast(id obj)
    {
        AutoreleasePoolPage *page = hotPage();
        if (page && !page->full()) {
            return page->add(obj);
        } else if (page) {
            return autoreleaseFullPage(obj, page);
        } else {
            return autoreleaseNoPage(obj);
        }
    }

public: static inline id autorelease(id obj)
    {
        assert(obj);
        assert(!obj->isTaggedPointer());
        id *dest __unused = autoreleaseFast(obj);
        assert(!dest  ||  dest == EMPTY_POOL_PLACEHOLDER  ||  *dest == obj);
        return obj;
    }

//往当前page中添加新对象
static inline void *push() 
    {
        id *dest;
        if (DebugPoolAllocation) {
            // Each autorelease pool starts on a new pool page.
            dest = autoreleaseNewPage(POOL_BOUNDARY);
        } else {
            dest = autoreleaseFast(POOL_BOUNDARY);
        }
        assert(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
        return dest;
    }

//pop对象,销毁
static inline void pop(void *token) 
    { }

```c s NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

//等同于 objc_autoreleasePoolPush();

Person *p = [[Person alloc] init];

[p autorelease];// 等同于 objc_autorelease(p);

p.name = @””;

[pool drain];//相当于release

//等同于 objc_autoreleasePoolPop(pool); ```

对pool不能进行autorelease操作,否则发生异常,autoreleasePool重载了autorelease方法,会运行时就出错。

最近的文章

Cookie的深入理解以及在开发中的作用

cookie的起源早期Web开发面临的最大问题之一是如何管理状态。简言之,服务器端没有办法知道两个请求是否来自于同一个浏览器。那时的办法是在请求的页面中插入一个token,并且在下一次请求中将这...…

继续阅读

更早的文章

ARC内存管理规则

现阶段移动端iOS开发基本都是使用arc模式进行开发,但是在有的情况下也是需要使用MRC的,比如在使用系统没有加入ARC管理内存的类的时候,比如使用CoreFoundation下的文件就需要我们...…

继续阅读