我的块没有保留它的一些对象

发布于 2024-10-10 07:46:42 字数 1827 浏览 5 评论 0 原文

来自块文档:

在引用计数环境中,通过 当您引用时默认 块内的 Objective-C 对象,它 被保留。即使您 只需引用实例变量 对象的。

我正在尝试实现一个完成处理程序模式,其中在执行工作之前向对象提供一个块,并在执行工作之后由接收器执行该块。由于我是一个良好的内存公民,因此该块应该拥有它在完成处理程序中引用的对象,然后当该块超出范围时它们将被释放。我知道,我必须复制该块以将其移动到堆,因为该块将在声明它的堆栈范围内生存。

然而,我的一个对象意外地被释放。经过一番尝试后,发现当块复制到堆时,某些对象不会被保留,而其他对象则会被保留。我不确定我做错了什么。这是我可以生成的最小测试用例:

typedef void (^ActionBlock)(UIView*);

在某些方法的范围内:

NSObject *o = [[[NSObject alloc] init] autorelease];
mailViewController = [[[MFMailComposeViewController alloc] init] autorelease];
NSLog(@"o's retain count is %d",[o retainCount]);
NSLog(@"mailViewController's retain count is %d",[mailViewController retainCount]);
ActionBlock myBlock = ^(UIView *view) {
       [mailViewController setCcRecipients:[NSArray arrayWithObjects:@"[email protected]",nil]];
       [o class];
    };
NSLog(@"mailViewController's retain count after the block is %d",[mailViewController retainCount]);
NSLog(@"o's retain count after the block is %d",[o retainCount]);
Block_copy(myBlock);
NSLog(@"o's retain count after the copy is %d",[o retainCount]);
NSLog(@"mailViewController's retain count after the copy is %d",[mailViewController retainCount]);

我希望两个对象在某个时刻被块保留,并且我当然希望它们的保留计数相同。相反,我得到以下输出:

o's retain count is 1
mailViewController's retain count is 1
mailViewController's retain count after the block is 1
o's retain count after the block is 1
o's retain count after the copy is 2
mailViewController's retain count after the copy is 1

oNSObject的子类)被正确保留,并且不会超出范围。但是,mailViewController 不会被保留,并且会在块运行之前被释放,从而导致崩溃。

From the Blocks documentation:

In a reference-counted environment, by
default when you reference an
Objective-C object within a block, it
is retained. This is true even if you
simply reference an instance variable
of the object.

I am trying to implement a completion handler pattern, where a block is given to an object before the work is performed and the block is executed by the receiver after the work is performed. Since I am being a good memory citizen, the block should own the objects it references in the completion handler and then they will be released when the block goes out of scope. I know enough to know that I must copy the block to move it to the heap since the block will survive the stack scope in which it was declared.

However, one of my objects is getting deallocated unexpectedly. After some playing around, it appears that certain objects are not retained when the block is copied to the heap, while other objects are. I am not sure what I am doing wrong. Here's the smallest test case I can produce:

typedef void (^ActionBlock)(UIView*);

In the scope of some method:

NSObject *o = [[[NSObject alloc] init] autorelease];
mailViewController = [[[MFMailComposeViewController alloc] init] autorelease];
NSLog(@"o's retain count is %d",[o retainCount]);
NSLog(@"mailViewController's retain count is %d",[mailViewController retainCount]);
ActionBlock myBlock = ^(UIView *view) {
       [mailViewController setCcRecipients:[NSArray arrayWithObjects:@"[email protected]",nil]];
       [o class];
    };
NSLog(@"mailViewController's retain count after the block is %d",[mailViewController retainCount]);
NSLog(@"o's retain count after the block is %d",[o retainCount]);
Block_copy(myBlock);
NSLog(@"o's retain count after the copy is %d",[o retainCount]);
NSLog(@"mailViewController's retain count after the copy is %d",[mailViewController retainCount]);

I expect both objects to be retained by the block at some point, and I certainly expect their retain counts to be identical. Instead, I get this output:

o's retain count is 1
mailViewController's retain count is 1
mailViewController's retain count after the block is 1
o's retain count after the block is 1
o's retain count after the copy is 2
mailViewController's retain count after the copy is 1

o (subclass of NSObject) is getting retained properly and will not go out of scope. However mailViewController is not retained and will be deallocated before the block is run, causing a crash.

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

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

发布评论

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

