实现可重用线程安全核心数据模式时的问题/问题



我在实现本教程中概述的线程安全核心数据概念时遇到问题。我的目标是拥有可重用的代码部分,可以接收参数,执行核心数据操作(添加、更新、删除),然后在完成后异步回调。

因此,这里是"安全"修改核心数据对象的块:

+ (void)saveDataInContext:(void(^)(NSManagedObjectContext *context))saveBlock
{
    NSManagedObjectContext *context = [NSManagedObjectContext context];
    [context setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
    [defaultContext setMergePolicy:NSMergeObjectByPropertyStoreTrumpMergePolicy];
    [defaultContext observeContext:context];                    
 
    block(context);                                     
    if ([context hasChanges])                               
    {
        [context save];
    }
} 

根据我的理解,这执行了一段代码?我不明白"上下文"是如何传入数字的。这是块签名的一部分吗?

因此,这是在后台执行操作并添加完成调用的包装器:

+ (void)saveDataInBackgroundWithContext:(void(^)(NSManagedObjectContext *context))saveBlock completion:(void(^)(void))completion
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        [self saveDataInContext:saveBlock];
        dispatch_sync(dispatch_get_main_queue(), ^{
            completion();
        });
    });
}

下面是使用它的示例:

NSArray *listOfPeople = ...;
[NSManagedObjectHelper saveDataInBackgroundWithContext:^(NSManagedObjectContext *localContext){
    for (NSDictionary *personInfo in listOfPeople)
    {
        PersonEntity *person = [PersonEntity createInContext:localContext];
        [person setValuesForKeysWithDictionary:personInfo];
    }
} completion:^{
    self.people = [PersonEntity findAll];
}];

这里传递的"本地上下文"是什么?我认为我在这里的大部分问题都围绕着不理解块。

简要浏览一下该教程,就会发现它正在谈论神奇的记录。 我从来没有用过它,所以我不能代表它。

// This declares a class method that returns void and takes a block as parameter.
// The block returns void and has one parameter, namely, a pointer to an
// NSManagedObjectContext object.
+ (void)saveDataInContext:(void(^)(NSManagedObjectContext *context))saveBlock;

你会像这样称呼该方法...

[SomeClass saveDataInContext:^(NSManagedObjectContext *context){
    // Some code
}];

这意味着您将代码块传递给函数。 在某些时候,它将执行您提供给它的代码。 当它这样做时,它将把托管对象上下文传递到块中,以便它可以对它做一些事情。

现在,看看该方法的实现...

+ (void)saveDataInContext:(void(^)(NSManagedObjectContext *context))saveBlock
{
    // Create a MOC - note there is no concurrency type, so it will get
    // NSConfinementConcurrencyType, which means it must be used exclusively
    // from the thread in which it was created.  Since it is a local variable
    // and it gets destroyed after this function is called, that should be cool
    // PROVIDED the using block does not do anything untoward with it.
    NSManagedObjectContext *context = [NSManagedObjectContext context];
    // Set the merge policy
    [context setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
    // MR must set some default context...
    // Some how the above context needs a persistent store to save...
    [defaultContext setMergePolicy:NSMergeObjectByPropertyStoreTrumpMergePolicy];
    // Probably setting up notification handler for DidSave
    [defaultContext observeContext:context];                    

    // Now, this is where the block you passed in gets called.
    // Note, that the managed object context has already been setup for you.
    // Now that it's setup, the block of code that you passed in is going
    // to be called, and it will be given a context that it can use to execute
    // code in the calling thread.
    block(context);                 
    // If you changed something to the context in your block of code, the save.
    if ([context hasChanges])                               
    {
        [context save];
    }
} 

让我们重新审视一下调用此方法的代码...

[SomeClass saveDataInContext:^(NSManagedObjectContext *context){
    // Now, the saveDataInContext method has been called.  However, inside
    // that method, a call was made to the block that was passed in.
    // That would be this here block of code.  So, if you look up in
    // the method, where is calls "block(context)" this block of code will
    // be executed right there.  Mentally, you can cut and paste this code
    // in that spot.
    // The context parameter is the context that was passed to this block.
    // you can use it to do any Core Data stuff...
}];

现在,这段代码非常相似,但它需要两个块。 一个用于在上下文中执行一些代码,另一个是在异步保存完成后执行的块。

保存块应该很熟悉。 它与上面示例中的概念相同。

完成是一个块,它返回 void,并且不采用参数。 当所有工作都完成后,它将被调用。

+ (void)saveDataInBackgroundWithContext:(void(^)(NSManagedObjectContext *context))saveBlock completion:(void(^)(void))completion
{
    // Dispatch some work on one of the global concurrent queues.  It will
    // get done on some thread, nobody knows which one, but it does not matter
    // because the code in this block calls saveDataInContext, and passes the
    // block it was given that does some modifications to the context.
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        [self saveDataInContext:saveBlock];
        // Now, when the above work is done, we are still running in some random
        // thread.  I guess the library wants all callbacks to happen on the main
        // thread, so this block is dispatched on the main thread.  Note that it
        // calls the second bock passed in as the completion block.
        // So the <saveBlock> will be run on some random thread, and then, when
        // it is done, the <completion> block will be called on the main thread.
        dispatch_sync(dispatch_get_main_queue(), ^{
            completion();
        });
    });
}

