相信你们早已对这个问题烂熟于心,但仍是带你们一块儿过一下.bash
在MRC时代,系统断定一个对象是否销毁是根据这个对象的引用计数器来判断的.其中每一个对象被建立时引用计数都为1,每当对象被其余指针引用时,须要手动使用[obj retain];让该对象引用计数+1,当指针变量不在使用这个对象的时候,须要手动释放release这个对象。 让其的引用计数-1,当一个对象的引用计数为0的时候,系统就会销毁这个对象.总的来讲在MRC模式下必须遵循谁建立,谁释放,谁引用,谁管理
app
若是在MRC下使用ARC:
在Build Phases的Compile Sources中选择须要使用MRC方式的.m文件,而后双击该文件在弹出的会话框中输入 -fobjc-arc
less
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.ui
咱们在建立工程的时候默认建立的.m文件main函数有个autoreleasePool的建立,而咱们本身的代码中也能够建立autoreleasePool 对象:this
main函数里的:spa
#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 {},这些临时变量便会自动的进行释放.debug
咱们想看下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
以后的全部对象