У меня 2 класса: NSObject класс DataParser и ViewController. В классе DataParser у меня есть несколько методов, которые анализируют XML, и результаты сохраняются в массиве, который является свойством класса DataParser. Я хочу показать результаты пользователю в моем ViewController. У меня есть это свойство @property (strong, nonatomic) DataParser *parser;, а в методе viewWillAppear я создал экземпляр класса DataParser, и я пытаюсь получить массив с результатами таким образом

self.parser = [[DataParser alloc] init];
[self.parser downloadBooksXML];
NSLog(@"%@", self.parser.array);

Но я все еще получаю ноль. Кто-нибудь знает, в чем может быть проблема?

- (void)downloadBooksXML
{
    NSURL *url = [NSURL URLWithString:@"http://..."];

    [self downloadDataFromURL:url withCompletionHandler:^(NSData *data) {
        if (data) {
            self.xmlParser = [[NSXMLParser alloc] initWithData:data];
            self.xmlParser.delegate = self;
            self.foundValue = [[NSMutableString alloc] init];
            [self.xmlParser parse];
        } else {
            NSLog(@"Error");
        }
    }];
}

РЕДАКТИРОВАТЬ: Когда я использую массив nslog в классе DataParser в методе parserDidEndDocument, он содержит данные

3
j. Doe 8 Сен 2016 в 17:32

3 ответа

Лучший ответ

Когда [self.parser downloadBooksXML] возвращается, self.parser.array еще не установлен, потому что данные не были загружены и проанализированы.

Данные будут доступны после завершения синтаксического анализа документа, который вы можете проверить с помощью метода делегата:

- (void)parserDidEndDocument:(NSXMLParser *)parser {
    NSLog(@"%@", self.parser.array);
}

Поэтому вы можете использовать такой шаблон:

- (void) viewDidLoad {
    [super viewDidLoad];
    self.parser = [[DataParser alloc] init];
    [self.parser downloadBooksXML];
}

- (void) dataFinished {
    NSLog(@"%@", self.parser.array);
    // update the user interface
}

- (void)parserDidEndDocument:(NSXMLParser *)parser {
   [self dataFinished];
}

В зависимости от реализации вашего другого кода вам может потребоваться переключиться в основную очередь для обновления пользовательского интерфейса:

- (void) dataFinished {
    NSLog(@"%@", self.parser.array);

    dispatch_async(dispatch_get_main_queue(), ^(void){
        // update the user interface
    });
}
3
Aaron Brager 8 Сен 2016 в 14:49

NSXMLParser - это синтаксический анализатор XML (синтаксический анализатор типа SAX). Он начинает читать ваш XML с самого начала и каждый раз, когда находит новый элемент, закрывающий элемент или символьные данные, сообщает вам об этом. При синтаксическом анализе вашего примера XML он будет называть его делегатами:

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict {
  if (elementName == @"YourElement") {
    // save the elements of attributeDict in your array! This is the place where you add the object to your property self.parser.array!
  }
}

PS: Если голосующий (-1) может сказать мне, что было не так в моем ответе?

-1
Todor Brachkov 8 Сен 2016 в 15:00

Учитывая, что вы, вероятно, устанавливаете свойство массива DataParser где-нибудь в своем XML-анализаторе и что downloadBooksXML выполняет загрузку и синтаксический анализ асинхронно, вы обнаружите нулевое значение сразу после возврата вызова к downloadBooksXML.

Шаблон делегирования, предложенный @Aaron, работает. Вы также можете подумать о добавлении собственного обработчика завершения в свой метод downloadBooksXML, примерно так:

typedef void (^DownloadBooksCompletionHandler)();

- (void)downloadBooksXMLWithCompletionHandler:(DownloadBooksCompletionHandler)completionHandler
{
    NSURL *url = [NSURL URLWithString:@"http://..."];

    [self downloadDataFromURL:url withCompletionHandler:^(NSData *data) {
        if (data) {
            self.xmlParser = [[NSXMLParser alloc] initWithData:data];
            self.xmlParser.delegate = self;
            self.foundValue = [[NSMutableString alloc] init];
            [self.xmlParser parse];
        } else {
            NSLog(@"Error");
        }

        completionHandler();         // <-- call the completionHandler!
    }];
}

Тогда вы можете «уведомить» вызывающего абонента о завершении загрузки, вызвав completionHandler(), получить доступ к массиву и обновить свой пользовательский интерфейс.

[self.parser downloadBooksXMLWithCompletionHandler:^()
{
  // now OK to access array property and update your UI
}];

Вы также можете рассмотреть возможность передачи NSError * обработчику завершения в качестве средства передачи информации об ошибке.

Наконец, остерегайтесь проблем с потоками ... completionHandler() будет выполняться в потоке, вызываемом вашим обработчиком завершения downloadDataFromURL. В зависимости от вашей реализации это может быть или не быть потоком пользовательского интерфейса.

1
CSmith 8 Сен 2016 в 15:07