performBlock:和performBlockAndWait之间的行为差​​异:? [英] Behavior differences between performBlock: and performBlockAndWait:?

查看:854
本文介绍了performBlock:和performBlockAndWait之间的行为差​​异:?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在私人队列中创建一个 NSManagedObjectContext 以处理从文件和/或服务获取的数据更新:

I'm creating an NSManagedObjectContext in a private queue to handle data updates I take from files and/or services:

NSManagedObjectContext *privateContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
privateContext.persistentStoreCoordinator = appDelegate.persistentStoreCoordinator;

因为我使用私人队列,我不完全理解 performBlock: performBlockAndWait:方法...要执行我的数据更新,我目前正在这样做:

Since I'm using a private queue, I don't fully understand the difference between performBlock: and performBlockAndWait: methods... To perform my data updates I'm currently doing this:

[privateContext performBlock: ^{

        // Parse files and/or call services and parse
        // their responses

        // Save context
        [privateContext save:nil];

        dispatch_async(dispatch_get_main_queue(), ^{
            // Notify update to user
        });
    }];

在这种情况下,我的数据更新是同步和顺序,所以我想这是正确的地方保存上下文,对吗?如果我做错了,我会感激,如果你让我知道。另一方面,这段代码是否等效?:

In this case, my data updates are made synchronoulsy and sequentially, so I suppose that is the correct place to save the context, right? If I'm doing something wrong, I'd appreciate if you let me know. On the other hand, would this code be equivalent?:

[privateContext performBlockAndWait: ^{

        // Parse files and/or call services and parse
        // their responses

        // Save context
        [privateContext save:nil];
    }];

// Notify update to user

再次,我想这是正确的

如果不执行同步服务调用或文件解析(如果有的话) ,我需要执行异步服务调用吗?如何管理这些数据更新?

What if instead of performing synchronous service calls or files parsing, I need to perform asynchronous service calls? How would these data updates be managed?

提前感谢

推荐答案

你是正确的,任何你想用MOC做的事情必须在 performBlock performBlockAndWait 中完成。请注意,retain / release对于托管对象是线程安全的,所以你不必在这些块之一内保留/释放托管对象的引用计数。

You are correct in that anything you want to do with a MOC must be done within either performBlock or performBlockAndWait. Note that retain/release is thread safe for managed objects, so you don't have to be inside one of those blocks to retain/release reference counts on managed objects.

它们都使用同步队列来处理消息,这意味着一次只能执行一个块。那么,这几乎是真的。请参阅 performBlockAndWait 的说明。无论如何,对MOC的访问将被序列化,使得每次只有一个线程访问MOC。

They both utilize a synchronous queue to process messages, which means that only one block will execute at a time. Well, that's almost true. See the descriptions of performBlockAndWait. In any event, the access to the MOC will be serialized such that only one thread is accessing the MOC at a time.

tl; dr不要担心关于差异,并始终使用 performBlock

有很多不同之处。

performBlock 是异步的,因为它立即返回,并且块在未来的某个时间在某些未公开的线程上执行。通过 performBlock 提供给MOC的所有块将按照它们添加的顺序执行。

performBlock is asynchronous, in that it returns immediately, and the block is executed at some time in the future, on some undisclosed thread. All blocks given to the MOC via performBlock will execute in the order they were added.

performBlockAndWait 是同步的,因为调用线程将等待,直到块在返回之前已经执行。该块是否在其他线程中运行,或者在调用线程中运行并不重要,而是一个不可信的实现细节。

performBlockAndWait is synchronous, in that the calling thread will wait until the block has executed before returning. Whether the block runs in some other thread, or runs in the calling thread is not all that important, and is an implementation detail that can't be trusted.

注意,然而,它可以实现为嘿,一些其他线程,去运行这个块,我要坐在这里什么都不做,直到你告诉我已经完成。或者,它可以实现为嘿,Core Data,给我一个锁,防止所有其他块运行,所以我可以在自己的线程上运行此块。或者它可以以某种其它方式实现。

