[译文]如何才能不崩溃 #5: Threading, part 2

原文地址:

http://inessential.com/2015/05/26/how_not_to_crash_5_threading_part_2

前面发布的关于线程的文章 留下了一个开发的问题,关于怎样让代码运行在主线程之外进行沟通 - 安全的 - 返回到主线程。

对象创建的后台任务掌握着任务的结果。这是一个硬性的规则。

通常创建任务的对象在整个app的生命周期中一直存活着。图片缓存可能是个例子 - 缓存可能会在app的生命周期中被清空,但是缓存对象在这段时间内一直存活着。

另一个例子是类似于 Vesper 的 VSAccount 对象。总是有一个 VSAccount 实例。用户也许有也许没有一个服务端账户。用户可能会改变他们的账户。但是一直会有一个 VSAccount 对象贯穿于整个app的生命周期。

(注意:显然,一个app管理多个账户会做不同的事情。但是 Vesper 管理最多一个服务端账户,所以,这工作的很好。在 Vesper 的例子中,多个账户在 YAGNI(You Aren't Going to Need It,你并不需要)规则下失效。)

VSAccount 对象负责向服务端发送http请求,并接收结果。它在后台队列中将JSON转化为中间对象。

它调用JSON处理器处理NSData数据,并拥有block回调。当处理器处理完成,它会在主线程上回调block。

if (callback) {
  dispatch_async(dispatch_get_main_queue(), ^{
    callback(parsedObjects)
  });
}

这对我来说是一个公共的模式 - 在主线程上调用一个携带参数的block - 我对此有一个公共的方法。JSON处理器实际上在做类似这样的事情:

BSCallBlockWithParameter(callback, parsedObjects);

BSCallBlockWithParameter看起来像这样:

if (!callback)
  return;
}
dispatch_async(dispatch_get_main_queue(), ^{
  callback(parsedObjects);
});

我一直这样使用。超级方便。

做这项工作的关键

我从来不想去关心对象创建了后台任务之后,可能会消失的问题。所以我只在存活于整个app生命周期的对象中创建后台任务。

你不想进入这样一个情况,一个对象创建了一个后台任务后,在任务完成回调之前消失了(部分或者全部)。这是一个潜在的、复杂的问题,我甚至不想考虑。(首先,我讨厌 weak-self (原文:I hatethe weak-self dance, for starters.))

这正是你编写不崩溃的代码时所需要的心态:如果某件事情比较复杂,那么就会有出错的倾向。寻找一个方法让它变得更简单。

(你可以指出一个复杂的事情,并且证明它是对的 - 但是,你之后会不会怀疑,然后感觉需要检查一下代码?如果它错了,你是否会打破它?或者,如果其他人接触它呢?)

所以我做了这么一个简单的事情:使用不会被释放的对象。

但是有一个逃生出口需要记住:一个回调block可以安全的调用类方法和C函数。实例方法并不安全,如果实例消失了 - 但是类方法和C函数在概念上可以安全的被调用。

我并不经常使用这方面的知识,但是我发现它越来越有用了。谨慎使用。

Article by 付军