基本数据类型、alloc/init、new、copy初始化的对象以及isTaggedPointer对象的创建并不会引起内存的增加,这是因为自动释放池不会对这些方式创建的变量进行管理。基本数据类型的局部变量在栈区,由系统进行管理,alloc/init、new、copy的初始化的对象由ARC管理,isTaggedPointer小对象类型无需对内存进行管理,它的值存在指针中。除了以上提到的小对象和alloc/init、new、copy初始化的对象以外的其他对象都可以由自动释放池进行内存的管理.
**
当使用alloc/new/copy/mutableCopy开始的方法进行初始化时,会生成并持有对象(也就是不需要pool管理,系统会自动的帮他在合适位置release)
例如: NSObject *stu = [[NSObject alloc] init];
那么对于其他情况,例如
id obj = [NSMutableArray array];
这种情况会自动将返回值的对象注册到autorealeasepool,代码等效于:
@autorealsepool{
id __autorealeasing obj = [NSMutableArray array];
}
作者:Joy___
链接:https://www.jianshu.com/p/e3690f3e4675
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
**
下文中提到的栈是指数据结构--先进后出
什么是自动释放池
OC中的一种内存自动回收机制,它可以延迟加入AutoreleasePool中的变量release的时机,即当我们创建了一个对象,并把他加入到了自动释放池中时,他不会立即被释放,会等到一次runloop结束或者作用域超出{}或者超出[pool release]之后再被释放
自动释放池的使用就是将作用域中的代码包含在__AtAutoreleasePool的构造函数和析构函数中,由atautoreleasepoolobj对象对作用域的内存进行管理.
由AutoreleasePoolPage的数据结构我们可以得出,autoreleasepool是一个以AutoreleasePoolPage分页管理的双向链表。
总结
自动释放池autorelease是一个以AutoreleasePoolPage为页,进行分页管理的双向链表;页的数据结构是AutoreleasePoolPageData;每一个autoreleasepool对象只有一个哨兵(POOL_BOUNDARY),哨兵放在第一页中;每一页的大小为4096字节;每一页的前56个字节存储页的AutoreleasePoolPageData结构体数据;第一页的第56往后8个字节存储哨兵,后面存储autorelease对象,总共可以存储504个;从第二页开始,每页可以存储505个对象;objc_autoreleasepoolpush是一个查找child,递增next,创建新页的过程;objc_autoreleasepoolpop是一个查找parent,递减next,释放对象,销毁page的过程,遇到哨兵对象即停止。
POOL_BOUNDARY:哨兵,可以理解为一个标记,释放池最后一位,释放到该位置时说明对象已经释放完成。
POOL_BOUNDARY 插入的位置:
每次 @autoreleasepool{} 一开始都会最终调用 AutoreleasePoolPage 的 push 方法,则每次都会入栈一个POOL_BOUNDARY哨兵对象用来标记 autoreleasepool 的起始位置。
作者:6ffd6634d577
链接:https://www.jianshu.com/p/416f1c3d24ae
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
int main(){
@autoreleasepool {
NSObject *obj = [[NSObject alloc] init];
}
}
上面代码实际可以认为转化为下面的代码进行执行
int main(){
{
void * atautoreleasepoolobj;
// objc_autoreleasePoolPush()第一个内容就是哨兵对象,即atautoreleasepoolobj 哨兵地址
atautoreleasepoolobj = objc_autoreleasePoolPush();
NSObject *obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
// 释放对象时到哨兵地址结束。
objc_autoreleasePoolPop(atautoreleasepoolobj)
}
}
自动释放池什么时候创建(主线程),什么时候销毁?(ARC)
分两种情况:
如果不是使用autoreleasepool,
NSObject *obj = [[NSObject alloc] init];
则是在出了作用域就释放
如果是
@autoreleasepool {
//对象
}
则是
每一次运行循环执行后,也就是每当事件被触发时都会创建自动释放池。在程序执行的过程中,所有autorelease的对象在出了作用域之后会被添加到最近创建的自动释放池中。运行循环结束前会释放自动释放池,还有池子满了也会销毁。
Runloop和Autorelease
iOS 在主线程的Runloop 注册了2个Observer(还有其他的Observer)
一个Observer监听kCFRunloopEntry事件,会调用objc_autoreleaePoolPush()
另外Observer一个监听两个事件:
kCFRunloopBeforeWaiting事件,会调用objc_autoreleaePoolPop()、objc_autoreleaePoolPush(),
kCFRunloopBeforeExit事件,会调用objc_autoreleaePoolPop()
都说子线程不主动获取没有runloop,而每个runloop都会在开始创建一个自动释放池,那没有runloop,子线程是否有自动释放池呢?比如下面这个
那么,
- 子线程是否有自动释放池呢
在子线程中原本是没有自动释放池的,但是如果有runloop或者autorelease对象的时候,就会自动的创建自动释放池。
苹果官方文档中有介绍,每一个线程都会维护自己的Autoreleasepool 栈,所以子线程虽然默认没有开启Runloop,但是依然存在
Autoreleasepool,在子线程退出的时候会去释放autorelease 对象。
如果子线程中没有创建Autoreleasepool,而一旦产生Autorelease对象,就会自动创建AutoreleasePoolPage页,并将对象加入到其栈中,所以一般情况下,子线程中即使不手动添加自动释放池,也不会产生内存泄露。
这篇文章Autorelease 对象的内存管理,作者给出了明确的答案: OS X 10.9+和 iOS 7+不会造成内存泄露,当子线程未开启runloop的时候而你用到了autorelease对象会调用 autoreleaseNoPage 方法,这个方法会为你创建一个hotpage(可以理解为当前正在使用的autoreleasePoolPage),并调用page->add(obj)将autorelease对象加入到自动释放池中,这样就不会造成内存泄露了!
这个是autoreleaseNoPage的源码:
————————————————
版权声明:本文为CSDN博主「不在下雪天」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lixuezhi86/article/details/81813926
- 这个obj究竟在什么时候释放呢?
每个autorelease创建的时候都会监听当前线程的销毁方法,在线程退出时调用tls_dealloc方法。
然后这个tls_dealloc会调用autoreleasePoolPop清楚所有autorelease对象。 - 是在子线程销毁后释放,还是与子线程的生命周期无关?
子线程销毁后
- Autoreleasepool 与 Runloop 的关系
- ARC 下什么样的对象由 Autoreleasepool 管理
- 子线程默认不会开启 Runloop,那出现 Autorelease 对象如何处理?不手动处理会内存泄漏吗?
针对第一个问题,比较容易理解,可以看一下:ibireme 的 深入理解RunLoop,主线程默认为我们开启 Runloop,Runloop 会自动帮我们创建Autoreleasepool,并进行Push、Pop 等操作来进行内存管理
第二个问题,ARC 下什么样的对象由 Autoreleasepool 管理呢?大多数人的回答是:“都会由 pool 进行管理”。其实并不是这样的,对于普通的对象是由编译器在合适的地方为我们 Realease 了。针对这个问题,我已经总结过:引用计数带来的一次讨论,是参考了经典的《iOS与OS X多线程和内存管理 》这本书。
针对第三个问题,感觉比较难以回答,需要很细致的读过 Runtime 、Autoreleasepool 的源码才可以。我也是参考了 StackOverFlow 的回答:does NSThread create autoreleasepool automaticly now?。我再来简单阐述下,在子线程你创建了 Pool 的话,产生的 Autorelease 对象就会交给 pool 去管理。如果你没有创建 Pool ,但是产生了 Autorelease 对象,就会调用 autoreleaseNoPage 方法。在这个方法中,会自动帮你创建一个 hotpage(hotPage 可以理解为当前正在使用的 AutoreleasePoolPage,如果你还是不理解,可以先看看 Autoreleasepool 的源代码,再来看这个问题 ),并调用 page->add(obj)
将对象添加到 AutoreleasePoolPage 的栈中,也就是说你不进行手动的内存管理,也不会内存泄漏啦!StackOverFlow 的作者也说道,这个是 OS X 10.9+和 iOS 7+ 才加入的特性。并且苹果没有对应的官方文档阐述此事,但是你可以通过源码了解。这里张贴部分源代码:
static __attribute__((noinline))
id *autoreleaseNoPage(id obj)
{
// No pool in place.
// hotPage 可以理解为当前正在使用的 AutoreleasePoolPage。
assert(!hotPage());
// POOL_SENTINEL 只是 nil 的别名
if (obj != POOL_SENTINEL && DebugMissingPools) {
// We are pushing an object with no pool in place,
// and no-pool debugging was requested by environment.
_objc_inform("MISSING POOLS: Object %p of class %s "
"autoreleased with no pool in place - "
"just leaking - break on "
"objc_autoreleaseNoPool() to debug",
(void*)obj, object_getClassName(obj));
objc_autoreleaseNoPool(obj);
return nil;
}
// Install the first page.
// 帮你创建一个 hotpage(hotPage 可以理解为当前正在使用的 AutoreleasePoolPage
AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
setHotPage(page);
// Push an autorelease pool boundary if it wasn't already requested.
// POOL_SENTINEL 只是 nil 的别名,哨兵对象
if (obj != POOL_SENTINEL) {
page->add(POOL_SENTINEL);
}
// Push the requested object.
// 把对象添加到 自动释放池 进行管理
return page->add(obj);
}
作者:Joy___
链接:https://www.jianshu.com/p/f87f40592023
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
需要手动添加AutoreleasePool的情况
. 编写的不是基于UI框架的程序,例如命令行工具;
. 通过循环方式创建的大量的临时对象;
. 使用非Cocoa 程序创建的子线程。
什么对象会加入自动释放池
alloc、new、copy、mutableCopy等持有对象的方法,不会加入到autoreleasepool;而其他不持有对象的方法则通过objc_autoreleaseReturnValue和objc_retainAutoreleaseReturnValue来判断是否需要加入到autoreleasepool,这是编译器的优化。
iOS5及之后,关键字__weak修饰的对象,直接调用release,不会加入autoreleasepool。
id指针(id )和对象指针(如NSError*),会自动加上关键字__autoreleasing,加入到autoreleasepool。
使用容器的block版本的枚举器时,内部会自动添加一个autoreleasepool:
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
// 这里被一个局部@autoreleasepool包围着
}];
当然,在普通for循环和for in循环中没有,for循环中遍历会产生大量autorelease变量时,就需要手动添加局部autoreleasepool。
不错的博客:
https://www.jianshu.com/p/7bd2f85f03dc
作者:boy丿log
链接:https://www.jianshu.com/p/d891f13d92ca
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
参考:https://zhuanlan.zhihu.com/p/321687906
作者:赵哥窟
链接:https://www.jianshu.com/p/9dad9c3247ed
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。