Цель C: замена синхронного запроса - PullRequest
0 голосов
/ 19 сентября 2018

У меня проблема с заменой этого кода.

-(NSMutableArray *) GetPrices: {

    NSError *error;
    NSURLResponse *response;
    NSData *tw_result = [NSURLConnection       
       sendSynchronousRequest:urlRequest 
       returningResponse:&response error:&error];

У меня проблема в том, что функция, которая вызывает этот код, обрабатывает URL-адрес, а затем возвращает данные методу, который его вызывает.

Ранее я использовал это следующим образом.

ViewController вызывает функцию для сбора данных путем создания очереди операций (чтобы был доступен пользовательский интерфейс и основной поток)

     NSOperationQueue *myQueue = [NSOperationQueue new];
      NSInvocationOperation *operation = [[NSInvocationOperation alloc]
 initWithTarget:self selector:@selector(loadDataWithOperation) object:nil];
                [myQueue addOperation:operation];
                [operation release];
                [myQueue release];

Функция в очереди операций вызывает метод для получения данных об объекте, а затем этот метод выполняет синхронный URL-запрос.

-(void)loadDataWithOperation {

    self.sectionPriceArray = [self.myObject GetPrices];

Таким образом, myObject возвращает массив цен.

У меня естьпопытался использовать NSSession, но я не могу понять, как передать результат обратно, поскольку метод завершается до получения tw_result из обработчика завершения.

Любые мысли, которые я должен сделать в Objective C, поскольку я неу меня нет разрешения от клиента на преобразование в swift.

РЕДАКТИРОВАНИЕ вопроса с более подробной информацией:

Внутри моего метода GetPrices я попытался

NSURLSession *session = [NSURLSession sharedSession];
        [[session dataTaskWithRequest:urlRequest
                    completionHandler:^(NSData *tw_result,
                                        NSURLResponse *response,
                                        NSError *error) {
                        result = [[NSString alloc] initWithData:tw_result encoding:NSUTF8StringEncoding];

        NSArray *resultArray = [result componentsSeparatedByString:@"\n"];
        }) resume];

Но я не могу понятькак заставить это работать на один уровень выше на уровне вызова.

1 Ответ

0 голосов
/ 19 сентября 2018

Как отметил @maddy, вы захотите использовать блок завершения для метода getPrices вместо return - return + async не смешивается.

Это будет общая форма для преобразованияваш метод getPrices для:

- (void)_getPricesWithCompletion:(void(^)(NSMutableArray *sectionPriceArray))priceCompletion;

Этот сайт: http://goshdarnblocksyntax.com имеет некоторые из общих применений объявления синтаксиса блоков.

Обычно вы вызываете этот асинхронный метод и затем устанавливаетеваш iVar в блоке завершения и перезагрузите связанные элементы интерфейса после получения новых данных.Что-то вроде этого:

[self _getPricesWithCompletion:^(NSMutableArray *sectionPriceArray) {
    self.sectionPriceArray = sectionPriceArray;
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        // reload any UI elements dependent on your sectionPriceArray here
    }];
}];

Теперь в примере кода, который вы показываете, кажется, что вы используете NSOperationQueue для постановки в очередь различных операций.Здесь все может быть немного сложнее.Последующие операции очереди не будут ожидать завершения ваших асинхронных операций перед выполнением.Так, например, если у вас есть операция после операции getPrices, которая использует результат извлечения цен, iVar почти наверняка не будет содержать правильных данных на данный момент.В этом случае вам нужно будет использовать какой-то семафор для обработки ожидания завершения асинхронной операции, прежде чем продолжить работу, которая зависит от нее.

Вот пример того, что я имею в виду:

NotProperlyWaiting.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface NotProperlyWaiting : NSObject
@property (strong, nullable) NSMutableArray *sectionPriceArray;
- (void)callOperations;
- (void)fakeServerCallWithCompletion:(void(^)(NSData *tw_result, NSURLResponse *response, NSError *error))completion;
@end

NotProperlyWaiting.m

#import "NotProperlyWaiting.h"

@interface NotProperlyWaiting()
- (void)_getPricesWithCompletion:(void(^)(NSMutableArray *sectionPriceArray))priceCompletion;
- (void)_printPricesArray;
@end

@implementation NotProperlyWaiting

- (instancetype)init {
    self = [super init];
    if (self) {
        _sectionPriceArray = [NSMutableArray array];
    }
    return self;
}

