iOS之对象销毁

匿名 (未验证) 提交于 2019-12-02 23:40:02
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Batac_Lee/article/details/91454051

这是我查看了iOS源码之后,在结合这篇文章黑幕背后的Autorelease而总结出来的知识,在此向这位大神学习了。

AutoReleasePool自动释放池,一般理解就是自动帮OC对象添加release操作,一般涉及的问题有AutoRelease实现原理,什么时候释放,以及怎么释放的问题。
写个示例代码触发AutoReleasePool

 @autoreleasepool{ NSString *strTest = [NSString stringWithFormat:@"test%i", 100]; // 这里必须要拼接字符串,不然系统会编译成常量字符串,那就不受AutoReleasePool操作了   } 

那么会编译成:

 NSAutoReleasePool *pool = [[NSAutoReleasePool alloc] init]; NSString *strTest = [NSString stringWithFormat:@"test%i", 100]; // stringWithFormat里面也会调用autorelease,所以strTest这里不用调用autorelease [pool drain]; 

这里要引入一个哨兵对象的概念,简单来说,就是一个对象存储着一个起始内存地址。

  • NSAutoReleasePool *pool = [[NSAutoReleasePool alloc] init];
    里面会调用NSAutoReleasePool 类的 push() 标记 一个哨兵对象 的内存地址,以NSAutoReleasePool成员变量的方式保存。
  • 在AutoReleasePool代码块里面 创建 的OC对象,都会标记到哨兵对象之后的内存块中。(其实就是类似Array添加对象,然后在释放后把Array里面的所有对象都给释放掉一样)
  • 调用 [pool drain] 的时候,实际是调用NSAutoReleasePool 的pop方法,然后把添加在哨兵对象之后的OC对象全部释放

AutoReleasePoolPage的源码大概是这样的流程

 NSAutoReleasePool *pool = [[NSAutoReleasePool alloc] init];       ------>  id context = AutoreleasePoolPage.push(); [obj1 autorelease]; -------> getHotPage().add(obj1); 添加到哨兵对象之后 [obj2 autorelease]; -------> getHotPage().add(obj2); [pool drain]; -------> AutoreleasePoolPage.pop(context); 

** 关于OC对象何时释放 **
然后在NSAutoRelasePool对象执行销毁时[pool drain]后,自动释放池会把添加到该pool里面的所有OC对象都执行一遍release(retainCount-=1),如果恰好此时retainCount<=0 ,那么系统会自动调用dealloc方法废弃对象。
如果你是在主线程里面写了一个用autoRelease 修饰的OC对象,但是没有给该对象指定自定义的NSAutoRelasePool,那么系统会把自动释放的OC对象添加到最近的NSAutoReleasePool中,即 main.m 里面的 autoreleasepool(注意主线程Runloop每次循环只是在这个autoreleasepool里面创建分页page,然后循环结束执行pop释放分页,并没有再新建autoreleasepool),这样的话只有等一次Runloop循环执行结束之后,该pool执行pop才会释放对象。
而 main 的 autoreleasepool,要等到APP结束运行的时候,才会彻底执行释放drain,即UIApplicationMain方法执行返回了。

 #import <UIKit/UIKit.h> #import "AppDelegate.h"  int main(int argc, char * argv[]) {     @autoreleasepool {         return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));     } } 

目前在代码中创建局部对象,系统都会自动添加autoRelease修饰,那么主线程Runloop在每次循环结束时,会把本次Runloop循环里面所新建的OC对象都指定一次release,如果retainCount==0,那么该对象会被释放。
这样就存在一个情况:在一个函数A里面新建的局部对象Object1,在另一个函数B里面还是可以访问这个局部对象Object1,到函数C里面Object1被释放,就说明函数A和函数B的代码是在同一次Runloop里面执行的,而函数C是在下一次Runloop循环里面调用。

示例

最后附上查看线程池的方式:(NSAutoreleasePool 必须是MRC环境:设置MRC方式

 // 因为showPools 未公开,只能这么调用了 IMP imp = [NSAutoreleasePool methodForSelector:@selector(showPools)];  if(imp != nil){     ((void(*)(void))imp)(); }  // Developer Documentation里面搜索 NSAutoreleasePool 就可以找到方法说明 // Debugging // [+showPools](apple-reference-documentation://hc4NqmvBGF) // Displays the state of the current thread's autorelease pool stack to stderr . 

示例代码

 //  main.m int main(int argc, char * argv[]) {     @autoreleasepool {         NSObject *obj = [[[NSObject alloc] init] autorelease];         NSObject *obj1 = [[[NSObject alloc] init] autorelease];         IMP imp = [NSAutoreleasePool methodForSelector:@selector(showPools)];         if(imp != nil){             ((void(*)(void))imp)();         }                  return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));     } }  //  AppDelegate.m - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions  {     NSObject *objtemp =  [[[NSObject alloc] init] autorelease];;     NSObject *objtemp1 = [[[NSObject alloc] init] autorelease];     NSObject *objtemp2 =  [[[NSObject alloc] init] autorelease];          IMP imp = [NSAutoreleasePool methodForSelector:@selector(showPools)];     if(imp != nil){         ((void(*)(void))imp)();     }     return YES; } 

输出结果:(上面几个autoRelease对象 都在同一个主线程释放池,但是在不同的区域(就是上面所说的分页))。

 objc[7994]: ############## objc[7994]: AUTORELEASE POOLS for thread 0x103de33c0 objc[7994]: 857 releases pending. objc[7994]: [0x7faa43805000]  ................  PAGE (full)  (cold) objc[7994]: [0x7faa43805038]  ################  POOL 0x7faa43805038 objc[7994]: [0x7faa43805040]    0x61800000a270  NSObject  --- 注意看这里 ---  objc[7994]: [0x7faa43805048]    0x61800000a280  NSObject  --- 注意看这里 --- objc[7994]: [0x7faa43805050]    0x618000036120  __NSCFString objc[7994]: [0x7faa43805058]  ################  POOL 0x7faa43805058 ...... **objc[7994]: [0x7faa43805098]  ################  POOL 0x7faa43805098** objc[7994]: [0x7faa438050a0]    0x6080000b1d60  UIMutableApplicationSceneSettings objc[7994]: [0x7faa438050a8]    0x60000006ea80  UIMutableApplicationSceneClientSettings objc[7994]: [0x7faa438050b0]    0x600000034fa0  UIDevice ...... objc[7994]: [0x7faa42010b10]    0x61000006a580  __NSBundleTables objc[7994]: [0x7faa42010b18]    0x610000084f10  NSBundle objc[7994]: [0x7faa42010b20]    0x61800000a5f0  NSObject  --- 注意看这里 --- objc[7994]: [0x7faa42010b28]    0x61800000a600  NSObject  --- 注意看这里 ---  objc[7994]: [0x7faa42010b30]    0x61800000a610  NSObject  --- 注意看这里 ---  objc[7994]: ##############



作者:晨寂
链接:https://www.jianshu.com/p/61d8131c6bf3
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

文章来源: https://blog.csdn.net/Batac_Lee/article/details/91454051
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!