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

查看:34
本文介绍了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

我再次想这是保存上下文的正确位置......这两种方法之间有什么区别(如果有,在这种情况下)?

Again I guess that is the correct place to save the context... what are the differences between both methods (if any, in this case)?

如果不是执行同步服务调用或文件解析,我需要执行异步服务调用怎么办?如何管理这些数据更新?

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 做的任何事情都必须在 performBlockperformBlockAndWait.请注意,保留/释放对于托管对象是线程安全的,因此您不必在这些块之一中保留/释放托管对象的引用计数.

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.

有许多不同之处.我敢肯定还有更多,但这里是我认为最重要的理解.

There are a number of differences. I'm sure there are more, but here are the ones that I think are most important to understand.

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 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 是抢占式的,可以跳过 FIFO 队列.

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.

您很容易在调用 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 不封装用户事件".如果您希望将该块视为不同的用户事件,则必须自己进行.

performBlockAndWait does not encapsulate a "user event." If you want the block to be treated as a distinct user event, you must do it yourself.

performBlock 将块包装在自己的自动释放池中.

performBlock wraps the block in its own autoreleasepool.

performBlockAdWait 不提供唯一的自动释放池.如果您需要,您必须自己提供.

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天全站免登陆