像前面一样,当你调用该方法时,你可以在心里用你传入的第一个块替换 ,并用第二个块替换。

[NSManagedObjectHelper saveDataInBackgroundWithContext:^(NSManagedObjectContext *localContext){
    // This is the first block.  It gets executed where you see <saveBlock>
    // being used in the earlier method.  You are being given the already
    // prepared MOC, and it's name is <localContext>.  Do your managed object
    // context stuff with it.  Note that it will be running in some unknown thread.
    for (NSDictionary *personInfo in listOfPeople)
    {
        PersonEntity *person = [PersonEntity createInContext:localContext];
        [person setValuesForKeysWithDictionary:personInfo];
    }
} completion:^{
    // Now, this is the second block, which is run when all the core data saving
    // has been completed.  It will run on the main thread.
    self.people = [PersonEntity findAll];
}];

希望这能帮助你理解正在发生的事情,即使我不知道神奇的记录到底在做什么。

编辑

针对此评论...

我想我不明白这些块是如何工作的。如果一个块有这个 方法签名 "+ (void)saveDataInContext:(void(^)(NSManagedObjectContext *context))saveBlock"为什么块不使用"context"或"saveBlock"?哪个是块的返回值,哪个是 传递价值?- 迈克·

首先,没有此签名...

+ (void)saveDataInContext:(void(^)(NSManagedObjectContext *context))saveBlock;

这是一个类方法。 让我们一点一点地分解它。 但是,首先,让我们忘记块参数,并使用一些简单的东西进行比较。

+ (void)foo:(NSInteger)someInteger;

这是一个类方法,foo: ,它返回 void 并接受一个参数。 该单个参数的类型为 NSInteger 。 如果我想称呼它,我会这样做:

[SomeClass foo:42];

同样。。。

+ (void)saveDataInContext:(void(^)(NSManagedObjectContext *context))saveBlock;

是一个类方法,saveDataInContext: ,它返回 void 并接受一个参数。 该单个参数的类型为 void(^)(NSManagedObjectContext *context)

现在,不要让那个狼吞虎咽的家伙欺骗你。 它只是一个类型(尽管如果你不太了解 C,解析起来有点混乱)那么,什么是void(^)(NSManagedObjectContext *context)

首先,这是一个block。 如果该(^)void之后(*)它将是一个函数指针。

基本上,这意味着该参数的类型是一个返回void并具有一个参数的block,即指向NSManagedObjectContext的指针(名称为 context )。

所以,如果我们大声朗读...

+ (void)saveDataInContext:(void(^)(NSManagedObjectContext *context))saveBlock;

是一个类方法,带有选择器 saveDataInContext: 返回 void 并有一个参数,该参数的名称为 saveBlock,类型为"返回 void 的块,并且有一个类型为 NSManagedObjectContext * 的参数"。

就像我们这样称呼第一个例子一样...

[SomeClass foo:42];

我们这样称呼后一个例子...

[SomeClass saveDataInContext:^(NSManagedObjectContext *context){
    // We are creating a bock of code, so stuff some code in here.
}];

现在,就像您将整数42传递给foo:一样,您将{}之间的块作为参数传递给saveDataInContext:

现在,请注意,saveDataInContext: 方法的签名需要一个本身具有参数的块。 所以,当你提供你的块时,你基本上是在说,"嘿,这里有一个代码块供你调用,当你这样做时,确保你给我一个指向我可以使用的NSManagedObjectContext对象的指针。

这意味着当调用您的块

时,调用代码将调用您的块并为您提供变量名为 contextNSManagedObjectContext *

可以这样想,作为saveDataInContext:的微不足道的例子。

+ (void)saveDataInContext:(void(^)(NSManagedObjectContext *context))saveBlock {
    // Create a context to give the block we are going to call..
    NSManagedObjectContext *moc = //       
    saveBlock(moc);
}

现在,当调用代码时,您将获得moc对象作为参数。 基本上,该方法创建一个托管对象上下文,执行所有线程安全操作,然后调用代码块,并为您提供指向它已安全创建的托管对象上下文的指针。 您的代码在该安全环境的范围内执行,使用作为函数(块)参数传递给它的 MOC。

我希望这不会使情况变得更糟...

最新更新