Note, however, that it could be implemented as "Hey, some other thread, go run this block. I'm gonna sit here doing nothing until you tell me it's done." Or, it could be implemented as "Hey, Core Data, give me a lock that prevents all those other blocks from running so I can run this block on my own thread." Or it could be implemented in some other way. Again, implementation detail, which could change at any time.

我会告诉你,上次我测试它, performBlockAndWait 在调用线程上执行块(意味着上段中的第二个选项)。这只是真正的信息,以帮助您了解正在发生什么,不应以任何方式依赖。

I'll tell you this though, the last time I tested it, performBlockAndWait executed the block on the calling thread (meaning the second option in the above paragraph). This is only really information to help you understand what is going on, and should not be relied upon in any way.

performBlock 始终异步,因此不可重入。嗯,有些人可能认为它是可重入的,因为你可以从一个块中调用它,该块用 performBlock 调用。但是,如果这样做,所有调用 performBlock 将立即返回,并且块将不会执行,直到至少当前执行的块完成其工作。

performBlock is always asynchronous, and is thus not reentrant. Well, some may consider it reentrant, in that you can call it from within a block that was called with performBlock. However, if you do this, all calls to performBlock will return immediately, and the block will not execute until at least the currently executing block completely finishes its work.

[moc performBlock:^{
    doSomething();
    [moc performBlock:^{
      doSomethingElse();
    }];
    doSomeMore();
}];

这些函数将始终以此顺序执行:

These functions will always be executed in this order:

doSomething()
doSomeMore()
doSomethingElse()

performBlockAndWait 始终同步。此外,它也是可重入的。多个调用不会死锁。因此,如果你在一个块内部调用 performBlockAndWait ,因为另一个 performBlockAndWait ,那么没关系。您将获得预期的行为,因为第二次调用(和任何后续调用)不会导致死锁。此外,第二个将在它返回之前完全执行,如你所期望的。

performBlockAndWait is always synchronous. Furthermore, it is also reentrant. Multiple calls will not deadlock. Thus, if you end up calling performBlockAndWait while you are inside a block that was being run as a result of another performBlockAndWait, then it's OK. You will get the expected behavior, in that the second call (and any subsequent calls) will not cause a deadlock. Furthermore, the second one will completely execute before it returns, as you would expect.

[moc performBlockAndWait:^{
    doSomething();
    [moc performBlockAndWait:^{
      doSomethingElse();
    }];
    doSomeMore();
}];

这些函数将始终以此顺序执行:

These functions will always be executed in this order:

doSomething()
doSomethingElse()
doSomeMore()



FIFO



FIFO代表先入先出,意味着块将按照它们被放入的顺序执行内部队列。

FIFO

FIFO stands for "First In First Out" which means that blocks will be executed in the order in which they were put into the internal queue.

performBlock 始终遵循内部队列的FIFO结构。

performBlock always honors the FIFO structure of the internal queue. Every block will be inserted into the queue, and only run when it is removed, in FIFO order.

根据定义, performBlockAndWait 断开FIFO排序,因为它跳过已经排队的块的队列。

By definition, performBlockAndWait breaks FIFO ordering because it jumps the queue of blocks that have already been enqueued.

使用提交的块 performBlockAndWait 不必等待队列中运行的其他块。有很多方法可以看到这一点。一个简单的一个是这个。

Blocks submitted with performBlockAndWait do not have to wait for other blocks that are running in the queue. There are a number of ways to see this. One simple one is this.

[moc performBlock:^{
    doSomething();
    [moc performBlock:^{
      doSomethingElse();
    }];
    doSomeMore();
    [moc performBlockAndWait:^{
      doSomethingAfterDoSomethingElse();
    }];
    doTheLastThing();
}];

这些函数将始终以此顺序执行:

These functions will always be executed in this order:

doSomething()
doSomeMore()
doSomethingAfterDoSomethingElse()
doTheLastThing()
doSomethingElse()

在这个例子中很明显,这就是为什么我使用它。但是,如果你的MOC从多个地方得到东西。可能有点混乱。

