我无法实现this tutorial中概述的线程安全核心数据概念。我的目标是拥有一个可重用的代码部分,可以接受参数,进行核心数据操作(添加,更新,删除),然后在完成后异步回调。
因此,“安全地”修改核心数据对象的块:
+ (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];
}];
这里传递的'localContext'是什么?我认为我的大部分问题都围绕着不理解障碍。
答案 0 :(得分:2)
简要介绍一下该教程,可以看出它正在谈论神奇的记录。我从未使用它,所以我不能说它。
// 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...
}];
现在,这段代码非常相似,但需要两个块。一个用于在上下文中执行一些代码,另一个是在异步保存完成后执行的块。
saveBlock应该很熟悉。它与上例中的概念相同。
完成是一个块,返回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];
}];
希望这有助于你了解正在发生的事情,即使我不知道幕后的真实记录是什么。
修改强>
回应此评论......
我认为我不理解这些块是如何工作的。如果一个块有这个 方法签名“+ (空隙)saveDataInContext:(无效(^)(的NSManagedObjectContext * context))saveBlock“为什么块不使用”context“或”saveBlock“?这是一个块的返回值,它是 传递价值? - Mike S
首先,块没有此签名......
+ (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
为(*)
,那么它将是一个函数指针。
基本上,这意味着该参数的类型是block
,它返回void
并且有一个参数,即指向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
对象的指针可以使用。
这意味着,在调用您的区块时,调用代码会调用您的区块并使用变量名NSManagedObjectContext *
向您提供context
。
将此视为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作为函数(块)参数。
我希望这不会让情况变得更糟......