我的代码中发现了一个僵尸,现在该怎么办?

时间:2014-01-08 20:54:07

标签: ios objective-c multithreading background-process nszombie

我正在开发一个基于时间轴的应用程序,用于显示来自App.net的帖子。当我点击一个单元格时,我加载了一个加载数据控制器对象的TableViewController。然后,此控制器对象将加载数据对象。控制器对象进入后台线程,获取线程中的帖子。当它获取它们时,它将它们发送到数据对象,该数据对象也进入后台线程以将格式应用于帖子并预先计算显示帖子文本所需的高度。

然后,我从后台主题调用dispatch_async(dispatch_get_main_queue(), ^{...并发布通知:

[[NSNotificationCenter defaultCenter] postNotificationName:PostStreamDataDidUpdateNotification object:self];
在这种情况下,

self是TableViewController用于填充其单元格的数据对象。发布此通知时,该程序会发出激烈的异议,因为技术上可以由导航控制器释放self(数据对象),数据控制器对象和TableViewController。当我加载一个线程,因此进入后台线程,然后从导航控制器弹出视图时,会发生这种情况。 TableViewController和它拥有的一切一样发布:数据控制器,数据对象等。

我不知道该怎么办。从互联网上的搜索,我可以推断出我遇到了僵尸问题。听起来很容易理解,直到我找不到任何人在谈论如何避免或修复它们。仪器使它们易于查找,但如何避免它们......

我认为,如果我宣布属性,将某些内容设置为强引用可能是个坏主意。

目前TableViewController声明数据控制器(PRSPostStreamDataController):

@property (nonatomic, strong) PRSPostStreamDataController *postStreamDataController;

该数据控制器对象声明了数据对象:

@property (nonatomic, strong, readonly) PRSPostStreamData *data;

有些问题在脑海中浮现:

  • 我应该在进入后台线程之前调用newSelf = [self copy];然后返回newSelf作为通知对象吗? (我试过这个,但此时我只是在画布上扔油漆)

  • 我应该在dealloc方法中取消后台线程吗?如果是这样,那怎么办? This question有一个非常类似的前提,除了请求完成,我的问题在于后台线程。我不存在简单的delegate = nil;

  • 测试self = nil似乎是徒劳的,因为Xcode调试器告诉我自己不是零。

  • 我是否应该完全放弃对后台线程的希望?


我是新的堆栈溢出(以及iOS开发),但我知道问题应该是单一和简洁的。所以问题是:

很高兴我可以使用Instruments在我的应用程序中追踪Zombies,我很高兴能看到负责的方法但是如何避免在后台线程上创建僵尸对象?

这个问题很广泛,但我希望我提供的信息可以缩小范围。如果我能改进问题,请告诉我

2 个答案:

答案 0 :(得分:0)

一种可能的解决方案是分离您的数据访问层,即。模型,到一个独立的服务(通常实现为单身)。这样,数据由此服务拥有和管理,而不是由显示数据的视图控制器拥有和管理。此模式有时称为Model-View-Controller-Store。它有时也会被其他名字所取代 - 查找它。

此SO问题还涉及类似问题:Pattern for preloading data for subsequent view to be displayed

答案 1 :(得分:0)

我在Palimondo's answer的评论中提到我试图使用KVO来解决我的问题。事实证明,KVO比我想象的要复杂一点,或者至少对于这个特殊的问题。我想使用KVO因为它意味着我可以从ViewController对象而不是可能在后台线程上进入“僵尸模式”的对象进行监视。但是,它不允许我在我想要的时候发送更新。我尝试监控数据对象中的BOOL,但这与使用NSNotificationCenter发送通知一样简单。

所以,最后我决定在我的数据对象中添加一个BOOL属性,在发布通知之前将对其进行检查。以下是一种方法的简化代码:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{

    // Calculate the cell heights, text colors, etc.

    dispatch_async(dispatch_get_main_queue(), ^{
        if (self.hasPopped == NO) {
            [[NSNotificationCenter defaultCenter] postNotificationName:PostStreamDataDidUpdateNotification object:self];
        }
    });
});

默认情况下,self.hasPopped将等于NO,因此此代码将始终运行。当弹出导航堆栈的视图时,它将被释放,因此在拥有此数据对象的TableViewController的dealloc方法中,我发送通知:

[[NSNotificationCenter defaultCenter] postNotificationName:PostStreamHasBeenPopped object:self.postStreamDataController.data];

数据对象已注册接收此通知,因此在将其设置为self.hasPopped时会YES。现在,如果任何后台进程返回到主线程,将跳过导致崩溃的通知(并且希望最终释放数据对象)。我还可以通过测试数据对象中的notification.object == self来验证哪个视图控制器发送了通知。如果我有多个TableViewControllers同时运行,比如提及和全局时间线,这可能很有用。

如果你注意到我没有真正回答这个广泛的问题,那是因为我不确定如何。我已经解决了僵尸所遇到的具体问题,所以我觉得我应该为那些像我一样的人做好准备。