现在的位置: 首页 > 综合 > 正文

Objective-C循环引用(转)

2019年11月13日 综合 ⁄ 共 3890字 ⁄ 字号 评论关闭

  所有的引用计数系统,都存在循环应用的问题。例如下面的引用关系:

  对象a创建并引用到了对象b.

  对象b创建并引用到了对象c.

  对象c创建并引用到了对象b.

  这时候b和c的引用计数分别是2和1。当a不再使用b,调用release释放对b的所有权,因为c还引用了b,所以b的引用计数为1,b不会被释放。b不释放,c的引用计数就是1,c也不会被释放。从此,b和c永远留在内存中。

  这种情况,必须打断循环引用,通过其他规则来维护引用关系。比如,我们常见的delegate往往是assign方式的属性而不是retain方式 的属性,赋值不会增加引用计数,就是为了防止delegation两端产生不必要的循环引用。如果一个UITableViewController 对象a通过retain获取了UITableView对象b的所有权,这个UITableView对象b的delegate又是a, 如果这个delegate是retain方式的,那基本上就没有机会释放这两个对象了。自己在设计使用delegate模式时,也要注意这点。

  因为循环引用而产生的内存泄露也是Instrument无法发现的,所以要特别小心。

  惯有规则:

  规则1: 是不是我们在用对象时,只要不是自己alloc的,就不需要去release了?

  (有很多类都提供“便利构造函数(convenience constructors)”,它们创建对象但并不增加引用计数,意味着不需要调用release来释放所有权。很好辨认,它们的名字中不会有alloc和copy。)

  通常,不是你alloc, copy或者retain过的,就不需要去release了。

  规则2:关于“循环引用”中的例子,为什么将delegate赋为nil,就可以避免循环问题?

  并不是将delegate赋值为nil就可以避免循环问题,实际上delegate往往都设计成assign方式的属性,而不是retain属性的。也就是说并没有获得所有权,这是保证不产生循环问题的条件。

  我们在项目中,有时需要一些全局变量存储一些基本信息,那么这些全局的变量内存又是如何管理的呢?

  下面是网友分享的经验,具体的网址我记不清了,我在这里直接拿过来放到我的博客了,在此向写本文的作者致敬!

  正是由于众多具有开源精神的网友,技术才能更好的传播!

  项目中有时需要一些全局变量存储应用程序运行过程中的一直存在的信息,翻看了一些资料,决定使用Apple官方文档推荐的Signaleton模式,使用过程很顺利,但是随之而来的是关于内存管理的考虑,官方文档的示例代码如下:

  static MyGizmoClass *sharedGizmoManager = nil;

  + (MyGizmoClass*)sharedManager

  {

  if (sharedGizmoManager == nil) {

  sharedGizmoManager = [[super allocWithZone:NULL] init];

  }

  return sharedGizmoManager;

  }

  + (id)allocWithZone: (NSZone *)zone

  {

  return [[self sharedManager] retain];

  }

  - (id)copyWithZone: (NSZone *)zone

  {

  return self ;

  }

  - (id)retain

  {

  return self;

  }

  - (NSUInteger)retainCount

  {

  return NSUIntegerMax; //denotes an object that cannot be released

  }

  - (void)release

  {

  //do nothing

  }

  - (id)autorelease

  {

  return self;

  }

  可以看到上面的代码中除了为保证这个静态类的唯一性,还有一个奇怪的地方则是这个类没有dealloc方法,那么这个类实例化后所分配的内存是在什么时候释放的?如果这个类中还含有一些其他的成员变量,这些变量所分配的内存又是在何时释放的呢?

  以之前C/C++平台的经验,静态变量的内存被放在全局区(或称之为静态区),静态变量的内存在应用程序启动之前由系统分配,在应用程序退出之后又由系统自己回收。Cocoa基于C设计,所以大致上应该也是这个样子。

  为了验证这个问题,我尝试着给这个类添加了一个dealloc方法,之后更加奇怪的事情就发生了,我在这个类dealloc方法中设置断点并且尝试输出log,但当程序退出时不仅断点没有断下来,甚至在console中连这段log的踪影都看不到,我开始怀疑这段dealloc没有被调用,之后我又在其他类的dealloc方法中输出log,发现一个规律:dealloc方法在程序运行过程中随着类实例的释放会被调用,但是当直接退出程序时(按HOME键),所有的dealloc都不被调用。这就说明当应用退出的时候,Cocoa是不会调用实例的dealloc方法的,这是意味着什么呢?我猜想,可能Cocoa会在应用程序退出时自己回收所有的内存,并且不像C++那样去调用存在的类实例的析构方法?为证实这个猜想,google之,最终在Cocoa With Love和Apple的另一份官方文档中找到了答案,并证明我的这个猜测是正确的:

  官方文档中是这么解释的: " When an application terminates, objects may not be sent a dealloc message since the process’s memory is automatically cleared on exit—it is more efficient simply to allow the operating system to clean up resources than to invoke all the memory management methods." 大致的意思就是:当应用程序退出时,对象不会接受到一个dealloc消息,系统会自己清理所有的资源,Apple认为这样比去调用一个内存管理方法更有 效率。

  Cocoa With Love的Matt关于我提问的解答是这样的: "You don't need to free data from a singleton -- it lasts until the program quits, so the dealloc method will never be invoked. If you need to close a network connection, or something else that actually needs to be ended, you should do this in a "close" method and invoke the "close" method on the singleton in your applicationWillTerminate: method of your application delegate." 意思差不多和官方一样,应用退出时,dealloc方法不会被调用,并且他建议我避免在dealloc中去作网络或其他类似的必须终止的操作,而应该将这 些操作放在applicationWillTerminate:中,以确定这些操作被执行。

  呵呵,谢谢Matt,他的这个建议确实是非常有效果的,否则我的下一个问题肯定的是:那么一定得终止的操作放在哪里执行?

  所以关于这个问题的研究暂时就告一段落了,得出的结论是:

  1.不用担心静态全局变量的内存的问题,系统会在应用程序结束之后,回收这些内存;

  2.应用程序结束时会直接回收所有的程序运行中的资源,而不调用对象的dealloc方法;

  3.不要将类似网络或文件的关闭(应该是任何)操作放在类的dealloc方法中执行;

  当然这也只是告一段落了,由此我又产生了两个问题:

  1.如果系统会在程序结束后回收所有的资源,那么是否基本不用考虑什么内存泄露的问题?内存泄露只会在程序运行过程中发生?即使发生了内存泄露,应用程序退出时,系统也会回收这些内存;

  2.Cocoa如何实现这一套资源管理机制的呢?只以内存说,难道系统给应用程序指定一段内存,你应用程序分配的内存只会在这一段区域里,当应用程序退出之后,我系统就直接抹了这段内存?

  这两个问题想请教tinyfool 和 robinlu,不知他们二位有没有时间。不过我的这个blog太丑了,我先把字弄大点.

  文中参考资料:

  1.Apple推荐Sinaleton方法的文档Cocoa Fundamental Guide:Cocoa Objects中Creating a Singleton Instance一节 这里还有中文的

  2.Cocoa With Love中关于top-level data的话题:Singletons, AppDelegates and top-level data. 这里讨论还了你需要全局数据的条件,还提供了另外一个方法存放全局的数据:放置在AppDelegates中,并且讨论了这种方法的不妥之处,建议和我一样的新手可以拜读一下.

  3.Apple解释应用程序退出时不调用dealloc方法的文档:Memory Management Programming Guide for Cococa:Object Ownship and Disposal中Deallocating an Object一节的important:一段

抱歉!评论已关闭.