在其委托回调方法中释放委托对象

在其委托回调方法中释放委托对象

问题描述:

我想弄明白以下情况的推荐做法。某些对象(如CLLocationManager或MKReverseGeocoder)将其结果异步发送到委托回调方法。是否可以在回调方法中释放CLLocationManager或MKReverseGeocoder实例(或任何类)?关键是你不再需要该对象,所以你要让它停止发送更新,将其委托设置为nil,并释放对象。

I'm trying to figure out what the recommended practice is for the following situation. Certain objects, such as CLLocationManager or MKReverseGeocoder, send their results asynchronously to a delegate callback method. Is it OK to release that CLLocationManager or MKReverseGeocoder instance (or whatever class it may be) in the callback method? The point is that you no longer need that object around, so you tell it to stop sending updates, set its delegate to nil, and release the object.

伪代码:

@interface SomeClass <CLLocationManagerDelegate>
...
@end

@implementation SomeClass

...

- (void)someMethod
{
    CLLocationManager* locManager = [[CLLocationManager alloc] init];
    locManager.delegate = self;
    [locManager startUpdatingLocation];
}

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
    // Do something with the location
    // ...

    [manager stopUpdatingLocation];
    manager.delegate = nil;
    [manager release];
}

@end

模式被认为总是OK,如果它被认为是永远不会,或者它取决于类?

I am wondering if this usage pattern is considered to be always OK, if it's considered to be never OK, or if it depends on the class?

有一个明显的情况下,释放委托对象错了,那就是如果它需要做的东西,在它已经通知委托人。如果代理释放对象,其内存可能会被覆盖,应用程序崩溃。 (这似乎是在我的应用程序中发生的CLLocationManager在一个特定的情况下,只有在模拟器上发生了什么)我想弄清楚如果它是一个模拟器bug,或者如果我做的是从根本上有缺陷。)

There is an obvious case where releasing the delegating object would go wrong and that is if it needs to do stuff after it has notified the delegate. If the delegate releases the object, its memory may get overwritten and the application crashes. (That appears to be what happens in my app with CLLocationManager in a particular circumstance, both only on the simulator. I'm trying to figure out if it is a simulator bug or if what I am doing is fundamentally flawed.)

我一直在搜索,我找不到一个结论性的答案。

I have been searching and I cannot find a conclusive answer to this. Does anyone have an authoritative source that can answer this question?

这是一个很好的问题,我等了几个小时的希望有人会给一个足够的答案,但因为没有人甚至回答,我会试试。首先,我将评论你的方法,然后我尝试建议我如何绕过这个。

This is a very good question, I waited few hours in hope someone would give a sufficient answer, but because no one even replied, I'll give it a try. First I'll comment on your approach, then I try to suggest how I would go around this.

这绝对是一个非常糟糕的主意,释放 - 从而释放一个对象其代表。只是想想对象(如CLLocationManager)如何调用他们的代理 - 他们只是在一些方法的中间调用它们。当调用委托完成时,代码的执行返回到已经解除分配的对象的方法。 BAM!

It's definitely a very bad idea to release - thus deallocate an object from its delegate. Just think about how objects (like a CLLocationManager) do call their delegates - they just call them in the middle of some method. When call to delegate is finished, code's execution comes back to a method of an object that has already been deallocated. BAM!

让我们忘记一个事实,这是一个坏主意。我看到两个选项如何轻松修复 。首先, autorelease 而不是 release 给对象一个更长的时间垃圾邮件 - 它至少可以幸存从委托返回。对大多数情况来说,这应该足够了,至少如果API的作者做得好,并在主API类后面封装逻辑(在CLLocationManager的情况下,它可能正在等待GPS关闭...)。第二个选项是延迟释放( performSelector:withObject:afterDelay:)),但这是更糟的实现API的解决方法。

Let's forget for a moment about the fact that this is a bad idea. I see two options how to fix that easily. First, autorelease instead of release gives an object a little longer time spam - it'd at least survive returning from delegate. That should be sufficient for most of the cases, at least if author of API did her job well and encapsulated logic behind the main API class (in case of CLLocationManager it might be waiting for GPS to turn off...). Second option would be to delay releasing (performSelector:withObject:afterDelay: comes to mind), but that's more of a workaround for badly implemented APIs.

所以,如果发布它不是一个好主意,那么是什么?

So if releasing it is not a good idea, then what is ?

那么,发布CLLocationManager释放这些几个字节的内存不会保存您的应用程序从系统内存不足时终止。无论如何,是真的只有一次,你需要当前用户的位置吗?

Well, what do you really gain by releasing a CLLocationManager ? Freeing those few bytes of memory is not going to save your app from terminating when system is out of memory. Anyway, is it really only once that you need the current user's location ?

我建议你将与CLLocationManager相关的任务封装到一个单独的类,可能甚至单例 - 类将成为其委托,它将负责与CLLocationManager通信并通知您的应用程序有关结果(可能通过发送 NSNotification )。 CLLocationManager将从该类的 dealloc 中释放,而不是作为委托回调的结果。 stopUpdatingLocation 应该足够了,释放几个字节的内存 - 好,你可以做到当你的应用程序进入后台,但只要你的应用程序运行,释放那些几个字节不

I suggest that you encapsulate tasks related to CLLocationManager into a separate class, probably even a singleton - that class would become its delegate, and it would take care of communicating with CLLocationManager and informing your application about results (probably by sending NSNotification). CLLocationManager would be released from that class's dealloc, and never as a result of a delegate callback. stopUpdatingLocation should suffice, freeing few bytes of memory - well, you can do it when your app is entering background, but as long as your app runs, freeing those few bytes do not make any significant improvement in memory consumption.

**添加**

这是很自然的,正确的一个代表拥有一个对象的所有权,代表它作为代理。但是委托不应该作为回调的结果释放对象。这里有一个例外,它是回调告诉你处理结束了。一个例子是 NSURLConnection connectionDidFinishLoading:,其中在文档代理将不会收到进一步的消息 。你可以让一个类下载一堆文件,每个文件都有不同的 NSURLConnection (将你的类作为委托),分配并释放它们作为文件下载进度。

It's natural, and correct, for a delegate to have ownership of an object for which it acts as delegate. But the delegate should not release the object as a result of a callback. There's one exception to this though, and it is the callback telling you processing is over. As an example for this is NSURLConnection's connectionDidFinishLoading: which states in documentation "The delegate will receive no further messages". You can have a class downloading bunch of files, each with a different NSURLConnection (having your class as delegate), allocating AND releasing them as download of files progress.

CLLocationManager 的行为不同。您的程序中应该只有一个CLLocationManager实例。该实例由一些代码管理,可能是单例 - 可以在应用程序进入后台时释放,在唤醒时重新初始化。 CLLocationManager 的生命周期与其管理类相同,它也充当委托。

CLLocationManager's behavior is different. You should have only one instance of CLLocationManager in your program. That instance is managed by some code, probably a singleton - one that could be released when application goes into background, reinitialized when it wakes up. CLLocationManager's lifespan would be the same as of its managing class, which acts as delegate as well.