It's obvious in this example, which is why I used it. Consider, however, if your MOC is getting stuff called on it from multiple places. Could be a bit confusing.

要记住的一点是, performBlockAndWait 是抢占式的,

The point to remember though, is that performBlockAndWait is preemptive and can jump the FIFO queue.

您永远不会得到一个死锁调用 performBlock 。如果你在块中做一些蠢事,那么你可能会死锁,但调用 performBlock 将永远不会死锁。你可以从任何地方调用它,它只是将块添加到队列,并在将来的某个时间执行它。

You will never get a deadlock calling performBlock. If you do something stupid inside the block, then you could deadlock, but calling performBlock will never deadlock. You can call it from anywhere, and it will simply add the block to the queue, and execute it some time in the future.

你可以很容易得到调用< c $ c> performBlockAndWait ,特别是如果你从一个外部实体可以在嵌套上下文中调用或不加区分的方法调用它。具体来说,如果父进程对子进程调用 performBlockAndWait ,您几乎可以确保死锁您的应用程序。

You can easily get deadlocks calling performBlockAndWait, especially if you call it from a method that an external entity can call or indiscriminately within nested contexts. Specifically, you are almost guaranteed to deadlock your applications if a parent calls performBlockAndWait on a child.

Core Data认为用户事件是对 processPendingChanges 的调用之间的任何事件。您可以阅读为什么这种方法很重要的细节,但在用户事件中会发生什么对通知,撤销管理,删除传播,更改合并等有影响。

Core Data considers a "user event" to be anything between calls to processPendingChanges. You can read the details of why this method is important, but what happens in a "user event" has implications on notifications, undo management, delete propagation, change coalescing, etc.

performBlock 封装了一个用户事件,这意味着代码块在对 processPendingChanges 的不同调用之间自动执行。

performBlock encapsulates a "user event" which means the block of code is automatically executed between distinct calls to processPendingChanges.

performBlockAndWait 未封装用户事件。

performBlock 将块封装在自己的autoreleasepool中。

performBlock wraps the block in its own autoreleasepool.

performBlockAdWait 不提供唯一的autoreleasepool。

performBlockAdWait does not provide a unique autoreleasepool. If you need one, you must provide it yourself.

个人而言,我不相信是很多很好的理由使用 performBlockAndWait 。我相信有人有一个用例,不能以任何其他方式完成,但我还没有看到它。如果有人知道该用例,请与我共享。

Personally, I do not believe there are very many good reasons to use performBlockAndWait. I'm sure someone has a use case that can't be accomplished in any other way, but I've yet to see it. If anyone knows of that use case, please share it with me.

最接近的是调用 performBlockAndWait 上下文(不要在 NSMainConcurrencyType MOC上执行此操作,因为它可能会锁定您的UI)。例如,如果要确保数据库在当前块返回之前已完全保存到磁盘,其他块则有机会运行。

The closest is calling performBlockAndWait on a parent context (don't ever do this on a NSMainConcurrencyType MOC because it could lock up your UI). For example, if you want to ensure that the database has completely saved to disk before the current block returns and other blocks get a chance to run.

因此,前一段时间,我决定将Core Data作为一个完全异步的API。因此,我有很多核心数据代码,我没有一个单独的调用 performBlockAndWait ,在测试之外。

Thus, a while ago, I decided to treat Core Data as a completely asynchronous API. As a result, I have a whole lot of core data code, and I do not have one single call to performBlockAndWait, outside of tests.

生活是这样的好多了。当我认为它必须有用或者他们不会提供它时,我的问题要比我回来的要少。

Life is much better this way. I have much fewer problems than I did back when I thought "it must be useful or they wouldn't have provided it."

现在,我根本没有任何需要 performBlockAndWait 。结果,也许它改变了一些,我只是错过了它,因为它不再对我感兴趣...但我怀疑。

Now, I simply no longer have any need for performBlockAndWait. As a result, maybe it has changed some, and I just missed it because it no longer interests me... but I doubt that.

这篇关于performBlock:和performBlockAndWait之间的行为差​​异:?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