评论(3

乖乖公主 2024-10-17 07:46:42

不要使用-retainCount。

对象的绝对保留计数是没有意义的。

您应该调用 release 的次数与导致对象被保留的次数完全相同。不能少(除非你喜欢泄漏),当然也不能多(除非你喜欢崩溃)。

请参阅内存管理指南了解完整详细信息。

(抄袭@bbum的答案之一)


至于你的问题:

你真的在观察崩溃吗?或者你只是盲目地从臀部射击并认为它可能会崩溃?

从您发布的代码来看,mailViewController 似乎是一个实例变量,在这种情况下,该块将保留 self 而不是实例变量。由于您自动释放实例变量,它会被NSAutoreleasePool清理,就像您所期望的那样。

总之:

  1. 不要使用 -retainCount
  2. 不要autorelease您希望在运行循环之外存在的实例变量。

编辑更多说明:

创建块时将发生以下情况:

  1. 检查其范围内的对象引用。有两个:self->mailViewControllero
  2. self->mailViewController 是结构体 (self) 的成员,因此不会直接保留。相反,保留 self
  3. o 是一个局部变量。保留它。

这是正确的行为。至于您的代码...

  1. o 是使用 +0 保留计数创建的
  2. self->mailViewController 是使用 +0 保留计数创建的
  3. myBlock 创建时保留计数为 +0。 o 现在有 +1 RC,self 也是如此。 self->mailViewController 仍然有 +0 RC
  4. myBlock 已复制 => +1 保留计数

快进到该运行循环周期的末尾。

  1. 当前自动释放池已耗尽。所有保留计数为 +0 的对象都将被释放,包括 self->mailViewController。 self->mailViewController 现在指向已释放的内存,本质上是垃圾。

快进到执行 myBlock 时的某个未来点,

  1. myBlock 尝试调用 self->mailViewController 上的方法。但是,self->mailViewController 不再指向有效的对象,并且您的应用程序崩溃了。

但是,如果 mailViewController 不是实例变量,那么我们需要查看更多代码。我认为您所看到的行为不太可能是块运行时的问题,但有可能。

Do not use -retainCount.

The absolute retain count of an object is meaningless.

You should call release exactly same number of times that you caused the object to be retained. No less (unless you like leaks) and, certainly, no more (unless you like crashes).

See the Memory Management Guidelines for full details.

(cribbed from one of @bbum's answers)


As for your question:

Are you actually observing a crash? Or are you just shooting blindly from the hip and thinking that it might crash?

From the code you posted, it would appear that mailViewController is an instance variable, in which case the block would be retaining self instead of the instance variable. And since you autoreleased your instance variable, it's getting cleaned up by an NSAutoreleasePool just like you'd expect.

So in summary:

  1. Do not use -retainCount.
  2. Do not autorelease instance variables that you want to exist beyond this turn of the run loop.

edit A bit more clarification:

Here's what's going to happen when your block is created:

  1. Inspect object references in its scope. There are two: self->mailViewController and o.
  2. self->mailViewController is a member of a struct (self), so it is not retained directly. Retain self instead.
  3. o is a local variable. Retain it.

This is proper behavior. As for your code...

  1. o is created with a +0 retain count
  2. self->mailViewController is created with a +0 retain count
  3. myBlock is created with a +0 retain count. o now has a +1 RC, as does self. self->mailViewController still has a +0 RC
  4. myBlock is copied => +1 retain count

Fast forward to the end of this run loop cycle.

  1. The current autorelease pool is drained. All objects with a +0 retain count are deallocated, including self->mailViewController. self->mailViewController now points to deallocated memory, and is essentially garbage.

Fast forward to some future point when myBlock is executed

  1. myBlock attempts to invoke a method on self->mailViewController. However, self->mailViewController no longer points to a valid object, and your app crashes.

However, if mailViewController is not an instance variable, then we need to see more code. I think it'd be highly unlikely that the behavior you're seeing is a problem with the blocks runtime, but it is possible.

横笛休吹塞上声 2024-10-17 07:46:42

文档不再这么说了。现在它正确地说:

在手动引用计数环境中,使用局部变量
复制块时保留块内的内容。

The documentation no longer says that. It now correctly says:

In a manually reference-counted environment, local variables used
within the block are retained when the block is copied.

肩上的翅膀 2024-10-17 07:46:42

由于“mailViewController”是当前类实例的成员,因此该块实际上在这里保留“self”。

As the "mailViewController" is a member of your current class instance, so the block actually retain "self" here.

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