gpt4 book ai didi

objective-c - Objective-C block : Is there a way to avoid 'self' being retained?

转载 作者:太空狗 更新时间:2023-10-30 03:51:34 24 4
gpt4 key购买 nike

我尽量把它写下来,但描述起来并不容易——所以感谢阅读 =)

我是开源 iPhone 框架的主要开发人员 Sparrow . Sparrow 以 Flash AS3 库为蓝本,因此具有与 AS3 一样的事件系统。目前,该系统通过指定选择器来工作——但我很乐意通过允许为事件监听器使用 block 来扩展该系统。但是,我遇到了内存管理问题。

我将向您展示一个典型的事件用例 - 现在处理它们。

// init-method of a display object, inheriting from 
// the base event dispatcher class
- (id)init
{
if (self = [super init])
{
// the method 'addEventListener...' is defined in the base class
[self addEventListener:@selector(onAddedToStage:)
atObject:self
forType:SP_EVENT_TYPE_ADDED_TO_STAGE];
}
return self;
}

// the corresponding event listener
- (void)onAddedToStage:(SPEvent *)event
{
[self startAnimations]; // call some method of self
}

这很简单:当一个对象被添加到显示列表时,它会收到一个事件。目前,基类将事件监听器记录在 NSInvocation 对象数组中。 NSInvocation 的创建方式保留其目标和参数。 (用户可以让它这样做,但在 99% 的情况下,这是没有必要的)。

不保留这些对象是一个有意识的选择:否则,即使用户在 dealloc 方法中删除了事件监听器,上面的代码也会导致内存泄漏!原因如下:

- (id)init
{
if (self = [super init])
{
// [self addEventListener: ...] would somehow cause:
[self retain]; // (A)
}
return self;
}

// the corresponding event listener
- (void)dealloc
{
// [self removeEventListener...] would cause:
[self release]; // (B)
[super dealloc];
}

乍一看,这似乎很好:init 方法中的 retain 与 dealloc 方法中的 release 配对。然而,这是行不通的,因为永远不会调用 dealloc 方法,因为保留计数永远不会达到零!

正如我所说,“addEventListener...”方法正是出于这个原因,在其默认版本中不保留任何内容。由于事件的工作方式(它们几乎总是由“自身”或无论如何保留的子对象分派(dispatch)),这不是问题。

但是,现在我们来到了问题的核心部分:我不能用 block 来做到这一点。查看事件处理的 block 变体,因为我希望它具有:

- (id)init
{
if (self = [super init])
{
[self addEventListenerForType:ADDED_TO_STAGE block:^(SPEvent *event)
{
[self startAnimations];
}];
}
return self;
}

这看起来很棒,而且非常易于使用。但是:当用户在“self”上调用方法或在 block 中使用成员变量时——好吧,几乎总是这样—— block 将自动保留“self”,并且永远不会释放对象.

现在,我知道任何用户都可以通过对 self 进行 __block 引用来纠正这个问题,如下所示:

__block id blockSelf = self;
[self addEventListenerForType:ADDED_TO_STAGE block:^(SPEvent *event)
{
[blockSelf startAnimations];
}];

但是,老实说,我敢肯定几乎所有用户都不知道这样做或忘记这样做。 API 不仅应该易于使用,还应该难以滥用,而这显然违反了这一原则。 API 的用户肯定会滥用它。

让我感到困扰的是,我知道不必保留“self”——它在我当前的实现中工作而无需保留它。所以想告诉区 block 他不需要保留 self ——我,图书馆,应该告诉区 block ,这样用户就不必考虑它。

在我的研究中,我还没有找到这样做的方法。而且我想不出一种方法来更改我的架构以适应 block 的限制。

有人知道我能做些什么吗?
即使您没有,也感谢您阅读到这里——我知道这是一个冗长的问题;-)

最佳答案

我与 Apple 支持讨论了这个话题,他们告诉我我的预期:目前,没有办法让我告诉 block 它不应该保留 self 。但是,他们提供了两种解决该问题的方法。

第一个是将 self 作为参数传递给 block 而不是 block 变量。 API 将变为:

[self addEventListenerForType:ADDED_TO_STAGE 
block:^(id selfReference, SPEvent *event)

因此,API 将负责传递(非保留的) self 。开发人员仍然需要被告知他们必须使用此引用而不是 self,但至少它会易于使用。

Apple 在这种情况下使用的另一种解决方案是提供一种与 release/dealloc 不同的方法来关闭监听器。一个很好的例子就是 NSTimer 的“invalidate”方法。它是由于 NSRunLoop 和 NSTimer 之间的内存循环而创建的(按设计)。

关于objective-c - Objective-C block : Is there a way to avoid 'self' being retained?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3975675/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com