【问题标题】:RetainCount OK to use in this instance?RetainCount 可以在这种情况下使用吗?
【发布时间】:2012-04-05 16:03:25
【问题描述】:

RetainCount == 坏

retainCount 是禁忌、不可靠、不可预测的,一般不应该使用。我没有在我的代码中的任何地方使用它,但我在一个类中看到过它,我以一种有趣的方式使用它。

我有一个类运行一个无限期运行的线程,直到线程被取消。问题是线程增加了所有者的保留计数,在我的例子中是实例化它的类。所以,即使我用完了那个类,那个实例仍然会挂起,除非管理我的类的人也有聪明的办法知道关闭线程。这是一种解决方案,但这是我在代码中找到的。

- (oneway void)release
{
    // This override allows allows this object to be dealloced 
    // by shutting down the thread when the thread holds the last reference.
    // Otherwise, the object will never be dealloc'd
    if (self.retainCount == 2)
    {
        [self quitDispatchThread];
    }

    [super release];
}

这是一个聪明的解决方案,但我不知道该怎么想。它覆盖类上的 release 并检查保留计数是否为 2。换句话说,它检查线程是否是唯一使我的对象保持活动状态的东西(因为保留计数约为从 2 减到 1),如果是,则终止线程(quitDispatchThread 将阻塞,直到线程终止)。

所以……

能否依靠retainCount来判断是否为1?

通常人们说要远离retainCount,因为你不知道那里是否有一些自动发布。但是,如果retainCount 是一个,那么我知道只有线程保持它处于活动状态,我不必担心retainCount 可能由于某些自动释放等而关闭......

这段代码有什么问题?

我正要删除它,但它实际上似乎是有道理的。其他对象不必知道我的类正在运行线程。其他对象可以安全地 retainrelease 甚至 autorelease 拥有线程的对象,而不必担心关闭线程,因为它会自行处理。

这段代码实际上感觉很干净,这让我很惊讶。

编辑 :: NSThread 正在保留我的对象

我使用 NSThread 的事实增加了我的对象的保留计数。我的对象是targetselector 是线程运行的方法。

initWithTarget:selector:object:

返回一个使用给定参数初始化的 NSThread 对象。

  • (id)initWithTarget:(id)目标选择器:(SEL)选择器对象:(id)参数

参数

目标

选择器指定的消息要发送到的对象。

选择器

要发送到目标的消息的选择器。这个选择器必须 只接受一个参数,并且不能有返回值。

参数

传递给目标的单个参数。可能为零。

返回值

使用给定参数初始化的 NSThread 对象。

讨论

对于非垃圾收集的应用程序,方法选择器是 负责为新分离的建立一个自动释放池 线程并在它退出之前释放该池。垃圾收集 应用程序不需要创建自动释放池。

对象目标和参数在分离线程执行期间保留。它们在线程最终释放时 退出。

【问题讨论】:

  • (舌尖上)如果你觉得这段代码很干净,试着把它移植到 ARC。
  • 试图通过操纵retainCount 来修复Cocoa 中的任何问题是一个存在设计问题的信号。我认为循环所有权是问题的根源。你能重新设计你的代码,使线程没有对拥有对象的保留引用吗?这段代码看起来很干净,就像打蜡的胡子看起来很干净一样,但是这段代码看起来不合适,就像奶奶嘴唇上的蜡胡子不合适一样。
  • @Mr.Berna 我希望奶奶没有小胡子,但如果她有,我希望它会打蜡。 :D 我正在使用NSThread,当您将它初始化到目标、选择器、参数时,它会保留target。所以......我认为我无法绕过循环所有权。但是,该对象(及其关联的线程)仅在一处被清理,因为它仅在代码中的一处使用。我已经有代码可以做到这一点。我什至正要检查零钱。然而,当我回顾这个变化时,我不得不问自己为什么要这样做,尤其是当我知道它有效时。
  • 我的答案已更新;最后两段说明您对-[NSThread initWithTarget:selector:object:] 的使用。简而言之,让目标不是线程的所有者。

标签: objective-c ios nsthread retaincount


【解决方案1】:

retainCount 是禁忌、不可靠、不可预测的,一般不应该使用。

您可以依赖retainCount IFF 的值,您的对象不会通过任何对您不透明的代码,例如任何 Cocoa 框架。在实践中,这几乎是不可能实现的,因此发出警告。 Cocoa 的内部可能会出于多种原因多次传递您的对象、保留、释放并将其放入自动释放池中,并且您不能在任何给定点依赖它的绝对值。

问题是线程增加了所有者的保留计数,在我的例子中是实例化它的类。

这是一个保留周期。这里的答案是想办法打破这个循环,而不是颠覆引用计数机制。当您的线程或拥有对象知道线程正在执行的工作已完成(或需要提前停止)时,必须有某个时间点 解除分配。

听起来拥有对象是客户端代码与线程正在执行的工作的接口。此拥有对象需要一个“立即关闭”方法,该方法需要在 所有者释放它之前被调用(并记录为“必须调用”)。在该关闭方法中,您可以通过释放线程来中断循环。

我不完全确定线程保留其创建者的情况(循环非常清楚地表明 某事 您的所有权模型有问题)- 我猜你'正在使用NSThreadinitWithTarget:...,目标是创建/拥有对象。这有点混淆了标准 MVC 模式——线程的所有者是“控制器”,线程本身(及其运行的代码)更像是“模型”。

换句话说,控制器不应该包含线程的代码。我建议您将线程的代码分解为另一个对象以用作目标。然后控制器对象拥有线程本身和“工作”目标对象,它们都不拥有控制器。瞧,没有循环!

【讨论】:

    【解决方案2】:

    不。您依赖于一个抽象的实现,该实现在其大门上公开标有“Keep Out”。

    只需使用您自己的内存和实现来管理您的依赖项。这可能包括 ivar 和/或用于完成/销毁阶段的方法。

    另请参阅release 的文档:

    发布

    减少接收者的引用计数。 (必填)

    - (oneway void)release

    讨论

    当引用计数达到0时,接收者会收到一个dealloc消息。

    你只会实现这个方法来定义你自己的 引用计数方案。这样的实现不应该调用 继承方法;也就是说,它们不应包含发布消息 超级。

    如果您想确保您的程序在多个版本中都能正常运行,最好现在就更改它。

    【讨论】:

    • +1 我喜欢release 的文档特别说不要调用super。仅此一项就使发布覆盖成为一个坏主意。感谢您的回答。
    • @Sam 是的,我认为这意味着“为优化保留”。影响行为的 current 优化的 idk,但我不会走这条路。你可能已经和我一样测试了它:)不客气。
    猜你喜欢
    • 2018-05-05
    • 2022-11-14
    • 2013-11-14
    • 2012-03-28
    • 1970-01-01
    • 2016-09-12
    • 2016-11-07
    • 1970-01-01
    • 2022-08-05
    相关资源
    最近更新 更多