BlocksKit bk_apply块中的非确定性崩溃

时间:2016-04-22 19:11:07

标签: objective-c objective-c-blocks

我有一个使用bk_apply构建NSMutableDictionary的函数,BlocksKit是第三方块实用程序库documentation提供的方法。该函数的测试套件通常可以正常通过,但每次运行它都会崩溃。

NSMutableDictionary *result = [[NSMutableDictionary alloc] init];

[inputSet bk_apply:^(NSString *property) {
    NSString *localValueName = propertyToLocalName[property];
    NSObject *localValue = [self valueForKey:localValueName];

    result[property] = localValue ?: defaults[property];                // Crash

    // Convert all dates in result to ISO 8601 strings
    if ([result[property] isKindOfClass:[NSDate class]]) {              // Crash
        result[property] = ((NSDate *)result[property]).ISODateString;  // Crash
    }

}];

崩溃总是发生在引用result的行上,但每次都不是同一行。

检查调试器中result的内容,我发现了很奇怪的值,比如

po result
{
    val1 = "Some reasonable value";
    val2 = "Also reasonable value";
    (null) = (null);
}

NSDictionary无法拥有null个键或值,因此很明显会违反某些不变量。

造成此次崩溃的原因是什么?如何解决?

1 个答案:

答案 0 :(得分:2)

来自BlocksKit thread schedulerbk_apply

  

枚举将在适当的后台队列上进行。这将   有明显的速度提升,特别是在双核设备上,但是   您必须了解您发送的对象的线程安全性   在街区内。

上面的代码在线程方面非常不安全,因为它在多个线程上读取和写入可变变量。

崩溃的间歇性来自于bk_each是非确定性的事实。当多个线程访问共享内存发生以按顺序而不是并行安排执行时,崩溃不会发生。因此可以“幸运”#34;一些甚至大部分时间,但代码仍然是错误的。

调试器打印输出是危险的一个很好的例子。暂停的线程最有可能从result读取,而另一个线程执行插入。

NSMutableDictionary插入可能不是原子的;示例步骤可能是,

  1. 为新条目分配内存
  2. 将条目的密钥复制到内存中
  3. 将条目的值复制到内存中
  4. 如果您从步骤 1 2 之间的其他线程中读取字典,您将看到已分配内存的条目,但内存中不包含任何值。

    最简单的解决方法是切换到{{3}}。 bk_eachbk_apply做同样的事情,但它以保证顺序执行的方式实现。