如何管理 unsafe_unretained ivars/properties?

发布于 2024-12-29 19:19:16 字数 654 浏览 4 评论 0原文

几周前我开始使用 Objective-C 和 iOS(值得记住),对于糟糕的图表我提前表示歉意!

在此处输入图像描述

上图显示了我对 Web 服务的调用的结构。细箭头表示一个对象创建另一个对象,而粗箭头表示一个对象持有对所指向对象的强(保留)引用。

我相信这包含所谓的“循环引用”,并且在释放对象时会产生问题。

我知道简单的答案是替换一些对弱引用的强引用,我很乐意这样做,但我的项目也针对 iOS 3.2(不是我的决定 - 我无法真正改变这个事实!) 。所以,我认为我说得对,我必须使用 __unsafe_unretained 来代替,但我很担心这些不会自动归零,因为当对象被释放时我最终会遇到 EXC_BAD_ACCESS 问题。 ..

所以我的问题首先是我有循环引用。为了解决这个问题,我必须使用 __unsafe_unretained,这导致了我的第二个问题:如何正确管理这些?

可能相关的一个问题是: NSURLConnection 如何管理它的强引用?我从各种渠道听说它保留了其代表?所以...如果我保留一个 NSURLConnection(也是它的委托)并且它保留我,这也将是一个循环引用,不是吗?它如何解决我的问题?

非常欢迎任何建议!

问候, 缺口

I started objective-c and iOS a couple of weeks ago (worth bearing in mind), and I apologise in advance for the awful diagram!!

enter image description here

The above diagram shows the structure of my calls to a webservice. Thin arrows denote an object creating another object, whereas thick arrows denote an object holding a strong (retained) reference to the pointed-to object.

I believe that this contains what is called a "circular reference" and will create problems when it comes to deallocating the objects.

I understand that the easy answer would be to replace some of the strong references to weak ones, which I'd love to do, except my project is also targeting iOS 3.2 (not my decision - I can't really change this fact!). So, I think I'm right in saying that I have to use __unsafe_unretained instead, but I'm quite worried about the fact that these won't auto-zero, as I'll end up with EXC_BAD_ACCESS problems when objects get deallocated...

So my problem is firstly that I have circular references. To solve, I would have to use __unsafe_unretained, which leads to my second problem: How to correctly manage these?

A question that might be related is: How does NSURLConnection manage it's strong references? I have heard from various sources that it retains its delegate? So...if I retain an NSURLConnection, (and am also its delegate) and it retains me, this would also be a circular reference, no? How does it get around my problem?

Any advice is very welcome!

Regards,
Nick

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

羁绊已千年 2025-01-05 19:19:16

当父对象引用子对象时,应该使用强引用。当子对象引用其父对象时,它应该使用弱引用,也称为 unsafe_unretained。

按照惯例,iOS 中的委托关系通常是弱引用,因此您会发现 Apple 自己的类上的大多数委托属性都被声明为 unsafe_unretained。

因此,您的控制器保留其正在使用的服务,但这些服务仅弱链接回控制器。这样,如果控制器被释放,则可以安全地处理整个批次,而无需任何循环引用。

这样做的危险在于,如果 Web 服务正在执行一些长时间运行的任务,并且控制器在完成之前被释放,那么该服务将留下一个指向它现在已释放的委托的悬空指针。如果它尝试向委托发送消息,例如“我已经完成”,它将崩溃。

有几种方法可以帮助解决这个问题(它们并不相互排斥 - 您应该尽可能尝试全部执行):

1)始终在控制器的 dealloc 方法中将服务的委托属性设置为 nil。这确保了当控制器被释放时,对它的委托引用被设置为 nil(类似于 ARC 弱引用自动执行的粗略手动操作)。

2) 当创建自己的具有委托的服务类时,让它们在运行时保留其委托,然后在完成时释放委托。这样,当服务仍在向其发送消息时,委托对象无法被释放,但一旦服务完成,它仍然会被释放(NSTimer 和 NSURLConnections 都是这样工作的 - 它们在运行时保留其委托并释放它)当他们完成时)。

