Понимание ошибки RACSignal

В настоящее время я делаю свои первые шаги в ReactiveCocoa, и у меня есть крутая кривая обучения, чтобы понять принципы.

Во всяком случае, вот что я уже придумал.

Я привязываю свойство NSArray к RACSignal, чтобы иметь возможность реагировать на входящие данные JSON по сети.

- (void)updateRandomUserData 
{
    @weakify(self);
    RAC(self, users) = [[self fetchRandomUserData] doNext:^(NSDictionary *json){
    @strongify(self);
    NSMutableArray *randomUsers = [NSMutableArray array];
        for (NSDictionary *dict in json[@"data"]) {
           BKRandomUser *randomUser = [MTLJSONAdapter modelOfClass:[BKRandomUser class] fromJSONDictionary:dict error:nil];
           [randomUsers addObject:randomUser];
        }

        self.users = randomUsers;
    }];
}

Создание сигнала выглядит так:

- (RACSignal *)fetchRandomUserData
{

    return [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {

      NSURLRequest *request = [NSURLRequest requestWithURL:url];

      AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
      [subscriber sendNext:JSON];
      } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON, NSError *error) {
        [subscriber sendError:error];
      }];

      [operation start];

      return [RACDisposable disposableWithBlock:^{
          [operation cancel];
      }];

      }] doError:^(NSError *error) {
        NSLog(@"error: %@", [error description]);
    }];

}

Теперь, когда веб-служба не предоставляет никаких данных, я хочу отреагировать на это. Прямо сейчас приложение вылетает со следующим утверждением, которое я, честно говоря, не понимаю:

* Завершение работы приложения из-за необработанного исключения "NSInternalInconsistencyException", причина: "Получена ошибка от имени: [[+createSignal:] -doError:] -doNext: в привязке для ключевого пути "users" на : ( нулевой)'

Что мне здесь не хватает?

Спасибо!


person MrBr    schedule 04.03.2014    source источник


Ответы (2)


Принятый ответ решит проблему, но есть более идиоматический и элегантный способ справиться с этим:

Во-первых, вместо использования -doNext: используйте -map: для преобразования вашего JSON в массив пользователей:

RAC(self, users) = [[self fetchRandomUserData] map:^(NSDictionary *json){
    NSMutableArray *randomUsers = [NSMutableArray array];
    for (NSDictionary *dict in json[@"data"]) {
       BKRandomUser *randomUser = [MTLJSONAdapter modelOfClass:[BKRandomUser class] fromJSONDictionary:dict error:nil];
       [randomUsers addObject:randomUser];
    }

    return randomUsers;
}];

Затем для обработки ошибки вы можете использовать -catch::

RAC(self, users) = [[[self fetchRandomUserData] map:^(NSDictionary *json){
    NSMutableArray *randomUsers = [NSMutableArray array];
    for (NSDictionary *dict in json[@"data"]) {
       BKRandomUser *randomUser = [MTLJSONAdapter modelOfClass:[BKRandomUser class] fromJSONDictionary:dict error:nil];
       [randomUsers addObject:randomUser];
    }

    return randomUsers;
}] catch:^(NSError *error) {
    return [RACSignal return:@[]];
}];

В этом примере, если происходит ошибка, мы ее перехватываем и заменяем пустым массивом. Ты мог делать там все, что хотел. Замените его на nil или +[RACSignal empty], если вы просто хотите игнорировать все это. Или вызовите другой метод, который возвращает RACSignal *.

person joshaber    schedule 04.03.2014
comment
Хорошо, спасибо за разъяснение и дополнительное объяснение. Это действительно очень помогает. Вопрос, который возник при чтении вашего ответа, заключался в том, как я могу узнать, что лучше всего использовать для возвращающегося сигнала RAC. Так что же в данном случае делает -map: более подходящим, чем -doNext:? - person MrBr; 05.03.2014
comment
-map: берет каждое значение из сигнала и преобразует его. Вот почему блок возвращает id. -doNext: ничего не возвращает. Это просто дает вам изменение пика сигнала. - person joshaber; 06.03.2014
comment
Что в этом примере может вызвать ошибку и запустить -catch? - person codeperson; 16.03.2014
comment
-fetchRandomUserData, как определено в исходном вопросе, отправляет ошибку при сбое запроса JSON. - person joshaber; 17.03.2014

  1. Написав RAC(self, users), вы хотите связать возврат сигнала со свойством users, но вы ничего не возвращаете в обработчике сигнала.
  2. Вы используете doNext, который является инъекцией для текущего сигнала. Вы можете подписаться на него вместо того, чтобы вводить побочный эффект.

Решить:

Вы можете удалить привязку RAC(self, users), если хотите назначить ее в побочном эффекте subscribeNext, поэтому просто подпишитесь на сигнал в вашем случае, например:

@weakify(self);
[[self fetchRandomUserData] subscribeNext:^(NSDictionary *json) {
    @strongify(self);
    NSMutableArray *randomUsers = [NSMutableArray array];
    for (NSDictionary *dict in json[@"data"]) {
        BKRandomUser *randomUser = [MTLJSONAdapter modelOfClass:[BKRandomUser class] fromJSONDictionary:dict error:nil];
        [randomUsers addObject:randomUser];
    }

    self.users = randomUsers;
}];
person onevcat    schedule 04.03.2014
comment
Большое спасибо! Действительно, это было решение поймать крушение. Я предположил, что обработчиком сигнала является блок doError:^(NSError *error). Кроме того, мне действительно трудно понять все различные варианты подписки, такие как doNext, doCompleted, subscribeNext... и т. д. ... - person MrBr; 04.03.2014
comment
doSomething — это вставка текущего сигнала, этот блок будет выполняться до отправки соответствующего сигнала, и сам сигнал будет передаваться вместе с ним (возвращая новый сигнал RACSignal). Хотя метод subscribeSomething предназначен для взаимодействия с сигналом и возвращает RACDisposable - person onevcat; 04.03.2014