微信扫一扫关注公个人众号了解更多iOS知识、提高工做技能、大厂内推:bash
当你须要延迟调用release方法的时候会使用autorelease。如:微信
- (NSString *)fullName {
NSString *string = [[[NSString alloc] initWithFormat:@"%@ %@",
self.firstName, self.lastName] autorelease];
return string;
}
复制代码
在上面代码中,经过alloc生成并持有对象,根据iOS内存管理规则,在失去对象的引用以前,咱们必需要放弃该对象的全部权。若是使用release,那么对象在返回以前就会失去全部权,致使返回一个无用的对象。使用autorelease,会保证调用放在使用该返回值以前,该对象不会释放,也就是说,autorelease能够保证对象在跨越“方法调用边界”后存活。框架
这时你可能会有一个疑问,使用autorelease的对象到底在何时释放呢?接下来,咱们将会围绕这个问题逐步进行研究。less
Coco框架会帮咱们维护一个autorelease pool用来将对象临时保存在内存中,以便稍后释放。函数
iOS应用程序是在event loop(事件循环)中运行的。当应用程序加载时,程序就会进入事件循环并等待用户的交互事件。当一个点击事件发生时,Cocoa框架会检测到该事件,而后建立事件对象,并生成一个autorelease pool对象。整个流程以下表所示: oop
经过查看Objc源码,来确认autorlease的实现:优化
- (id)autorelease {
return ((id)self)->rootAutorelease();
}
inline id objc_object::rootAutorelease()
{
assert(!UseGC);
if (isTaggedPointer()) return (id)this;
if (prepareOptimizedReturn(ReturnAtPlus1)) return (id)this;
return rootAutorelease2();
}
id objc_object::rootAutorelease2()
{
assert(!isTaggedPointer());
return AutoreleasePoolPage::autorelease((id)this);
}
复制代码
经过源码能够发现,当调用对象的autorelease方法时,最终调用的是AutoreleasePoolPage的autorelease方法。ui
接下来咱们看一下AutoreleasePoolPage的实现,this
AutoreleasePoolPage是使用C++实现的一个类,截取源码中的主要部分,其实现以下:spa
class AutoreleasePoolPage
{
#define POOL_SENTINEL 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;
id *next;
pthread_t const thread;
AutoreleasePoolPage * const parent;
AutoreleasePoolPage *child;
uint32_t const depth;
uint32_t hiwat;
public:
static inline id autorelease(id obj)
{
AutoreleasePoolPage *page = hotPage();
if (page && !page->full()) {
return page->add(obj);
} else if (page) {
return autoreleaseFullPage(obj, page);
} else {
return autoreleaseNoPage(obj);
}
}
static inline void *push()
{
id *dest;
if (DebugPoolAllocation) {
// Each autorelease pool starts on a new pool page.
dest = autoreleaseNewPage(POOL_SENTINEL);
} else {
dest = autoreleaseFast(POOL_SENTINEL);
}
assert(*dest == POOL_SENTINEL);
return dest;
}
static inline void pop(void *token)
{
AutoreleasePoolPage *page;
id *stop;
page = pageForPointer(token);
stop = (id *)token;
if (DebugPoolAllocation && *stop != POOL_SENTINEL) {
// This check is not valid with DebugPoolAllocation off
// after an autorelease with a pool page but no pool in place.
_objc_fatal("invalid or prematurely-freed autorelease pool %p; ",
token);
}
if (PrintPoolHiwat) printHiwat();
page->releaseUntil(stop);
// memory: delete empty children
if (DebugPoolAllocation && page->empty()) {
// special case: delete everything during page-per-pool debugging
AutoreleasePoolPage *parent = page->parent;
page->kill();
setHotPage(parent);
} else if (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();
}
}
}
id *add(id obj)
{
assert(!full());
unprotect();
id *ret = next;
*next++ = obj;
protect();
return ret;
}
void releaseAll()
{
releaseUntil(begin());
}
}
复制代码
经过分析上面的源码,咱们能够了解到对象调用autorelease方法的过程以下:
在ARC下,咱们使用@autoreleasepool{}来使用AutoreleasePool:
@autoreleasepool {
}
复制代码
使用clang(clang -rewrite-objc)命令将其转化成C++代码,关键代码以下:
extern "C" __declspec(dllimport) void * objc_autoreleasePoolPush(void);
extern "C" __declspec(dllimport) void objc_autoreleasePoolPop(void *);
struct __AtAutoreleasePool {
__AtAutoreleasePool()
{
atautoreleasepoolobj = objc_autoreleasePoolPush();
}
~__AtAutoreleasePool()
{
objc_autoreleasePoolPop(atautoreleasepoolobj);
}
void * atautoreleasepoolobj;
};
复制代码
所以咱们能够得出结论:Autorelease Pool的生命周期是由 void * objc_autoreleasePoolPush(void)和void objc_autoreleasePoolPop(void *)这两个方法实现的。
经过查看Objc源码,肯定这两个方法的实现:
void *objc_autoreleasePoolPush(void)
{
if (UseGC) return nil;
return AutoreleasePoolPage::push();
}
void objc_autoreleasePoolPop(void *ctxt)
{
if (UseGC) return;
AutoreleasePoolPage::pop(ctxt);
}
复制代码
经过源码,咱们能够肯定,void * objc_autoreleasePoolPush(void)和 void objc_autoreleasePoolPop(void *)这两个方法是对AutoreleasePoolPage::push()和AutoreleasePoolPage::pop(ctxt)方法的封装。
经过分析AutoreleasePoolPage源码中push()和pop()方法的实现,咱们能够知道AutoreleasePool对象的生命周期
push()过程:
**注意:**每调用一次push()操做都会建立一个新的autoreleasepool对象,即往AutoreleasePoolPage中插入一个POOL_SENTINEL,而且返回插入的 POOL_SENTINEL 的内存地址。
pop()过程:
###ARC下autorelease优化策略
先看一下,ARC下,调用NSMutableArray类的array方法时,编译器为咱们作了哪些操做
+ (id)array {
id obj = objc_msgSend(NSMutableArray, @selector(alloc));
objc_msgSend(obj, @selector(init));
return objc_autoreleaseReturnValue(obj);
}
id obj = objc_retainAutoreleasedReturnValue([NSMutableArray array]);
复制代码
objc_autoreleaseReturnValue函数会检查使用该函数的方法或函数调用方的执行命令列表,若是方法或函数的调用方在调用了方法或函数后紧接着调用objc_retainAutoreleasedReturnValue()函数,那么就不将返回的对象注册到autoreleasepool中,而是直接传递到方法或函数的调用方。
使用容器的block版本的枚举器时,内部会自动添加一个AutoreleasePool
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
// 这里被一个局部@autoreleasepool包围着
}];
复制代码
在普通for循环和for in循环中没有。for循环和for in循环中遍历产生大量autorelease变量时,就须要手加局部 AutoreleasePool啦。