0

このチュートリアルで概説されているスレッドセーフなコア データの概念を実装するのに問題があります。私の目標は、引数を取り、コア データ操作 (追加、更新、削除) を実行し、完了時に非同期にコールバックできるコードの再利用可能な部分を用意することです。

したがって、コアデータオブジェクトを「安全に」変更するブロックは次のとおりです。

+ (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」は何ですか? ここでの問題のほとんどは、ブロックを理解していないことに関係していると思います。

4

1 に答える 1

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...
}];

さて、このコードは非常に似ていますが、2 つのブロックが必要です。1 つはコンテキストでコードを実行するために使用され、もう 1 つは非同期保存が完了して実行されるブロックです。

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();
        });
    });
}

前と同じように、そのメソッドを呼び出すと、渡された最初のブロックに精神的に置き換え、2 番目のブロックに置き換えることができます。

[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」を使用しないのはなぜですか? ブロックの戻り値と渡される値はどれですか? – マイク S

まず、ブロックにはこの署名がありません...

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

それがクラスメソッドです。少しずつ分解してみましょう。ただし、最初に、ブロックパラメーターを忘れて、比較のために簡単なものを使用しましょう。

+ (void)foo:(NSInteger)someInteger;

これは、1 つの引数foo:を返し、受け取るクラス メソッドです。voidその単一の引数の型はNSInteger. 呼びたい場合は、次のようにします。

[SomeClass foo:42];

同じく...

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

saveDataInContext:は、 void を返し、引数を 1 つ取るクラス メソッドです。その単一の引数の型はvoid(^)(NSManagedObjectContext *context).

さて、そのゴブリーディーグックにだまされてはいけません。それは単なる型です (ただし、C をあまり理解していない場合、解析するのはやや混乱します)。void(^)(NSManagedObjectContext *context)

まず、それはblockです。(^)それがその後だったvoid場合、(*)それは関数ポインターになります。

基本的には、その引数の型が ablockを返しvoid、1 つのパラメーター、つまり a へのポインターNSManagedObjectContext(名前はcontext) を持つことを意味します。

では、声に出して読むと…

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

クラスメソッドであり、名前を持ち、「void を返し、 type のパラメーターを 1 つ持つブロック」タイプのパラメーターを 1 つsaveDataInContext:返すセレクターを持ちます。voidsaveBlockNSManagedObjectContext *

このように最初の例を呼び出すのと同じように...

[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 *、変数 name を提供するということです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 を使用して、その安全な環境の範囲内で実行されます。

それが悪化していないことを願っています...

于 2012-08-17T02:27:42.530 に答える