【问题标题】:In Objective-c What happens if a parent deallocs a child, while a child method is executing?在 Objective-c 中,如果父级释放子级,而子级方法正在执行,会发生什么?
【发布时间】:2012-02-29 21:54:21
【问题描述】:

我有一位非常喜欢孩子的父母。子级对父级的引用很弱。

父级在一个线程上调用子级的方法。在子方法完成执行之前,父级可能会释放子级。没有明显的方法可以同步它以避免父级释放子级直到方法返回。

会发生什么?

编辑:我没有使用 ARC,它将在 iOS 3.1.2 上运行

编辑:使用 [NSValue valueWithNonretainedObject] 怎么样?

据此: https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/GarbageCollection/Articles/gcAPI.html

当对象被垃圾回收时,引用设置为 nil。但是由于iOS没有垃圾回收,会不会有悬空指针?

编辑:发现这篇有用的帖子,所以我想我也会与其他了解该主题的人分享:

http://cocoawithlove.com/2009/07/rules-to-avoid-retain-cycles.html

【问题讨论】:

    标签: objective-c multithreading release dealloc


    【解决方案1】:

    以下内容不适用于 ARC(如果您说的是 ARC)

    它可能会崩溃。但是,您可以在一种方法启动时调用[self retain];,并在方法完成执行之前调用[self release];(在返回之前或方法结束之前)。父级会在子级上调用release,但这已被保留(如果方法正在执行),所以retainCount 应该仍然> 0(当调用dealloc 时)。

    如果方法使用对父级的弱引用,那么在解除分配时,父级应调用child.parent = nil;,以防止子级访问其释放的内存。

    【讨论】:

    • 这听起来确实是个好建议。我也想听听其他人对此的看法,因为我不是很有经验。
    • 如果父级释放,子级会返回什么?如果父级在子级上调用一个返回值的方法,并且父级在子级返回之前解除分配,那么返回值到哪里去了?它会崩溃吗?我想解决这个问题的方法也是在方法中保留和释放父级?
    • 如果从父方法调用(同步),则返回值将转到父方法,因为子方法是从父方法调用的,父方法等待返回值(除非@ 987654325@)。如果方法被异步调用,那么当方法完成时子进程可能会调用[parent callbackMethod];,但由于parent 设置为nil(父进程在释放时调用child.parent = nil;),所以什么也不会发生。作为@Mo。提到,如果可能,最好取消或等到孩子停止执行后再释放)
    • -1 这不好。父级可以在子级上调用 release 并在子级方法启动和对[self retain] 的调用之间释放它,实际上增加了引用计数。您需要适当的同步
    • 扩展@JeremyP 所说的内容;保留/释放应该在每个线程的基础上进行管理。当一个对象从一个线程“传递”到另一个线程时,它应该在第一个线程上保留并在第二个线程上释放(或者应该有一个同步原语来强制执行此操作)。否则会导致难以调试竞争条件。
    【解决方案2】:

    如果你想拥有一个设计良好的代码,你需要从一开始就避免这个问题。例如,仅在其所有子项的工作完成时才释放父项,或者将发布代码放在一个完成处理程序块中,该处理程序块仅在没有子方法执行时才被调用。

    然而,快速修复是 babbidi 所说的,或者如果您使用 ARC,则为:在方法的开头保留对局部变量中父级的强引用,例如 localParent。这会创建一个保留周期,因此两个对象都不会被释放。由于localParent 是本地的,它会在方法结束时自动释放,从而打破保留循环并允许释放两个对象。

    如果您的方法有返回值,请在异步块中使用本地父级来有效地模拟自动释放行为:

    dispatch_async(dispatch_get_main_queue(), ^{
        [localParent self]; // this does nothing
    });
    

    【讨论】:

    • 感谢您的回复。我认为也许我的设计应该完全避免弱引用。但是此时我无法“崩溃”我的应用程序,因为线程调度程序似乎以正确的顺序有效地停止了所有线程。但这需要验证,因为它是第三方代码,并且有可能所有线程恰好在 dealloc 开始之前完成执行。
    【解决方案3】:

    如果你正在使用类似的东西:

    [NSThread detachNewThreadSelector: selctor target: theChildObject withObject: nil];
    

    你没有问题。目标被保留(如果提供了对象),直到线程完成执行。如果没有,您需要安排适当的同步,例如在父级中有一个子级的原子访问器。

    // in the parent interface
    
    @property (retain) id child; // Note no nonatomic
    
    // in the child thread 
    
    id myChild = [parent child];  // myChild will be autoreleased in the current thread if the child property is atomic
    if (myChild != nil)
    {
        [myChild retain];             // not strictly necessary as long as the current autorelease pool is not drained 
        // do the stuff you need with myChild
        [myChild release];
    }
    

    【讨论】:

    • 请注意,dispatch_async() 也是如此;它会在块执行期间保留分派块中的所有对象。
    • 那么如果我们使用non-atomic的话,child要么为nil,要么一直保留到线程结束?感谢您的回复。
    • 我可能无法使用它 - 所有的孩子都存储在 NSMutableArray 中。我可以将数组的属性更改为原子属性,但是由于它们存储在数组中,这对孩子有什么影响?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-24
    • 2012-08-15
    • 2015-07-28
    • 2017-03-25
    • 2015-08-14
    • 2016-10-04
    相关资源
    最近更新 更多