- (void)callOperations {
    // setup our completion block to be passed in (this is what will eventually set the self.sectionPricesArray
    void (^pricesCompletion)(NSMutableArray *) = ^ void (NSMutableArray *sectionPricesArrayFromCompletion){
        self.sectionPriceArray = sectionPricesArrayFromCompletion;
    };
    NSOperationQueue *myQueue = [NSOperationQueue new];
    NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(_getPricesWithCompletion:) object:pricesCompletion];
    NSInvocationOperation *printOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(_printPricesArray) object:nil];
    [myQueue addOperation:operation];
    [myQueue addOperation:printOperation];
}

- (void)_getPricesWithCompletion:(void(^)(NSMutableArray *sectionPricesArray))priceCompletion {
    [self fakeServerCallWithCompletion:^(NSData *tw_result, NSURLResponse *response, NSError *error) {
        // check the error or response or whatever else to verify that the data is legit from your server endpoint here
        // then convert the data to your mutable array and pass it through to our completion block
        NSString *stringData = [[NSString alloc] initWithData:tw_result encoding:NSUTF8StringEncoding];
        NSMutableArray *tempPricesArray = [NSMutableArray arrayWithArray:[stringData componentsSeparatedByString:@"\n"]];
        // now our completion block passing in the result prices array
        priceCompletion(tempPricesArray);
    }];
}

- (void)_printPricesArray {
    NSLog(@"NotWaiting -- Prices array : %@", self.sectionPriceArray);
}

// this is a fake version of NSURLSession
- (void)fakeServerCallWithCompletion:(void(^)(NSData *tw_result, NSURLResponse *response, NSError *error))completion {
    NSString *fakeServerResponse = @"FirstThing\nSecondThing\nThirdThing";
    NSData *fakeData = [fakeServerResponse dataUsingEncoding:NSUTF8StringEncoding];
    NSURLResponse *fakeResponse = [[NSURLResponse alloc] init];
    NSError *fakeError = [NSError errorWithDomain:@"FakeErrorDomain" code:33 userInfo:nil];
    // never call sleep in your own code, this is just to simulate the wait time for the server to return data
    sleep(3);
    completion(fakeData,fakeResponse,fakeError);
}

    NS_ASSUME_NONNULL_END

ProperlyWaiting.h (Подкласс NotProperlyWaiting.h для повторного использования callOperation и fakeServerCallWithCompletion:)

#import "NotProperlyWaiting.h"

NS_ASSUME_NONNULL_BEGIN

@interface ProperlyWaiting : NotProperlyWaiting

@end

NS_ASSUME_NONNULL_END

ProperlyWaiting.m

#import "ProperlyWaiting.h"

@interface ProperlyWaiting()
- (void)_getPricesWithCompletion:(void(^)(NSMutableArray *sectionPricesArray))priceCompletion;
- (void)_printPricesArray;
@property dispatch_semaphore_t semaphore;
@end

@implementation ProperlyWaiting

- (void)callOperations {
    self.semaphore = dispatch_semaphore_create(0);
    [super callOperations];
}

// identical implementations to NotProperlyWaiting, but this time we'll use a semaphore to ensure the _printPricesArray waits for the async operation to complete before continuing
- (void)_getPricesWithCompletion:(void(^)(NSMutableArray *sectionPricesArray))priceCompletion {
    [self fakeServerCallWithCompletion:^(NSData *tw_result, NSURLResponse *response, NSError *error) {
        // check the error or response or whatever else to verify that the data is legit from your server endpoint here
        // then convert the data to your mutable array and pass it through to our completion block
        NSString *stringData = [[NSString alloc] initWithData:tw_result encoding:NSUTF8StringEncoding];
        NSMutableArray *tempPricesArray = [NSMutableArray arrayWithArray:[stringData componentsSeparatedByString:@"\n"]];
        // now our completion block passing in the result prices array
        priceCompletion(tempPricesArray);
        // signal our semaphore to let it know we're done
        dispatch_semaphore_signal(self.semaphore);
    }];
}

- (void)_printPricesArray {
    // wait for the semaphore signal before continuing (so we know the async operation we're waiting on has completed)
    dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"Waiting -- Prices array : %@", self.sectionPriceArray);
}

@end

С примерами вызовов такого класса:

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    NotProperlyWaiting *notWaiting = [[NotProperlyWaiting alloc] init];
    [notWaiting callOperations];

    ProperlyWaiting *waiting = [[ProperlyWaiting alloc] init];
    [waiting callOperations];
}

Вывод в журнал будет:

NotWaiting -- Prices array : (
) 

А затем через 3 секунды:

Waiting -- Prices array : (
    FirstThing,
    SecondThing,
    ThirdThing
)

Некоторые дополнительные ссылки на полезную документацию по этой теме:

https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithBlocks/WorkingwithBlocks.html

https://developer.apple.com/documentation/dispatch/1452955-dispatch_semaphore_create

...