相信大家早已对这个问题烂熟于心,但还是带大家一起过一下.
在MRC时代,系统判定一个对象是否销毁是根据这个对象的引用计数器来判断的.其中每个对象被创建时引用计数都为1,每当对象被其他指针引用时,需要手动使用[obj retain];让该对象引用计数+1,当指针变量不在使用这个对象的时候,需要手动释放release这个对象。 让其的引用计数-1,当一个对象的引用计数为0的时候,系统就会销毁这个对象.总的来说在MRC模式下必须遵循谁创建,谁释放,谁引用,谁管理
如果在MRC下使用ARC:
在Build Phases的Compile Sources中选择需要使用MRC方式的.m文件,然后双击该文件在弹出的会话框中输入 -fobjc-arc
ARC自动内存管理:
WWDC2011和iOS5所引入自动管理机制——自动引用计数(ARC),它不是垃圾回收机制而是编译器的一种特性。ARC管理机制与MRC手动机制差不多,只是不再需要手动调用retain、release、autorelease;当你使用ARC时,编译器会在在适当位置插入release和autorelease;ARC时代引入了strong强引用来带代替retain,引入了weak弱引用.总结来说ARC是LLVM和Runtime配合的结果
在ARC下使用MRC方法:
在ARC工程中如果要使用MRC的需要在工程的Build Phases的Compile Sources中选择需要使用MRC方式的.m文件,然后双击该文件在弹出的会话框中输入 -fno-objc-arc
自动释放池始于MRC时代,主要是用于 自动 对 释放池内 对象 进行引用计数-1的操作,即自动执行release方法,在MRC中使用autoreleasepool必须在代码块内部手动为对象调用autorelease把对象加入到的自动释放池,系统会自动在代码块结束后,对加入自动释放池中的对象发送一个release消息.无需手动调用release.
我们在创建工程的时候默认创建的.m文件main函数有个autoreleasePool的创建,而我们自己的代码中也可以创建autoreleasePool 对象:
main函数里的:
#import <UIKit/UIKit.h> #import "AppDelegate.h" int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } } 复制代码
自己代码中的:
- (void)testConst{ @autoreleasepool { for (int i = 0; i<1000; i++) { NSString *str1 = [NSString stringWithFormat:@"%ld",i]; } } } 复制代码
在这段代码里会创建大量的临时变量(先不考虑代码合不合理哈),就会消耗过多的内存空间,所以在开发过程中,如果当遇到需要创建、使用大量的临时变量时,可以将相关的代码放在autoreleasePool中进行,当出了@autoreleasepool {},这些临时变量便会自动的进行释放.
我们想看下autoreleasePool的底层实现该如何?
一般我们想看系统底层实现,一般有两个途径:第一是LLVM源码分析 第二个就是clang-rewrite-objc 进行底层编译,现在我们先用第二种方式进行探究:
将.m文件 clang-rewrite-objc -o main.cpp
int main(int argc, char * argv[]) { /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))) ); } } 复制代码
我们看到了@autoreleasepool {} 底层编译成 __AtAutoreleasePool __autoreleasepool.
我们一起来看下__AtAutoreleasePool结构:
struct __AtAutoreleasePool { __AtAutoreleasePool() { atautoreleasepoolobj = objc_autoreleasePoolPush(); } ~__AtAutoreleasePool() { objc_autoreleasePoolPop(atautoreleasepoolobj); } void * atautoreleasepoolobj; }; 复制代码
原来__AtAutoreleasePool 是个结构体,结构体里有:
__AtAutoreleasePool() {
atautoreleasepoolobj = objc_autoreleasePoolPush();
}和
~__AtAutoreleasePool() {
objc_autoreleasePoolPop(atautoreleasepoolobj);
}这两个函数,其实__AtAutoreleasePool(){}是构造函数,而~__AtAutoreleasePool() {}这个是析构函数,其实也好理解当@autoreleasepool {}刚创建的时候底层会对atautoreleasepoolobj进行构造,当出了@autoreleasepool {} 方法区域的时候,底层会对atautoreleasepoolobj进行析构pop出去.
但是objc_autoreleasePoolPush、objc_autoreleasePoolPop 到底做了什么事呢?我们只有从源码中找到答案了,请看下面的分析 autoreleasePool的进阶.
想要了解底层,必须要有源码,首先我们在网上 http://www.opensource.apple.com/apsl/
里下一份objc的源码通过一些配置导入我们的工程.(最新的版本好像是objc4-779.1版本)
void * objc_autoreleasePoolPush(void) { return AutoreleasePoolPage::push(); } 复制代码
AutoreleasePoolPage 又是什么东西呢?点进去看看:
class AutoreleasePoolPage : private AutoreleasePoolPageData { . . . } 复制代码
原来AutoreleasePoolPage继承于AutoreleasePoolPageData, AutoreleasePoolPageData 结构如下:
struct AutoreleasePoolPageData { magic_t const magic; // 16 __unsafe_unretained id *next; //8 pthread_t const thread; // 8 AutoreleasePoolPage * const parent; //8 AutoreleasePoolPage *child; //8 uint32_t const depth; // 4 uint32_t hiwat; // 4 AutoreleasePoolPageData(__unsafe_unretained id* _next, pthread_t _thread, AutoreleasePoolPage* _parent, uint32_t _depth, uint32_t _hiwat) : magic(), next(_next), thread(_thread), parent(_parent), child(nil), depth(_depth), hiwat(_hiwat) { } }; 复制代码
原来AutoreleasePoolPageData是个结构体,里面有自己的一些成员变量,如:magic_t的结构体变量、next指针类型变量、depth、hiwat变量、还有parent、child指针成员变量,
其中指针类型占8个字节,uint32_t类型占4个字节,结构体类型取决于结构体本身的大小,比如magic_t结构体 ,里面是:
static const uint32_t M0 = 0xA1A1A1A1; # define M1 "AUTORELEASE!" static const size_t M1_len = 12; uint32_t m[4]; 复制代码
有的盆友会说到 int32_t M0、size_t M1_len 各占4个字节,但是忽略了一点:
static const,静态变量.静态变量类型数据大小是放在内存里的全局数据段里,不在堆区,
所以magic_t结构体所占的大小是uint32_t m[4]的大小,一个uint32_t 为四字节,整个就是 4*4为16个字节.
所以AutoreleasePoolPageData结构体自己变量共占 56个字节.各个变量所占大小已在上面标注,其中各个变量所代表的意思如下:
总结一下AutoreleasePoolPage其实就是一个双向链表结构,AutoreleasePoolPage(自动释放池页) 用来存放 autorelease 的对象,但是每一页的大小是有限制的,假如某个AutoreleasePoolPage页中需要存放的autorelease 的对象过多,一页存放不完,所以它就需要指向父结点点,在指向父结点里的AutoreleasePoolPage页中继续存放.
那么每一页大小时所少呢?
class AutoreleasePoolPage : private AutoreleasePoolPageData { friend struct thread_data_t; public: static size_t const SIZE = #if PROTECT_AUTORELEASEPOOL PAGE_MAX_SIZE; // must be multiple of vm page size #else PAGE_MIN_SIZE; // size and alignment, power of 2 . . . #endif }复制代码
有个PAGE_MAX_SIZE,点击进去是4096.原来每一页AutoreleasePoolPage可以存放4096个字节.一共4096个字节, (4096 - AutoreleasePoolPage 中自己成员变量所占的字节)/每个对象中所占的字节. (4096 - 56)/8 = 505. 好的,每一AutoreleasePoolPage可以存放505个对象.下面把AutoreleasePoolPage 结构图放在下面,仅供大家参考.
结构弄清楚之后,我们继续跟代码:
void * objc_autoreleasePoolPush(void) { return AutoreleasePoolPage::push(); } 复制代码
static inline void *push() { id *dest; if (slowpath(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; } 复制代码
程序代码回来到else的判断里, dest = autoreleaseFast(POOL_BOUNDARY);这个POOL_BOUNDARY 就相当于 AutoreleasePoolPage 里的边界,一些技术书籍里也称作是哨兵对象,这个对象很有用的,等下就来看苹果为何这么设计:
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); } } 复制代码
之前通过分析代码一进入这个 @autoreleasepool {}时,就会调用objc_autoreleasePoolPush方法,接着来到了 autoreleaseFast 方法,AutoreleasePoolPage *page = hotPage();取的此时的page,因为第一次创建 page肯定取不到,所以会来到autoreleaseNoPage(obj)方法,
id *autoreleaseNoPage(id obj) { ASSERT(!hotPage()); bool pushExtraBoundary = false; if (haveEmptyPoolPlaceholder()) { pushExtraBoundary = true; } else if (obj != POOL_BOUNDARY && DebugMissingPools) { _objc_inform("MISSING POOLS: (%p) Object %p of class %s " "autoreleased with no pool in place - " "just leaking - break on " "objc_autoreleaseNoPool() to debug", objc_thread_self(), (void*)obj, object_getClassName(obj)); objc_autoreleaseNoPool(obj); return nil; } else if (obj == POOL_BOUNDARY && !DebugPoolAllocation) { return setEmptyPoolPlaceholder(); } // Install the first page. AutoreleasePoolPage *page = new AutoreleasePoolPage(nil); setHotPage(page); if (pushExtraBoundary) { page->add(POOL_BOUNDARY); } return page->add(obj); } 复制代码
一步步执行代码发现 会来到:
// Install the first page. AutoreleasePoolPage *page = new AutoreleasePoolPage(nil); setHotPage(page); if (pushExtraBoundary) { page->add(POOL_BOUNDARY); } return page->add(obj);复制代码
创建个new AutoreleasePoolPage,并且setHotPage,紧接着将我们的哨兵对象加入到AutoreleasePoolPage.也就是page->add(POOL_BOUNDARY);操作.
刚刚我们在3.1的时候说到,@autoreleasepool {} 会创建个new AutoreleasePoolPage,这个AutoreleasePoolPage 会将POOL_BOUNDARY 添加进来,那么什么时候会添加自动释放的对象呢?
肯定是当某个对象调用 autorelease方法 的时候,AutoreleasePoolPage 会把调用的对象加进来.
int main(int argc, const char * argv[]) { @autoreleasepool { // insert code here... // 1 + 504 + 505 + 505 NSObject *objc = [[NSObject alloc] autorelease]; NSLog(@"objc = %@",objc); }复制代码
回来到底层objc:
objc_autorelease(id obj) { if (!obj) return obj; if (obj->isTaggedPointer()) return obj; return obj->autorelease(); } 复制代码
objc_object::autorelease() { ASSERT(!isTaggedPointer()); if (fastpath(!ISA()->hasCustomRR())) { return rootAutorelease(); } return ((id(*)(objc_object *, SEL))objc_msgSend)(this, @selector(autorelease)); } 复制代码
inline id objc_object::rootAutorelease() { if (isTaggedPointer()) return (id)this; if (prepareOptimizedReturn(ReturnAtPlus1)) return (id)this; return rootAutorelease2(); } 复制代码
objc_object::rootAutorelease2() { ASSERT(!isTaggedPointer()); return AutoreleasePoolPage::autorelease((id)this); } 复制代码
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; } 复制代码
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); } } 复制代码
终于来到这里了,AutoreleasePoolPage *page = hotPage(); 获取page,在3.1的时候page已经创建了,所以他回来到 if (page && !page->full()) ,看看本页(AutoreleasePoolPage)有没有满,没满的话直接添加 page->add(obj);如果满的话来到autoreleaseFullPage(obj, page);
id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page) { ASSERT(page == hotPage()); ASSERT(page->full() || DebugPoolAllocation); do { if (page->child) page = page->child; else page = new AutoreleasePoolPage(page); } while (page->full()); setHotPage(page); return page->add(obj); } 复制代码
首先来到这里是个do-while 循环,while (page->full()); 这个方法就是因为之前的那个page满了才会来到这里,所以这个条件必然满足.然后进行判断 page是否有子节点了,如果有的话直接用子节点的page,如果没有的话那只能新创建个page了,然后新创建的page 父节点指向原来的节点.最后page->add(obj).
void objc_autoreleasePoolPop(void *ctxt) { AutoreleasePoolPage::pop(ctxt); } 复制代码
static inline void pop(void *token) { AutoreleasePoolPage *page; id *stop; if (token == (void*)EMPTY_POOL_PLACEHOLDER) { // Popping the top-level placeholder pool. page = hotPage(); if (!page) { // Pool was never used. Clear the placeholder. return setHotPage(nil); } // Pool was used. Pop its contents normally. // Pool pages remain allocated for re-use as usual. page = coldPage(); token = page->begin(); } else { page = pageForPointer(token); } stop = (id *)token; if (*stop != POOL_BOUNDARY) { // 第一个节点 - 没有父节点 if (stop == page->begin() && !page->parent) { // Start of coldest page may correctly not be POOL_BOUNDARY: // 1. top-level pool is popped, leaving the cold page in place // 2. an object is autoreleased with no pool } else { // Error. For bincompat purposes this is not // fatal in executables built with old SDKs. return badPop(token); } } if (slowpath(PrintPoolHiwat || DebugPoolAllocation || DebugMissingPools)) { return popPageDebug(token, page, stop); } return popPage<false>(token, page, stop); } 复制代码
这里面:
stop = (id *)token; if (*stop != POOL_BOUNDARY) { }复制代码
进行判断,pop什么时候结束,肯定是自动释放池页pop到那个哨兵对象的时候才算全部释放完,所以*stop != POOL_BOUNDARY,除非是坏节点,要不然*stop == POOL_BOUNDARY,
紧接着popPage<false>(token, page, stop);
popPage(void *token, AutoreleasePoolPage *page, id *stop) { if (allowDebug && PrintPoolHiwat) printHiwat(); page->releaseUntil(stop); // memory: delete empty children if (allowDebug && DebugPoolAllocation && page->empty()) { AutoreleasePoolPage *parent = page->parent; page->kill(); setHotPage(parent); } else if (allowDebug && DebugMissingPools && page->empty() && !page->parent) { // special case: delete everything for pop(top) // when debugging missing autorelease pools page->kill(); setHotPage(nil); } else if (page->child) { // hysteresis: keep one empty child if page is more than half full if (page->lessThanHalfFull()) { page->child->kill(); } else if (page->child->child) { page->child->child->kill(); } } } 复制代码
将自动释放池页 kill,包括子页child,父页parent 都进行kill.使所有的autorlease对象都进行释放.
在APP中,整个主线程是运行在一个自动释放池中的。
main函数中的自动释放池的作用:这个池块给出了一个pop点来显式的告诉我们这里有一个释放点,如果你的main在初始化的过程中有别的内容可以放在这里。
使用@autoreleasepool
标记,调用push()方法。
没有hotpage,调用
()
,设置EMPTY_POOL_PLACEHOLDER
。
因为设置了EMPTY_POOL_PLACEHOLDER
,所以会设置本页为hotpage
,添加边界标记POOL_BOUNDARY
,最后添加obj。
继续有对象调用autorelease
,此时已经有了page,调用page->add(obj)
。
如果page满了,调用autoreleaseFullPage()
创建新page,重复第6点。
到达autoreleasePool边界,调用pop方法,通常情况下会释放掉POOL_BOUNDARY
之后的所有对象