1

この解析操作は現在正常に動作していますが、UI がわずかにフリーズしていることに気付き始めたので、リファクタリングしてこれを非同期で実行しようとしています。しかし、私はいくつかの問題を抱えており、誰かが私を正しい方向に向けることを望んでいました. これが私の現在の(同期)コードです:

- (NSArray *)eventsFromJSON:(NSString *)objectNotation
{
    NSParameterAssert(objectNotation != nil);
    NSData *unicodeNotation = [objectNotation dataUsingEncoding:NSUTF8StringEncoding];
    NSError *error = nil;
    NSDictionary *eventsData = [NSJSONSerialization JSONObjectWithData:unicodeNotation options:0 error:&error];

    if (eventsData == nil) {
            //invalid JSON
            return nil;
        }

        NSArray *events = [eventsData valueForKeyPath:@"resultsPage.results"];
        if (events == nil) {
            //parsing error
            return nil;
        }

        NSLog(@"events looks like %@", events);
        NSMutableArray *formattedEvents = [NSMutableArray arrayWithCapacity:events.count];
        for (id object in [events valueForKeyPath:@"event"]) {
            Event *event = [[Event alloc] init];
            event.latitude = [object valueForKeyPath:@"location.lat"];
            event.longitude = [object valueForKeyPath:@"location.lng"];
            event.title = [object valueForKeyPath:@"displayName"];
            event.venue = [object valueForKeyPath:@"venue.displayName"];
            event.ticketsLink = [NSURL URLWithString:[object valueForKeyPath:@"uri"]];
            event.artist = [object valueForKeyPath:@"performance.artist.displayName"];
            event.date = [object valueForKeyPath:@"start.datetime"];

            [formattedEvents addObject:event];
        }

    return [NSArray arrayWithArray:formattedEvents];

}

私は NSOperationQueue を調べてきましたが、このメソッドから配列を返したいのですが、操作キューは戻り値を持つことを意図していないため、解決策を見つけるのに苦労しています。私もGCDを見ていますが、次のようなものがあります:

- (NSArray *)eventsFromJSON:(NSString *)objectNotation
    {
dispatch_queue_t backgroundQueue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);


__block NSMutableArray *mutable = [NSMutableArray array];
dispatch_async(backgroundQueue, ^{
    NSParameterAssert(objectNotation != nil);
    NSData *unicodeNotation = [objectNotation dataUsingEncoding:NSUTF8StringEncoding];
    NSError *error = nil;
    NSDictionary *eventsData = [NSJSONSerialization JSONObjectWithData:unicodeNotation options:0 error:&error];

    if (eventsData == nil) {
        //invalid JSON
        mutable = nil;
    }

    NSArray *events = [eventsData valueForKeyPath:@"resultsPage.results"];
    if (events == nil) {
        //parsing error
        mutable = nil;
    }

    NSLog(@"events looks like %@", events);
    NSMutableArray *formattedEvents = [NSMutableArray arrayWithCapacity:events.count];
    for (id object in [events valueForKeyPath:@"event"]) {
        Event *event = [[Event alloc] init];
        event.latitude = [object valueForKeyPath:@"location.lat"];
        event.longitude = [object valueForKeyPath:@"location.lng"];
        event.title = [object valueForKeyPath:@"displayName"];
        event.venue = [object valueForKeyPath:@"venue.displayName"];
        event.ticketsLink = [NSURL URLWithString:[object valueForKeyPath:@"uri"]];
        event.artist = [object valueForKeyPath:@"performance.artist.displayName"];
        event.date = [object valueForKeyPath:@"start.datetime"];

        [formattedEvents addObject:event];
    }

    mutable = [NSMutableArray arrayWithArray:formattedEvents];

});

return [mutable copy];
}

何らかの理由で、これは解析が完了する前にオブジェクトを返しているようですが、その可変オブジェクトからデータを取得していないため、解析が実際に行われていることに気付きました (結果をログアウトしています) )。この非同期処理を実行する方法について誰か教えてもらえますか?

ありがとう!!

4

3 に答える 3

3

主な問題は、その性質上、非同期操作が同期的に結果を返すことができないことです。から配列を返す代わりに-eventsFromJSON:、呼び出し元が結果が終了したときにコールバックを受け取る方法を提供する必要があります。Cocoa では、これに対して 2 つの一般的なアプローチがあります。

のようなメソッドを含む関連するデリゲート プロトコルを使用してデリゲートを作成し、-parser:(Parser *)parser didFinishParsingEvents:(NSArray *)events解析が終了したときにパーサーにデリゲートでこのメソッドを呼び出させることができます。

もう 1 つの解決策は、解析が完了したときに実行される完了ブロックを呼び出し元が提供できるようにすることです。したがって、次のようなことができます。

- (void)eventsFromJSON:(NSString *)objectNotation completionHandler:(void (^)(NSArray *events))completionHandler)
{
    dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_async(backgroundQueue, ^{
        NSMutableArray *mutable = [NSMutableArray array];
        NSParameterAssert(objectNotation != nil);
        NSData *unicodeNotation = [objectNotation dataUsingEncoding:NSUTF8StringEncoding];
        NSError *error = nil;

        // Snip...

        mutable = [NSMutableArray arrayWithArray:formattedEvents];

        dispatch_async(dispatch_get_main_queue(), ^{
            completionHandler([mutable copy]);
        });
    });
}

次に、このコードを次のように呼び出すことができます。

 - (void)parseJSONAndUpdateUI // Or whatever you're doing
{
    NSString *jsonString = ...;
    Parser *parser = [[Parser alloc] init];
    [parser parseEventsFromJSON:jsonString completionHandler:^(NSArray *events){
        // Update UI with parsed events here
    }];
}

私は 2 番目のブロックベースのアプローチの方が好きです。ほとんどの場合、コードが少なくなります。コードは、結果の配列を使用するコードがメソッド呼び出しの後に単純に続くため (ただし、完了ブロックのスコープ内にあるためインデントされます)、メソッドが配列を返すだけの同期アプローチに近くなります。

于 2012-10-22T18:29:39.633 に答える
1

parse メソッドに渡す完了ブロックを使用することをお勧めします。この方法では、値を返す必要はありませんが、解析された情報に対して必要なことを行うことができます。GCD を再度使用して、完了ブロックをメイン スレッドに配置する必要があります。

操作が完了したら、userInfo に配列を含む通知をメイン スレッドにポストすることもできます。

ただし、非同期操作の場合、値を返すことはできません。

于 2012-10-22T18:36:02.953 に答える
0

[mutable copy]戻り値が dispatch_async ブロックの外側にあるため、解析が完了する前に返されたオブジェクトを取得しています。dispatch_async は非同期に機能するため、すぐに戻り、戻り値を呼び出します[mutable copy](解析が完了していないため、これは空です)。

于 2012-10-22T18:30:46.523 に答える