3)尽量不要让长期运行的服务由诸如视图控制器之类的瞬态事物拥有。考虑创建拥有您的服务的单例对象(共享静态对象实例),这样无论视图层中发生什么情况,服务都可以在后台完成其工作。控制器仍然可以调用该服务,但不拥有它 - 该服务由一个静态对象拥有,该静态对象将在应用程序运行期间存在,因此不存在泄漏或过早发布的风险。该服务可以通过 NSNotifications 而不是委托调用与控制器进行通信,因此它不需要引用可能消失的对象。 NSNotifications 是在多个类之间进行通信而无需创建循环引用的好方法。

When a parent has a reference to a child object, it should use a strong reference. When a child has a reference to it's parent object, it should use a weak reference, aka unsafe_unretained.

By convention, delegate relationships in iOS are usually weak references, so you'll find that most delegate properties on Apple's own classes are declared as unsafe_unretained.

So your controller retains the services that it is using, but the services only weakly link back to the controller. That way, if the controller is released, the whole lot can be safely disposed of without any circular references.

The danger with this is that if the web service is doing some long-running task, and the controller gets released before it has finished, the service is left with a dangling pointer to it's now-deallocated delegate. If it tries to send a message to the delegate, such as "I have finished" it will crash.

There are a few approaches to help solve this (they aren't mutually exclusive - you should try to do them all whenever possible):

1) Always set the delegate properties of your services to nil in your controller's dealloc method. This ensures that when the controller is released, the delegate references to it are set to nil (sort of a crude, manual equivalent of what ARC's weak references do automatically).

2) When creating your own service classes that have delegates, make them retain their delegate while they are running and then release the delegate when they are done. That way the delegate object can't get deallocated while the service is still sending it messages, but it will still get released once the service has finished (NSTimer's and NSURLConnections both work this way - they retain their delegate while they are running and release it when they are done).

3) Try not to have long-running services owned by something transient like a view controller. Consider creating singleton objects (shared static object instances) that own your services, that way the service can do it's job in the background regardless of what's going on in the view layer. The controller can still call the service, but doesn't own it - the service is owned by a static object that will exist for the duration that the app is running, and so there's no risk of leaks or premature releases. The service can communicate with the controller via NSNotifications instead of delegate calls, so there is no need for it to have a reference to an object that may vanish. NSNotifications are a great way to communicate between multiple classes without creating circular references.

不离久伴 2025-01-05 19:19:16

您的所有问题和疑虑都是正确的,并且之前使用 assign(现在更好地命名为 __unsafe_unretained)带来的这个问题就是 Apple 为 weak 开发自动归零功能的原因。但我们多年来一直相当安全地处理分配委托,因此正如您所怀疑的,有一些方法可以做到这一点。

首先,作为实践问题,当您释放您所委托的对象时,您应该始终将自己清除为委托人。在 ARC 之前,这传统上是在 dealloc 中完成的:

- (void)dealloc {
  [tableView_ setDelegate:nil];
  [tableView_ release];
  tableView_ = nil;
}

您仍然应该在 dealloc if delegatesetDelegate:nil代码> 是__unsafe_unretained。这将解决最常见的问题形式(当委托在委托对象之前被释放时)。

关于 NSURLConnection,您也正确地认为它保留了其委托。这是可以的,因为它的生命周期通常比它的委托短得多(而表视图委托几乎总是与表视图具有相同的生命周期)。请参阅“ 如何解决/处理委托 EXC_BAD_ACCESS 错误? Obj C ”,在 ARC 之前的上下文中对此进行更多讨论(相同的概念适用于强/弱的新世界)。

All of your questions and concerns are correct, and this problem with the previous use of assign (now better named __unsafe_unretained) is why Apple developed auto-zeroing for weak. But we've dealt reasonably safely with assign delegates for many years, so as you suspect, there are ways to do it.

First, as a matter of practice, you should always clear yourself as the delegate when your release an object you were delegate for. Pre-ARC, this was traditionally done in dealloc:

- (void)dealloc {
  [tableView_ setDelegate:nil];
  [tableView_ release];
  tableView_ = nil;
}

You should still include that setDelegate:nil in your dealloc if delegate is __unsafe_unretained. This will address the most common form of the problem (when the delegate is deallocated before the delegating object).

Regarding NSURLConnection, you are also correct that it retains its delegate. This is ok because it has a lifespan typically much shorter than its delegate (versus a table view delegate which almost always has the same lifespan as the table view). See " How to work around/handle delegation EXC_BAD_ACCESS errors? Obj C " for more discussion on this in a pre-ARC context (the same concepts apply in the new world of strong/weak).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文