RunLoop与AutoReleasepool的关系学习

概念的陈述

autoReleasepool是我们iOS开发项目中及其重要的一个内存管理机制,自从我们是用ARC后,我们再也不用开启一个内容分配给某个对象,我们只管做我们开发应该做的事情,内存的分配和释放都交给autoReleasepool去处理,这样既方便也省事,所以autoReleasepool的概念就不多叙述了,自动内存释放池,相信只要做iOS开发的都能明白其作用的重要性。

AutoReleasepool的内部结构

 magic_t const magic;                      magic_t const magic; 
  id *next;                                 id *next;  
 .....                                      NSString *string49;
 NSString *...;                             NSString *string50;
 NSString *...;                             pthread_t const thread;
 NSString *string3;                         ......       
 NSString *string2;                         ......
 NSString *string1;
  pthread_t const thread;
  AutoreleasePoolPage * const 
  AutoreleasePoolPage *child;
  uint32_t const depth;
  uint32_t hiwat;
  PAGE_MAX_SIZE;
  COUNT;
  POOL_BOUNDARY;

  Page 1   //第一页                               page2//第二页           ...

1,AutoreleasePool并没有单独的结构,而是由若干个AutoreleasePoolPage以双向链表的形式组成
2,AutoreleasePoolPage每个对象会开辟4096字节内存(也就是虚拟内存一页的大小),除了上面的实例变量所占空间,剩下的空间全部用来储存autorelease对象的地址

Runloop与AutoReleasepool的关系

  NSLog(@"相关的关系 %@",[NSRunLoop currentRunLoop]);

当我们在程序中用日志打印相关数据的时候,显示的日志是

2020-07-30 10:38:49.003092+0800 VVeboTableViewDemo[1522:62613] 相关的关系 <CFRunLoop 0x600001ce0300 [0x7fff8062d750]>{wakeup port = 0x1903, stopped = false, ignoreWakeUps = false, 
current mode = kCFRunLoopDefaultMode,
common modes = <CFBasicHash 0x600002eba1f0 [0x7fff8062d750]>{type = mutable set, count = 2,
entries =>
    0 : <CFString 0x7fff869c52a0 [0x7fff8062d750]>{contents = "UITrackingRunLoopMode"}
    2 : <CFString 0x7fff80640a20 [0x7fff8062d750]>{contents = "kCFRunLoopDefaultMode"}
}
,
common mode items = <CFBasicHash 0x600002eba370 [0x7fff8062d750]>{type = mutable set, count = 11,
entries =>
    0 : <CFRunLoopSource 0x6000015e8000 [0x7fff8062d750]>{signalled = No, valid = Yes, order = -1, context = <CFRunLoopSource context>{version = 0, info = 0x0, callout = PurpleEventSignalCallback (0x7fff38c3a9b2)}}
    1 : <CFRunLoopSource 0x6000015f00c0 [0x7fff8062d750]>{signalled = No, valid = Yes, order = -1, context = <CFRunLoopSource context>{version = 1, info = 0x2e03, callout = PurpleEventCallback (0x7fff38c3a9be)}}
    2 : <CFRunLoopSource 0x6000015fc000 [0x7fff8062d750]>{si

但是相关控制台打印的数据是

0 : <CFRunLoopSource 0x6000015e8000 [0x7fff8062d750]>{signalled = No, valid = Yes, order = -1, context = <CFRunLoopSource context>{version = 0, info = 0x0, callout = PurpleEventSignalCallback (0x7fff38c3a9b2)}}
    1 : <CFRunLoopSource 0x6000015f00c0 [0x7fff8062d750]>{signalled = No, valid = Yes, order = -1, context = <CFRunLoopSource context>{version = 1, info = 0x2e03, callout = PurpleEventCallback (0x7fff38c3a9be)}}
    2 : <CFRunLoopSource 0x6000015fc000 [0x7fff8062d750]>{signalled = No, valid = Yes, order = -1, context = <CFRunLoopSource context>{version = 0, info = 0x600001bf84e0, callout = __handleEventQueue (0x7fff493c2e6e)}}
    3 : <CFRunLoopObserver 0x6000011fc1e0 [0x7fff8062d750]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x7fff4931eaa4), context = <CFArray 0x600002edf570 [0x7fff8062d750]>{type = mutable-small, count = 1, values = (
    0 : <0x7fbfa580d048>
)}}
    4 : <CFRunLoopObserver 0x6000011fc000 [0x7fff8062d750]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 1999000, callout = _beforeCACommitHandler (0x7fff4934fa4f), context = <CFRunLoopObserver context 0x7fbfa4c052c0>}
    5 : <CFRunLoopObserver 0x6000011f80a0 [0x7fff8062d750]>{valid = Yes, activities = 0x20, repeats = Yes, order = 0, callout = _UIGestureRecognizerUpdateObserver (0x7fff48eaa99e), context = <CFRunLoopObserver context 0x600000bf8e00>}
    6 : <CFRunLoopSource 0x6000015fc0c0 [0x7fff8062d750]>{signalled = No, valid = Yes, order = -2, context = <CFRunLoopSource context>{version = 0, info = 0x600002ea8db0, callout = __handleHIDEventFetcherDrain (0x7fff493c2edd)}}
    7 : <CFRunLoopSource 0x6000015e0000 [0x7fff8062d750]>{signalled = Yes, valid = Yes, order = 0, context = <CFRunLoopSource context>{version = 0, info = 0x6000004e0540, callout = FBSSerialQueueRunLoopSourceHandler (0x7fff36d82bd5)}}
    8 : <CFRunLoopObserver 0x6000011fc0a0 [0x7fff8062d750]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2001000, callout = _afterCACommitHandler (0x7fff4934fab8), context = <CFRunLoopObserver context 0x7fbfa4c052c0>}
    9 : <CFRunLoopObserver 0x6000011ec320 [0x7fff8062d750]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2000000, callout = _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv (0x7fff2b477daa), context = <CFRunLoopObserver context 0x0>}
    12 : <CFRunLoopObserver 0x6000011fc140 [0x7fff8062d750]>{valid = Yes, activities = 0x1, repeats = Yes, order = -2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x7fff4931eaa4), context = <CFArray 0x600002edf570 [0x7fff8062d750]>{type = mutable-small, count = 1, values = (
    0 : <0x7fbfa580d048>
)}}

第一个监听的是activities 是 NSRunLoopEntry状态,说明当runloop进入entry状态的时候,会调用_wrapRunLoopWithAutoreleasePoolHandler
,其内部会调用_objc_autoreleasePoolPush()创建自动释放池。
第二个监听的activities是 NSRunLoopBeforeWaiting 和NSRunLoopExit,BeforeWaiting 其回调方法_wrapRunLoopWithAutoreleasePoolHandler内部会调用先调用pop操作,然后再push 创建一个新的自动释放池。Exit会调用pop操作。

autoreleasepool的执行顺序就是Entry-->push ---> BeforeWaiting--->pop-->push -->Exit-->pop,按照这样的顺便,保证了,每一次push都对应一个pop。autoreleasepool释放操作在每一次runloop 的BeforeWaiting和exit的时候执行的

AutoreleasePool并没有单独的结构,而是由若干个AutoreleasePoolPage以双向链表的形式组合而成(分别对应结构中的parent指针和child指针)
AutoreleasePool是按线程一一对应的(结构中的thread指针指向当前线程)
AutoreleasePoolPage每个对象会开辟4096字节内存(也就是虚拟内存一页的大小),除了上面的实例变量所占空间,剩下的空间全部用来储存autorelease对象的地址
上面的id *next指针作为游标指向栈顶最新add进来的autorelease对象的下一个位置
一个AutoreleasePoolPage的空间被占满时,会新建一个AutoreleasePoolPage对象,连接链表,后来的autorelease对象在新的page加入
所以,若当前线程中只有一个AutoreleasePoolPage对象,并记录了很多autorelease对象地址时内存如下图:

图中的情况,这一页再加入一个autorelease对象就要满了(也就是next指针马上指向栈顶),这时就要执行上面说的操作,建立下一页page对象,与这一页链表连接完成后,新page的next指针被初始化在栈底(begin的位置),然后继续向栈顶添加新对象。

所以,向一个对象发送- autorelease消息,就是将这个对象加入到当前AutoreleasePoolPage的栈顶next指针指向的位置。

所以runloop 和AutoReleasepool是通过线程的方式一一对应的

什么情况下需要创建自动释放池?

其实自动释放池存在的意义是为了延迟释放一些对象,延迟向对象发送release消息。在实际的开发中,有两种情况是需要手动创建自动释放池的。

1、在多线程中,因为子线程中可能会使用便利构造器等方法来创建对象(类方法),那么这些对象的释放只能放在自动释放池中,此时需要在子线程中添加自动释放池。

使用便利构造器等方法来创建对象是autorelease的对象,需要制动释放池才能释放

主线程已经添加过自动释放池,在main函数里面。

2、就是如果一段代码里面(比如for循环)大量使用便利构造器创建对象,也需要手动添加自动释放池。

什么是便利构造器?

便利构造器在初始化方法的基础上前进了一⼩小步。封装了对象创建过程。

便利构造器是“+”方法,返回本类型的实例,方法名以类名开头,其实一些类中的静态方法也是便利构造器。

可以有0到多个参数。内部实现:封装了alloc和初始化方法。使用起来更加简洁。

runloop和 autorelease pool

先提出一个问题,在Iphone项目中,大家会看到一个默认的Autorelease pool,程序开始时创建,程序退出时销毁,按照对Autorelease的理解,岂不是所有autorelease pool里的对象在程序退出时才release, 这样跟内存泄露有什么区别?结果是,对于每一个Runloop, 系统会隐式创建一个Autorelease pool,这样所有的release pool会构成一个象CallStack一样的一个栈式结构,在每一个Runloop结束时,当前栈顶的Autorelease pool会被销毁,这样这个pool里的每个Object会被release。

那什么是一个runloop?一个UI事件,一个timer,一个系统delegate都称之为runloop(不是NSRunloop),runloop实际上是从接收消息,然后处理完消息的一个完整过程。

为了更加形象说明auto release pool机制,下面举例:

NSString* str1是assign。

UI事件:UIButton的target-action机制,在action中创建一个autorelease的UILabel对象,并赋值,在action中打印出值,action执行完毕,这个时候runloop结束,autorelease pool被释放,label也被释放,所以再调用这个对象的值时,出现bad_exec_access。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,245评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,749评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,960评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,575评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,668评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,670评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,664评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,422评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,864评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,178评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,340评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,015评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,646评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,265评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,494评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,261评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,206评论 2 352